15#include <gtest/gtest.h>
36 std::unique_ptr<sqlite_backend>
db_;
39 db_ = std::make_unique<sqlite_backend>();
47 "CREATE TABLE sensitive_data ("
48 " id INTEGER PRIMARY KEY,"
57 auto insert_result =
db_->execute_query(
58 "INSERT INTO sensitive_data "
59 "(id, ssn, credit_card, bank_account, password_hash) VALUES "
60 "(1, '123-45-6789', '4111111111111111', 'ACC123456789', 'hash_secret_123')"
64 GTEST_SKIP() <<
"SQLite not available";
69 if (
db_ &&
db_->is_initialized()) {
78 std::vector<std::string> sensitive_patterns = {
85 for (
const auto& pattern : sensitive_patterns) {
86 if (str.find(pattern) != std::string::npos) {
108 auto query_result = db_->select_query(
"SELECT * FROM sensitive_data");
114 db_->execute_query(
"INVALID SQL SYNTAX ERROR");
116 }
catch (
const std::exception& e) {
117 std::string error_msg = e.what();
120 EXPECT_FALSE(containsSensitiveData(error_msg))
121 <<
"Sensitive data leaked in exception: " << error_msg;
124 GTEST_SKIP() <<
"SQLite not available";
135 std::string bad_query =
"SELECT * FROM sensitive_data WHERE invalid_column = 1";
138 db_->select_query(bad_query);
139 }
catch (
const std::exception& e) {
140 std::string error_msg = e.what();
141 EXPECT_FALSE(containsSensitiveData(error_msg))
142 <<
"Sensitive data in database error: " << error_msg;
148 GTEST_SKIP() <<
"SQLite not available";
162 auto query_result = db_->select_query(
"SELECT * FROM sensitive_data");
164 auto result = query_result.value();
172 std::ostringstream debug_output;
173 for (
const auto& row : result) {
174 for (
const auto& [key, value] : row) {
175 debug_output << key <<
"=";
176 std::visit([&debug_output](
const auto& v) {
177 using T = std::decay_t<
decltype(v)>;
178 if constexpr (std::is_same_v<T, std::string>) {
180 }
else if constexpr (std::is_same_v<T, int64_t>) {
182 }
else if constexpr (std::is_same_v<T, double>) {
184 }
else if constexpr (std::is_same_v<T, bool>) {
185 debug_output << (v ?
"true" :
"false");
187 debug_output <<
"null";
196 std::string output = debug_output.str();
199 if (containsSensitiveData(output)) {
201 SUCCEED() <<
"SECURITY NOTE: Raw database output contains sensitive data. "
202 <<
"Production systems should mask fields matching patterns like: "
203 <<
"ssn, credit_card, password, secret, etc.";
206 GTEST_SKIP() <<
"SQLite not available";
215 std::stringstream captured;
216 std::streambuf* original = std::clog.rdbuf(captured.rdbuf());
223 .
where(
"ssn",
"=", std::string(
"123-45-6789"))
224 .
where(
"credit_card",
"=", std::string(
"4111111111111111"))
228 std::clog.rdbuf(original);
229 std::string log_output = captured.str();
232 EXPECT_FALSE(containsSensitiveData(log_output))
233 <<
"Sensitive data appeared in query builder logs: " << log_output;
247 std::vector<std::string> sensitive_patterns = {
273 SUCCEED() <<
"Documented " << sensitive_patterns.size()
274 <<
" sensitive column name patterns for masking";
288 auto query_result = db_->select_query(
"SELECT ssn FROM sensitive_data");
294 throw std::runtime_error(
"Test error after PII access");
295 }
catch (
const std::exception& e) {
296 std::string what_msg = e.what();
297 EXPECT_FALSE(containsSensitiveData(what_msg));
300 GTEST_SKIP() <<
"SQLite not available";
311 for (
int i = 2; i <= 100; ++i) {
313 "INSERT INTO sensitive_data (id, ssn, credit_card) VALUES (" +
314 std::to_string(i) +
", '" +
315 std::to_string(100 + i) +
"-45-6789', '4" +
316 std::string(15,
'1' + (i % 9)) +
"')";
317 db_->execute_query(query);
321 auto query_result = db_->select_query(
"SELECT * FROM sensitive_data");
323 ASSERT_GE(query_result.value().size(), 100u);
326 SUCCEED() <<
"Large datasets require careful error message construction";
328 GTEST_SKIP() <<
"SQLite not available";
343 auto query_result = db_->select_query(
"SELECT * FROM sensitive_data");
345 auto result = query_result.value();
352 EXPECT_TRUE(result.empty());
357 SUCCEED() <<
"Result objects support clearing sensitive data";
359 GTEST_SKIP() <<
"SQLite not available";
371 std::string sensitive =
"my_secret_password";
378 SUCCEED() <<
"SECURITY NOTE: Use secure memory clearing for sensitive strings. "
379 <<
"std::string::clear() does not securely erase memory.";
394 SUCCEED() <<
"All log levels should mask sensitive data, including DEBUG";
403 std::stringstream captured;
404 std::streambuf* original = std::cerr.rdbuf(captured.rdbuf());
407 db_->select_query(
"SELECT * FROM sensitive_data");
408 db_->execute_query(
"INVALID SYNTAX");
410 std::cerr.rdbuf(original);
411 std::string error_output = captured.str();
414 EXPECT_FALSE(containsSensitiveData(error_output))
415 <<
"Sensitive data in error log: " << error_output;
417 GTEST_SKIP() <<
"SQLite not available";
431 std::string full_cc =
"4111111111111111";
432 std::string masked_cc =
"****-****-****-1111";
435 EXPECT_EQ(full_cc.substr(full_cc.length() - 4),
"1111");
437 SUCCEED() <<
"Credit cards should be masked to show only last 4 digits";
446 std::string full_ssn =
"123-45-6789";
447 std::string masked_ssn =
"***-**-6789";
449 SUCCEED() <<
"SSN should be masked to show only last 4 digits";
Test fixture for data masking security tests.
std::unique_ptr< sqlite_backend > db_
bool containsSensitiveData(const std::string &str)
Helper to check if string contains any sensitive patterns.
Universal query builder that adapts to different database types.
query_builder & select(const std::vector< std::string > &columns)
std::string build() const
query_builder & where(const std::string &field, const std::string &op, const core::database_value &value)
TEST_F(DataMaskingTest, QueryResultsNotLeakedInExceptions)
Tests that query results don't appear in exception messages.
Abstract interface for database backends.
SQLite database backend plugin implementation.
Configuration for database connection.
#define ASSERT_TRUE(condition, message)
#define ASSERT_FALSE(condition, message)