fold/tests/integration/test_yams_integration.cpp
trvon a51cdf5748
M19–M30 + scout-rule encoder refactor
Multi-phase work merged into one commit:

* M19: flow-sensitive guard tracking (stmtAlwaysExits, walkIf early-exit)
* M20–M27: non-null marking across deref, addr-of, callsite preconds,
  if/while/switch guards, AddrOf-of-subexpr, CFG-block-scoped maps
* M30 Phase A: dialect-agnostic SourceLoc + LocationMapper + frontend/
  boundary marker; ClangLocationMapper wired through SafetyObserver
* M30 Phase B v2: QueryTracer + 13 SafetyObserver hooks migrated to
  ir::Expr / ir::Call / ir::MemberRef / ir::Symbol / ir::SourceLoc
* M30 Phase C/D/D2/D3: ir::Symbol, ir::Expr (with Kind), ir::Call,
  ir::MemberRef opaque types; ir_expr_clang.cpp houses the clang
  adapters; encodeExpr migrated to switch(kind) dispatch

Scout-rule extractions from encoder.cpp (2800 → 2371 lines):
* encoder_callee_predicates.{hpp,cpp} — isMallocLikeCall, isFreeCall,
  isAssertCall as pure std::string_view predicates
* encoder_type_queries.{hpp,cpp} — typeSizeBytes, sourceWidth,
  isUnsignedLike, byteWidthExpr
* encoder_callee_names.{hpp,cpp} — calleeName, qualifiedFunctionName,
  qualifiedCalleeName (with internal-linkage @-suffix)
* encoder_coerce.{hpp,cpp} — coerceToBv, coerceToExactBv, coerceToBool,
  coerceToType (BV-64 storage model documented at the boundary)
* encoder_reachability.{hpp,cpp} — ReachabilityCounter impl, markEncoded,
  recordEncoded, recordBailedNode, public profiling glue
* encoder_summary_instantiate.cpp — SMT-LIB summary substitution
  (instantiateSummaryExpr / Predicate / PredicateInContext +
  applySummaryParamPost; kills the redundant *Impl pass-through layer)
2026-05-21 11:17:50 -04:00

137 lines
4.4 KiB
C++

#include <clang/Tooling/CompilationDatabase.h>
#include <catch2/catch_test_macros.hpp>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include "fold/cli/scan_options.hpp"
#include "fold/cli/scan_runner.hpp"
#include "fold/detect/summary_store.hpp"
#include "fold/frontend/comp_db.hpp"
namespace {
struct TempDir {
std::filesystem::path path;
TempDir()
: path(std::filesystem::temp_directory_path() /
("fold-yams-integration-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()))) {
std::filesystem::create_directories(path);
}
~TempDir() { std::filesystem::remove_all(path); }
};
void writeFile(const std::filesystem::path& path, const std::string& content) {
std::ofstream(path) << content;
}
void writeCompileDb(const std::filesystem::path& dir, const std::filesystem::path& src) {
nlohmann::json db = nlohmann::json::array();
db.push_back({
{"directory", dir.string()},
{"command", std::string("clang++ -std=c++20 -c ") + src.string()},
{"file", src.string()},
});
writeFile(dir / "compile_commands.json", db.dump());
}
} // namespace
TEST_CASE("yams taint integration: readString to system", "[integration][yams][taint]") {
TempDir dir;
const auto src = dir.path / "main.cpp";
writeFile(src, "namespace std {\n"
" class string {\n"
" public:\n"
" const char* c_str() const;\n"
" };\n"
"}\n"
"namespace yams::daemon {\n"
" struct BinaryDeserializer {\n"
" std::string readString();\n"
" };\n"
"}\n"
"extern \"C\" int system(const char*);\n"
"int run(yams::daemon::BinaryDeserializer& bd) {\n"
" std::string cmd = bd.readString();\n"
" return system(cmd.c_str());\n"
"}\n");
writeCompileDb(dir.path, src);
std::string err;
auto db = fold::loadCompilationDatabase(dir.path.string(), err);
REQUIRE(db);
fold::cli::ScanOptions opts;
opts.compile_db = dir.path.string();
opts.detectors = {"taint-track"};
auto result = fold::cli::runDetectorsOnShard(*db, opts, {src.string()},
/*include_clone_clustering=*/false);
bool saw_taint = false;
for (const auto& finding : result.findings) {
if (finding.kind == fold::FindingKind::TaintTrack) {
saw_taint = true;
}
}
REQUIRE(saw_taint);
}
TEST_CASE("yams taint integration: Result::value() propagation", "[integration][yams][taint]") {
TempDir dir;
const auto src = dir.path / "main.cpp";
writeFile(src, "namespace std {\n"
" class string {\n"
" public:\n"
" const char* c_str() const;\n"
" };\n"
"}\n"
"template<typename T>\n"
"struct Result {\n"
" T val;\n"
" T& value() { return val; }\n"
"};\n"
"namespace yams::daemon {\n"
" struct BinaryDeserializer {\n"
" std::string readString();\n"
" };\n"
"}\n"
"extern \"C\" int system(const char*);\n"
"int run(yams::daemon::BinaryDeserializer& bd) {\n"
" Result<std::string> cmd;\n"
" cmd.val = bd.readString();\n"
" return system(cmd.value().c_str());\n"
"}\n");
writeCompileDb(dir.path, src);
std::string err;
auto db = fold::loadCompilationDatabase(dir.path.string(), err);
REQUIRE(db);
fold::cli::ScanOptions opts;
opts.compile_db = dir.path.string();
opts.detectors = {"taint-track"};
auto result = fold::cli::runDetectorsOnShard(*db, opts, {src.string()},
/*include_clone_clustering=*/false);
bool saw_taint = false;
for (const auto& finding : result.findings) {
if (finding.kind == fold::FindingKind::TaintTrack) {
saw_taint = true;
}
}
REQUIRE(saw_taint);
}