#include #include #include #include #include #include "fold/detect/cross_tu_cache.hpp" #include "fold/detect/sql_injection.hpp" #include "fold/report/finding.hpp" #include "fold/symbolic/budget.hpp" namespace { struct TestResult { std::vector findings; fold::SolverStats stats; }; std::unique_ptr parse(const std::string& code) { return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp"); } TestResult run(const std::string& code) { fold::detect::CrossTUCache::instance().clear(); auto unit = parse(code); REQUIRE(unit); fold::SolverBudget b; fold::SolverStats s; auto findings = fold::detectSqlInjectionInContext(unit->getASTContext(), b, s); return {std::move(findings), s}; } bool hasSqlInjection(const std::vector& findings) { for (const auto& f : findings) { if (f.kind == fold::FindingKind::SqlInjection) return true; } return false; } } // namespace TEST_CASE("sql-injection: getenv return assigned to query reaches DB sink", "[sql][getenv]") { auto [findings, stats] = run("extern \"C\" char* getenv(const char*);\n" "struct DB { void execute(const char*); };\n" "extern DB db;\n" "void f() {\n" " const char* q = getenv(\"QUERY_STRING\");\n" " db.execute(q);\n" "}\n"); (void)stats; REQUIRE(hasSqlInjection(findings)); REQUIRE(findings.size() == 1); CHECK(findings.front().confidence == fold::Confidence::Heuristic); CHECK_FALSE(findings.front().evidence.latent); } TEST_CASE("sql-injection: direct tainted parameter still reaches DB sink", "[sql][param]") { auto [findings, stats] = run("struct DB { void execute(const char*); };\n" "extern DB db;\n" "void f(const char* user_query) {\n" " db.execute(user_query);\n" "}\n"); (void)stats; REQUIRE(hasSqlInjection(findings)); REQUIRE(findings.size() == 1); CHECK(findings.front().confidence == fold::Confidence::Latent); CHECK(findings.front().evidence.latent); CHECK(findings.front().evidence.precondition_required); } TEST_CASE("sql-injection: incompatible sink overload fails closed", "[sql][model]") { auto [findings, stats] = run("int PQexec();\n" "void f() { (void)PQexec(); }\n"); (void)stats; REQUIRE_FALSE(hasSqlInjection(findings)); } TEST_CASE("sql-injection: hardcoded query is safe", "[sql][safe]") { auto [findings, stats] = run("struct DB { void execute(const char*); };\n" "extern DB db;\n" "void f() {\n" " db.execute(\"select 1\");\n" "}\n"); (void)stats; REQUIRE_FALSE(hasSqlInjection(findings)); }