129 lines
5 KiB
C++
129 lines
5 KiB
C++
#include <clang/Frontend/ASTUnit.h>
|
|
#include <clang/Tooling/Tooling.h>
|
|
#include <spdlog/logger.h>
|
|
#include <spdlog/sinks/ostream_sink.h>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "fold/detect/cross_tu_cache.hpp"
|
|
#include "fold/detect/sql_injection.hpp"
|
|
#include "fold/detect/taint_track.hpp"
|
|
#include "fold/report/finding.hpp"
|
|
#include "fold/symbolic/budget.hpp"
|
|
|
|
namespace {
|
|
|
|
std::unique_ptr<clang::ASTUnit> parse(const std::string& code) {
|
|
return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp");
|
|
}
|
|
|
|
std::vector<fold::Finding> runTaint(const std::string& code) {
|
|
fold::detect::CrossTUCache::instance().clear();
|
|
auto unit = parse(code);
|
|
REQUIRE(unit);
|
|
fold::SolverBudget b;
|
|
fold::SolverStats s;
|
|
return fold::detectTaintTrackInContext(unit->getASTContext(), b, s);
|
|
}
|
|
|
|
std::pair<std::vector<fold::Finding>, std::string> runTaintWithLog(const std::string& code) {
|
|
std::ostringstream log;
|
|
auto previous_logger = spdlog::default_logger();
|
|
auto sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(log);
|
|
spdlog::set_default_logger(std::make_shared<spdlog::logger>("taint-track-test", sink));
|
|
auto findings = runTaint(code);
|
|
spdlog::set_default_logger(previous_logger);
|
|
return {findings, log.str()};
|
|
}
|
|
|
|
std::vector<fold::Finding> runSql(const std::string& code) {
|
|
fold::detect::CrossTUCache::instance().clear();
|
|
auto unit = parse(code);
|
|
REQUIRE(unit);
|
|
fold::SolverBudget b;
|
|
fold::SolverStats s;
|
|
return fold::detectSqlInjectionInContext(unit->getASTContext(), b, s);
|
|
}
|
|
|
|
bool hasKind(const std::vector<fold::Finding>& findings, fold::FindingKind kind) {
|
|
for (const auto& finding : findings) {
|
|
if (finding.kind == kind)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("taint-track: tainted path into open() fires", "[taint-track][path]") {
|
|
auto findings = runTaint("extern \"C\" char* getenv(const char*);\n"
|
|
"extern \"C\" int open(const char*, int, ...);\n"
|
|
"int f() {\n"
|
|
" char* path = getenv(\"X\");\n"
|
|
" return open(path, 0);\n"
|
|
"}\n");
|
|
|
|
REQUIRE(hasKind(findings, fold::FindingKind::TaintTrack));
|
|
}
|
|
|
|
TEST_CASE("sql-injection: sqlite3_bind_text marks a value sanitized", "[taint-track][sql-bind]") {
|
|
const std::string code =
|
|
"struct sqlite3; struct sqlite3_stmt;\n"
|
|
"extern \"C\" char* getenv(const char*);\n"
|
|
"extern \"C\" int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void*);\n"
|
|
"extern \"C\" int sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), "
|
|
"void*, char**);\n"
|
|
"void f(sqlite3* db, sqlite3_stmt* stmt) {\n"
|
|
" char* user = getenv(\"X\");\n"
|
|
" sqlite3_bind_text(stmt, 1, user, -1, nullptr);\n"
|
|
" sqlite3_exec(db, user, nullptr, nullptr, nullptr);\n"
|
|
"}\n";
|
|
|
|
REQUIRE_FALSE(hasKind(runSql(code), fold::FindingKind::SqlInjection));
|
|
REQUIRE_FALSE(hasKind(runTaint(code), fold::FindingKind::TaintTrack));
|
|
}
|
|
|
|
TEST_CASE("taint-track: incompatible sink overload fails closed", "[taint-track][model]") {
|
|
auto findings = runTaint("int mysql_query();\n"
|
|
"void f() { (void)mysql_query(); }\n");
|
|
REQUIRE_FALSE(hasKind(findings, fold::FindingKind::TaintTrack));
|
|
}
|
|
|
|
TEST_CASE("taint-track: executeAsync is not an exec sink", "[taint-track][model]") {
|
|
const auto [findings, log] =
|
|
runTaintWithLog("struct Command { void executeAsync(); };\n"
|
|
"void f(Command& command) { command.executeAsync(); }\n");
|
|
|
|
REQUIRE_FALSE(hasKind(findings, fold::FindingKind::TaintTrack));
|
|
REQUIRE(log.find("rejected sink model for executeAsync") == std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("taint-track: tainted argv into execve() fires", "[taint-track][command]") {
|
|
auto findings = runTaint("extern \"C\" char* getenv(const char*);\n"
|
|
"extern \"C\" int execve(const char*, char* const[], char* const[]);\n"
|
|
"int f(char* argv[]) {\n"
|
|
" char* path = getenv(\"SHELL\");\n"
|
|
" return execve(path, argv, nullptr);\n"
|
|
"}\n");
|
|
|
|
REQUIRE(hasKind(findings, fold::FindingKind::TaintTrack));
|
|
}
|
|
|
|
TEST_CASE("taint-track: tainted argv into posix_spawnp() fires", "[taint-track][command]") {
|
|
auto findings =
|
|
runTaint("typedef int pid_t;\n"
|
|
"extern \"C\" int posix_spawnp(pid_t*, const char*, const void*, const void*, "
|
|
"char* const[], char* const[]);\n"
|
|
"int f(char* argv[]) {\n"
|
|
" pid_t pid = 0;\n"
|
|
" return posix_spawnp(&pid, \"/bin/sh\", nullptr, nullptr, argv, nullptr);\n"
|
|
"}\n");
|
|
|
|
REQUIRE(hasKind(findings, fold::FindingKind::TaintTrack));
|
|
}
|