Skip to content

Commit 9c88744

Browse files
committed
Test correctness of AssertSecurelyCleared
1 parent 973b233 commit 9c88744

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

cpp/src/arrow/util/secure_string_test.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "arrow/util/secure_string.h"
2323

24+
#include <src/gtest-internal-inl.h>
25+
2426
namespace arrow::util::test {
2527

2628
std::string_view StringArea(const std::string& string) {
@@ -73,6 +75,84 @@ void AssertSecurelyCleared(const std::string_view area, const std::string& secre
7375
}
7476
}
7577

78+
// GTest test result reporter that captures the result but does not hand it to the unit
79+
// test instance. This effectively hides the result from the GTest test framework.
80+
class Reporter : public testing::TestPartResultReporterInterface {
81+
public:
82+
explicit Reporter(testing::TestInfo* test_info)
83+
: result_(testing::TestPartResult::kSuccess, test_info->file(), test_info->line(),
84+
"") {}
85+
void ReportTestPartResult(const testing::TestPartResult& result) override {
86+
result_ = result;
87+
}
88+
const testing::TestPartResult& result() const { return result_; }
89+
90+
private:
91+
testing::TestPartResult result_;
92+
};
93+
94+
#define GET_TEST_RESULT_REPORTER() \
95+
testing::internal::GetUnitTestImpl()->GetTestPartResultReporterForCurrentThread()
96+
97+
#define SET_TEST_RESULT_REPORTER(reporter) \
98+
testing::internal::GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( \
99+
reporter);
100+
101+
#define CAPTURE_TEST_RESULT(capture, body) \
102+
{ \
103+
auto report = GET_TEST_RESULT_REPORTER(); \
104+
SET_TEST_RESULT_REPORTER(&capture); \
105+
body; \
106+
SET_TEST_RESULT_REPORTER(report); \
107+
}
108+
109+
TEST(TestSecureString, AssertSecurelyCleared) {
110+
// This tests AssertSecurelyCleared helper methods is actually able to identify secret
111+
// leakage. It captures test results emitted by ASSERT_EQ and then asserts result type
112+
// and message.
113+
auto capture = Reporter(test_info_);
114+
115+
auto short_zeros = std::string(10, '\0');
116+
AssertSecurelyCleared(std::string_view(short_zeros));
117+
118+
auto large_zeros = std::string(1000, '\0');
119+
AssertSecurelyCleared(large_zeros);
120+
121+
auto no_zeros = std::string("abcdefghijklmnopqrstuvwxyz");
122+
CAPTURE_TEST_RESULT(capture, AssertSecurelyCleared(no_zeros));
123+
ASSERT_EQ(capture.result().type(), testing::TestPartResult::kFatalFailure);
124+
ASSERT_EQ(std::string(capture.result().message()),
125+
"Expected equality of these values:\n"
126+
" area\n"
127+
" Which is: \"abcdefghijklmnopqrstuvwxyz\"\n"
128+
" std::string_view(zeros)\n"
129+
" Which is: "
130+
"\"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\"
131+
"0\\0\"\n");
132+
133+
auto some_zeros = no_zeros;
134+
some_zeros = std::string(10, '\0');
135+
AssertSecurelyCleared(some_zeros, 10);
136+
CAPTURE_TEST_RESULT(capture, AssertSecurelyCleared(some_zeros));
137+
ASSERT_EQ(capture.result().type(), testing::TestPartResult::kFatalFailure);
138+
ASSERT_EQ(std::string(capture.result().message()),
139+
"Expected equality of these values:\n"
140+
" area\n"
141+
" Which is: \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0lmnopqrstuvwxyz\"\n"
142+
" std::string_view(zeros)\n"
143+
" Which is: "
144+
"\"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\"
145+
"0\\0\"\n");
146+
147+
AssertSecurelyCleared(some_zeros, "12345678901234567890123456");
148+
CAPTURE_TEST_RESULT(capture, AssertSecurelyCleared(StringArea(some_zeros), no_zeros));
149+
ASSERT_EQ(capture.result().type(), testing::TestPartResult::kFatalFailure);
150+
ASSERT_EQ(std::string(capture.result().message()),
151+
"Failed\n"
152+
"15 characters of secret leaked into "
153+
"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0lmnopqrstuvwxyz\n");
154+
}
155+
76156
TEST(TestSecureString, SecureClearString) {
77157
// short string
78158
{

0 commit comments

Comments
 (0)