- persist TU-owned graphs and guarded configuration evidence - add closed-world production reachability classification - calibrate detector precision and reviewed self-scan policy - terminate poisoned workers instead of longjmp recovery
108 lines
4 KiB
C++
108 lines
4 KiB
C++
#include <clang/Tooling/Tooling.h>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "fold/detect/hardcoded_credential.hpp"
|
|
#include "fold/report/finding.hpp"
|
|
|
|
namespace {
|
|
|
|
std::unique_ptr<clang::ASTUnit> parse(const std::string& code) {
|
|
return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("hardcoded-credential: JWT literal is flagged", "[hardcoded-credential][jwt]") {
|
|
auto unit = parse(R"(
|
|
const char *token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
REQUIRE(findings[0].kind == fold::FindingKind::HardcodedCredential);
|
|
REQUIRE(findings[0].message.find("JWT") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: AWS access key id is flagged", "[hardcoded-credential][aws]") {
|
|
auto unit = parse(R"(
|
|
const char *access_key = "AKIA1234567890ABCDEF";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
REQUIRE(findings[0].message.find("AWS") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: generic private key armour is flagged",
|
|
"[hardcoded-credential][pem]") {
|
|
auto unit = parse(R"(
|
|
const char *key = "-----BEGIN OPENSSH PRIVATE KEY-----\nabc\n-----END OPENSSH PRIVATE KEY-----";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
REQUIRE(findings[0].message.find("private key") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: secret-looking literal assigned to api_key is flagged",
|
|
"[hardcoded-credential][heuristic]") {
|
|
auto unit = parse(R"(
|
|
const char *api_key = "AbCdEfGhIjKlMnOpQr9_123";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
REQUIRE(findings[0].message.find("secret-looking") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: placeholder api key literal is not flagged",
|
|
"[hardcoded-credential][placeholder][negative]") {
|
|
auto unit = parse(R"(
|
|
const char *api_key = "your_api_key_here";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.empty());
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: matcher and regex vocabulary are not credentials",
|
|
"[hardcoded-credential][vocabulary]") {
|
|
auto unit = parse(R"(
|
|
struct Text { unsigned long rfind(const char*, unsigned long) const; };
|
|
bool recognizes(Text value) {
|
|
return value.rfind("sk_live_1234567890abcdef", 0) == 0;
|
|
}
|
|
namespace std { struct regex { explicit regex(const char*); }; }
|
|
std::regex private_key_pattern("-----BEGIN [A-Z ]*PRIVATE KEY-----");
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.empty());
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: direct secret comparison remains reportable",
|
|
"[hardcoded-credential][comparison]") {
|
|
auto unit = parse(R"(
|
|
extern "C" int strcmp(const char*, const char*);
|
|
bool accepts(const char* provided) {
|
|
return strcmp(provided, "sk_live_1234567890abcdef") == 0;
|
|
}
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
}
|
|
|
|
TEST_CASE("hardcoded-credential: Stripe test key is flagged",
|
|
"[hardcoded-credential][stripe-test]") {
|
|
auto unit = parse(R"(
|
|
const char *secret = "sk_test_51NvTExampleSecretValue";
|
|
)");
|
|
REQUIRE(unit);
|
|
auto findings = fold::detectHardcodedCredentialInContext(unit->getASTContext());
|
|
REQUIRE(findings.size() == 1);
|
|
REQUIRE(findings[0].message.find("Stripe test key") != std::string::npos);
|
|
}
|