From 8d33abb79da07622d35dbea5d465f8a04654e6eb Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 21:55:21 -0700 Subject: [PATCH] Complete Step 491: security test skeleton generation --- editor/CMakeLists.txt | 9 ++ editor/src/SecurityTestSkeletonGenerator.h | 140 ++++++++++++++++++ editor/tests/step491_test.cpp | 164 +++++++++++++++++++++ progress.md | 40 +++++ 4 files changed, 353 insertions(+) create mode 100644 editor/src/SecurityTestSkeletonGenerator.h create mode 100644 editor/tests/step491_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 07b9bfe..67a0b76 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3298,4 +3298,13 @@ target_link_libraries(step490_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step491_test tests/step491_test.cpp) +target_include_directories(step491_test PRIVATE src) +target_link_libraries(step491_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/SecurityTestSkeletonGenerator.h b/editor/src/SecurityTestSkeletonGenerator.h new file mode 100644 index 0000000..d1c57fd --- /dev/null +++ b/editor/src/SecurityTestSkeletonGenerator.h @@ -0,0 +1,140 @@ +#pragma once + +// Step 491: Security Testing Skeleton Generation +// Generates security-focused test stubs and routing hints. + +#include +#include + +enum class SecurityTestKind { + InputValidationFuzz, + AuthBypass, + SqlInjectionProbe, + XssPayload, + AccessControlMatrix +}; + +struct SecurityTestCaseSpec { + SecurityTestKind kind = SecurityTestKind::InputValidationFuzz; + std::string testName; + std::string intentAnnotation; + std::string routeTo; // slm | human + std::string code; + bool humanReviewRequired = false; +}; + +struct SecuritySkeletonRequest { + std::string language; + std::string moduleName; + std::string entityName; + bool includeComplexPenTest = true; +}; + +struct SecuritySkeletonOutput { + std::vector tests; + std::vector notes; +}; + +class SecurityTestSkeletonGenerator { +public: + static SecuritySkeletonOutput generate(const SecuritySkeletonRequest& req) { + SecuritySkeletonOutput out; + const std::string base = req.moduleName.empty() ? "module" : req.moduleName; + const std::string entity = req.entityName.empty() ? "Resource" : req.entityName; + + out.tests.push_back(makeInputValidation(base, entity)); + out.tests.push_back(makeAuthBypass(base, entity)); + out.tests.push_back(makeSqlProbe(base, entity)); + out.tests.push_back(makeXssProbe(base, entity)); + out.tests.push_back(makeAccessMatrix(base, entity)); + + if (!req.includeComplexPenTest) { + for (auto& t : out.tests) { + if (t.kind == SecurityTestKind::SqlInjectionProbe || + t.kind == SecurityTestKind::XssPayload || + t.kind == SecurityTestKind::AuthBypass) { + t.humanReviewRequired = true; + t.routeTo = "human"; + } + } + out.notes.push_back("Complex penetration tests forced to human review"); + } + + out.notes.push_back("Security test skeletons generated with @Intent annotations"); + return out; + } + +private: + static SecurityTestCaseSpec makeInputValidation(const std::string& module, + const std::string& entity) { + SecurityTestCaseSpec t; + t.kind = SecurityTestKind::InputValidationFuzz; + t.testName = "test_" + module + "_input_validation_fuzz"; + t.intentAnnotation = "@Intent(fuzz boundary validation for " + entity + " inputs)"; + t.routeTo = "slm"; + t.code = + "void " + t.testName + "() {\n" + " // fuzz input boundaries and assert rejection of malformed payloads\n" + "}\n"; + return t; + } + + static SecurityTestCaseSpec makeAuthBypass(const std::string& module, + const std::string& entity) { + SecurityTestCaseSpec t; + t.kind = SecurityTestKind::AuthBypass; + t.testName = "test_" + module + "_auth_bypass"; + t.intentAnnotation = "@Intent(check auth bypass vectors for " + entity + ")"; + t.routeTo = "human"; + t.humanReviewRequired = true; + t.code = + "void " + t.testName + "() {\n" + " // attempt unauthorized access and assert denial\n" + "}\n"; + return t; + } + + static SecurityTestCaseSpec makeSqlProbe(const std::string& module, + const std::string& entity) { + SecurityTestCaseSpec t; + t.kind = SecurityTestKind::SqlInjectionProbe; + t.testName = "test_" + module + "_sql_injection_probe"; + t.intentAnnotation = "@Intent(probe SQL injection paths for " + entity + ")"; + t.routeTo = "human"; + t.humanReviewRequired = true; + t.code = + "void " + t.testName + "() {\n" + " // submit payload: \"' OR 1=1 --\" and assert query safety\n" + "}\n"; + return t; + } + + static SecurityTestCaseSpec makeXssProbe(const std::string& module, + const std::string& entity) { + SecurityTestCaseSpec t; + t.kind = SecurityTestKind::XssPayload; + t.testName = "test_" + module + "_xss_payload"; + t.intentAnnotation = "@Intent(probe XSS payload handling for " + entity + ")"; + t.routeTo = "human"; + t.humanReviewRequired = true; + t.code = + "void " + t.testName + "() {\n" + " // inject and assert escaping\n" + "}\n"; + return t; + } + + static SecurityTestCaseSpec makeAccessMatrix(const std::string& module, + const std::string& entity) { + SecurityTestCaseSpec t; + t.kind = SecurityTestKind::AccessControlMatrix; + t.testName = "test_" + module + "_access_control_matrix"; + t.intentAnnotation = "@Intent(validate role/resource access matrix for " + entity + ")"; + t.routeTo = "slm"; + t.code = + "void " + t.testName + "() {\n" + " // verify allowed and denied role combinations\n" + "}\n"; + return t; + } +}; diff --git a/editor/tests/step491_test.cpp b/editor/tests/step491_test.cpp new file mode 100644 index 0000000..0b49b6d --- /dev/null +++ b/editor/tests/step491_test.cpp @@ -0,0 +1,164 @@ +// Step 491: Security Testing Skeleton Generation Tests (12 tests) + +#include "SecurityTestSkeletonGenerator.h" + +#include +#include + +static int passed = 0, failed = 0; +#define TEST(name) { std::cout << " " << #name << "... "; } +#define PASS() { std::cout << "PASS\n"; ++passed; } +#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } +#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} + +static SecuritySkeletonOutput run(const std::string& module = "api", + const std::string& entity = "User", + bool complex = true) { + SecuritySkeletonRequest req; + req.language = "cpp"; + req.moduleName = module; + req.entityName = entity; + req.includeComplexPenTest = complex; + return SecurityTestSkeletonGenerator::generate(req); +} + +void test_generates_input_validation_fuzz_test() { + TEST(generates_input_validation_fuzz_test); + auto out = run(); + CHECK(out.tests[0].kind == SecurityTestKind::InputValidationFuzz, "kind mismatch"); + CHECK(out.tests[0].testName.find("input_validation_fuzz") != std::string::npos, "name mismatch"); + PASS(); +} + +void test_generates_auth_bypass_test() { + TEST(generates_auth_bypass_test); + auto out = run(); + bool found = false; + for (const auto& t : out.tests) if (t.kind == SecurityTestKind::AuthBypass) found = true; + CHECK(found, "missing auth bypass test"); + PASS(); +} + +void test_generates_sql_injection_probe_test() { + TEST(generates_sql_injection_probe_test); + auto out = run(); + bool found = false; + for (const auto& t : out.tests) if (t.kind == SecurityTestKind::SqlInjectionProbe) found = true; + CHECK(found, "missing sql injection test"); + PASS(); +} + +void test_generates_xss_payload_test() { + TEST(generates_xss_payload_test); + auto out = run(); + bool found = false; + for (const auto& t : out.tests) if (t.kind == SecurityTestKind::XssPayload) found = true; + CHECK(found, "missing xss payload test"); + PASS(); +} + +void test_generates_access_control_matrix_test() { + TEST(generates_access_control_matrix_test); + auto out = run(); + bool found = false; + for (const auto& t : out.tests) if (t.kind == SecurityTestKind::AccessControlMatrix) found = true; + CHECK(found, "missing access control matrix test"); + PASS(); +} + +void test_all_tests_include_intent_annotation() { + TEST(all_tests_include_intent_annotation); + auto out = run(); + for (const auto& t : out.tests) { + CHECK(t.intentAnnotation.find("@Intent(") == 0, "missing @Intent prefix"); + } + PASS(); +} + +void test_simple_validation_tests_route_to_slm() { + TEST(simple_validation_tests_route_to_slm); + auto out = run(); + bool ok = false; + for (const auto& t : out.tests) { + if (t.kind == SecurityTestKind::InputValidationFuzz && t.routeTo == "slm") ok = true; + } + CHECK(ok, "input validation fuzz should route to slm"); + PASS(); +} + +void test_complex_penetration_tests_route_to_human() { + TEST(complex_penetration_tests_route_to_human); + auto out = run(); + for (const auto& t : out.tests) { + if (t.kind == SecurityTestKind::SqlInjectionProbe || + t.kind == SecurityTestKind::XssPayload || + t.kind == SecurityTestKind::AuthBypass) { + CHECK(t.routeTo == "human", "complex pen test should route to human"); + CHECK(t.humanReviewRequired, "complex pen test should require human review"); + } + } + PASS(); +} + +void test_complex_pen_tests_forced_human_when_disabled_flag() { + TEST(complex_pen_tests_forced_human_when_disabled_flag); + auto out = run("api", "User", false); + bool hasNote = false; + for (const auto& n : out.notes) { + if (n.find("forced to human review") != std::string::npos) hasNote = true; + } + CHECK(hasNote, "missing forced-human note"); + PASS(); +} + +void test_test_names_include_module_name() { + TEST(test_names_include_module_name); + auto out = run("payments", "Invoice"); + for (const auto& t : out.tests) { + CHECK(t.testName.find("payments") != std::string::npos, "module name missing in test name"); + } + PASS(); +} + +void test_entity_name_propagates_into_intent_text() { + TEST(entity_name_propagates_into_intent_text); + auto out = run("api", "Invoice"); + bool mentioned = false; + for (const auto& t : out.tests) { + if (t.intentAnnotation.find("Invoice") != std::string::npos) mentioned = true; + } + CHECK(mentioned, "entity name not propagated"); + PASS(); +} + +void test_skeleton_output_contains_summary_note() { + TEST(skeleton_output_contains_summary_note); + auto out = run(); + bool found = false; + for (const auto& n : out.notes) { + if (n.find("Security test skeletons generated") != std::string::npos) found = true; + } + CHECK(found, "missing generation summary note"); + PASS(); +} + +int main() { + std::cout << "Step 491: Security Testing Skeleton Generation Tests\n"; + + test_generates_input_validation_fuzz_test(); // 1 + test_generates_auth_bypass_test(); // 2 + test_generates_sql_injection_probe_test(); // 3 + test_generates_xss_payload_test(); // 4 + test_generates_access_control_matrix_test(); // 5 + test_all_tests_include_intent_annotation(); // 6 + test_simple_validation_tests_route_to_slm(); // 7 + test_complex_penetration_tests_route_to_human(); // 8 + test_complex_pen_tests_forced_human_when_disabled_flag(); // 9 + test_test_names_include_module_name(); // 10 + test_entity_name_propagates_into_intent_text(); // 11 + test_skeleton_output_contains_summary_note(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index a851fcd..c71c369 100644 --- a/progress.md +++ b/progress.md @@ -7262,3 +7262,43 @@ E1300-range diagnostics and overlay-ready graph data. - `editor/src/ThreatModelIntegration.h` within header-size limit (`126` <= `600`) - `editor/tests/step490_test.cpp` within test-file size guidance (`167` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 491: Security Testing Skeleton Generation +**Status:** PASS (12/12 tests) + +Adds security-focused test skeleton generation so threat-relevant test +coverage is scaffolded alongside code and routed by complexity/risk. + +**Files added:** +- `editor/src/SecurityTestSkeletonGenerator.h` — security test skeleton engine: + - `SecurityTestKind`, `SecurityTestCaseSpec` + - `SecuritySkeletonRequest`, `SecuritySkeletonOutput` + - generated test classes: + - input validation fuzz + - auth bypass + - SQL injection probe + - XSS payload probe + - access control matrix + - per-test `@Intent(...)` annotation generation + - routing hints: + - simple validation/access matrix -> `slm` + - penetration-style tests -> `human` + `humanReviewRequired` + - optional force-human behavior for complex pen-test categories +- `editor/tests/step491_test.cpp` — 12 tests covering: + - presence of each security test kind + - intent annotation completeness + - routing policy behavior by test complexity + - force-human mode behavior and notes + - module/entity propagation into test names/intent text + - summary note emission +- `editor/CMakeLists.txt` — `step491_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step491_test step490_test` — PASS +- `./editor/build-native/step491_test` — PASS (12/12) +- `./editor/build-native/step490_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/SecurityTestSkeletonGenerator.h` within header-size limit (`140` <= `600`) +- `editor/tests/step491_test.cpp` within test-file size guidance (`164` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`