|
21 | 21 |
|
22 | 22 | #include "arrow/util/secure_string.h" |
23 | 23 |
|
| 24 | +#include <src/gtest-internal-inl.h> |
| 25 | + |
24 | 26 | namespace arrow::util::test { |
25 | 27 |
|
26 | 28 | std::string_view StringArea(const std::string& string) { |
@@ -73,6 +75,84 @@ void AssertSecurelyCleared(const std::string_view area, const std::string& secre |
73 | 75 | } |
74 | 76 | } |
75 | 77 |
|
| 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 | + |
76 | 156 | TEST(TestSecureString, SecureClearString) { |
77 | 157 | // short string |
78 | 158 | { |
|
0 commit comments