Complete Step 491: security test skeleton generation
This commit is contained in:
@@ -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)
|
||||
|
||||
140
editor/src/SecurityTestSkeletonGenerator.h
Normal file
140
editor/src/SecurityTestSkeletonGenerator.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
// Step 491: Security Testing Skeleton Generation
|
||||
// Generates security-focused test stubs and routing hints.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<SecurityTestCaseSpec> tests;
|
||||
std::vector<std::string> 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 <script>alert(1)</script> 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;
|
||||
}
|
||||
};
|
||||
164
editor/tests/step491_test.cpp
Normal file
164
editor/tests/step491_test.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Step 491: Security Testing Skeleton Generation Tests (12 tests)
|
||||
|
||||
#include "SecurityTestSkeletonGenerator.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
}
|
||||
40
progress.md
40
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`
|
||||
|
||||
Reference in New Issue
Block a user