torusguard 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.torusguard/.manifest.json +113 -9
- package/.torusguard/rules/README.md +93 -62
- package/.torusguard/rules/TG-AUTH-001-weak-password-storage.md +103 -0
- package/.torusguard/rules/TG-AUTH-002-client-only-authorization.md +85 -0
- package/.torusguard/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/.torusguard/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/.torusguard/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/.torusguard/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/.torusguard/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/.torusguard/rules/TG-DB-001-frontend-database-query.md +83 -0
- package/.torusguard/rules/TG-DB-002-privileged-database-credential.md +86 -0
- package/.torusguard/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/.torusguard/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/.torusguard/rules/TG-INPUT-001-missing-server-validation.md +98 -0
- package/.torusguard/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/.torusguard/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/.torusguard/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/.torusguard/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/.torusguard/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/.torusguard/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/.torusguard/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/.torusguard/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/.torusguard/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/.torusguard/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/.torusguard/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/.torusguard/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/.torusguard/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/.torusguard/rules/TG-SEC-002-public-environment-secrets.md +83 -0
- package/.torusguard/rules/TG-SEC-003-tracked-env-file.md +79 -0
- package/.torusguard/rules/TG-SEC-004-sensitive-logging.md +83 -0
- package/.torusguard/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
- package/.torusguard/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
- package/.torusguard/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/.torusguard/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/.torusguard/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/.torusguard/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/.torusguard/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/.torusguard/rules/active/TG-DB-001-frontend-database-query.md +83 -0
- package/.torusguard/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
- package/.torusguard/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/.torusguard/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/.torusguard/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
- package/.torusguard/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/.torusguard/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/.torusguard/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/.torusguard/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/.torusguard/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/.torusguard/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/.torusguard/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/.torusguard/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/.torusguard/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/.torusguard/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/.torusguard/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/.torusguard/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/.torusguard/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/.torusguard/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
- package/.torusguard/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
- package/.torusguard/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
- package/.torusguard/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
- package/.torusguard/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
- package/.torusguard/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
- package/.torusguard/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
- package/.torusguard/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
- package/.torusguard/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
- package/.torusguard/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
- package/.torusguard/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
- package/.torusguard/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
- package/.torusguard/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
- package/.torusguard/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
- package/.torusguard/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
- package/.torusguard/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
- package/.torusguard/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
- package/.torusguard/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
- package/.torusguard/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
- package/.torusguard/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
- package/.torusguard/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
- package/.torusguard/runs/report-latest.html +62 -20
- package/.torusguard/runs/run-20260908-112648-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-112648-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-112648-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-112648-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-112722-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-112722-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-112722-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-112722-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-113004-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-113004-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-113004-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-113004-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-113127-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-113127-audit/manifest.json +13 -0
- package/.torusguard/runs/run-20260908-113127-audit/remediation.md +262 -0
- package/.torusguard/runs/run-20260908-113127-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-115240-audit/apply_plan.md +6 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/diff_summary.md +221 -0
- package/.torusguard/runs/run-20260908-115240-audit/findings.json +1553 -0
- package/.torusguard/runs/run-20260908-115240-audit/findings.md +572 -0
- package/.torusguard/runs/run-20260908-115240-audit/manifest.json +14 -0
- package/.torusguard/runs/run-20260908-115240-audit/recheck.md +91 -0
- package/.torusguard/runs/run-20260908-115240-audit/remediation.md +278 -0
- package/.torusguard/runs/run-20260908-115240-audit/summary.md +5 -0
- package/.torusguard/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/run_manager.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/stack_detect.cpython-311.pyc +0 -0
- package/.torusguard/scripts/apply_runner.py +339 -0
- package/.torusguard/scripts/audit_runner.py +550 -0
- package/.torusguard/scripts/diff_guard.py +52 -4
- package/.torusguard/scripts/finding_scorer.py +58 -0
- package/.torusguard/scripts/harden_runner.py +375 -0
- package/.torusguard/scripts/html_reporter.py +25 -10
- package/.torusguard/scripts/manifest_builder.py +2 -0
- package/.torusguard/scripts/memory_engine.py +4 -0
- package/.torusguard/scripts/recheck_runner.py +221 -0
- package/.torusguard/scripts/recipes_runner.py +130 -0
- package/.torusguard/scripts/run_manager.py +17 -5
- package/.torusguard/skills/torusguard/SKILL.md +42 -42
- package/.torusguard/skills/torusguard/bootstrap.py +149 -70
- package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/client/src/App.jsx.bak +63 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/server/index.js.bak +82 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/engine/fixture_manager.py.bak +245 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/master_validation.py.bak +698 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/runner.py.bak +551 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_e2e.py.bak +498 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_qa_v0_6_0.py.bak +572 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_1_scale.py.bak +371 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_2_modern_stacks.py.bak +462 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_3_hardening.py.bak +444 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_0_0_memory.py.bak +302 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_1_0_advanced_memory.py.bak +342 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/skills/torusguard/payload/scripts/harden_runner.py.bak +353 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/tests/test_v0_6_0_governed_remediation.py.bak +353 -0
- package/README.md +70 -33
- package/bin/torusguard.js +92 -22
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/bootstrap.py +147 -68
- package/skills/torusguard/payload/.manifest.json +113 -9
- package/skills/torusguard/payload/rules/README.md +93 -62
- package/skills/torusguard/payload/rules/TG-AUTH-001-weak-password-storage.md +103 -0
- package/skills/torusguard/payload/rules/TG-AUTH-002-client-only-authorization.md +85 -0
- package/skills/torusguard/payload/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/skills/torusguard/payload/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/skills/torusguard/payload/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/skills/torusguard/payload/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/skills/torusguard/payload/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/skills/torusguard/payload/rules/TG-DB-001-frontend-database-query.md +83 -0
- package/skills/torusguard/payload/rules/TG-DB-002-privileged-database-credential.md +86 -0
- package/skills/torusguard/payload/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/skills/torusguard/payload/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/skills/torusguard/payload/rules/TG-INPUT-001-missing-server-validation.md +98 -0
- package/skills/torusguard/payload/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/skills/torusguard/payload/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/skills/torusguard/payload/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/skills/torusguard/payload/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/skills/torusguard/payload/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/skills/torusguard/payload/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/skills/torusguard/payload/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/skills/torusguard/payload/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/skills/torusguard/payload/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/skills/torusguard/payload/rules/TG-SEC-002-public-environment-secrets.md +83 -0
- package/skills/torusguard/payload/rules/TG-SEC-003-tracked-env-file.md +79 -0
- package/skills/torusguard/payload/rules/TG-SEC-004-sensitive-logging.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/skills/torusguard/payload/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-DB-001-frontend-database-query.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
- package/skills/torusguard/payload/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/skills/torusguard/payload/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
- package/skills/torusguard/payload/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
- package/skills/torusguard/payload/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
- package/skills/torusguard/payload/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
- package/skills/torusguard/payload/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
- package/skills/torusguard/payload/scripts/apply_runner.py +339 -0
- package/skills/torusguard/payload/scripts/audit_runner.py +550 -0
- package/skills/torusguard/payload/scripts/diff_guard.py +52 -4
- package/skills/torusguard/payload/scripts/finding_scorer.py +58 -0
- package/skills/torusguard/payload/scripts/harden_runner.py +375 -0
- package/skills/torusguard/payload/scripts/html_reporter.py +25 -10
- package/skills/torusguard/payload/scripts/manifest_builder.py +2 -0
- package/skills/torusguard/payload/scripts/memory_engine.py +4 -0
- package/skills/torusguard/payload/scripts/recheck_runner.py +221 -0
- package/skills/torusguard/payload/scripts/recipes_runner.py +130 -0
- package/skills/torusguard/payload/scripts/run_manager.py +17 -5
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +147 -68
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard v1.0.0 Adaptive Security Memory Engine Test Suite
|
|
4
|
+
Validates:
|
|
5
|
+
1. Memory directory scaffolding & privacy isolation (.gitignore)
|
|
6
|
+
2. Event recording across all 6 event types
|
|
7
|
+
3. Pattern distillation (amplification & categorization)
|
|
8
|
+
4. Pre-computed context window computation (JSON cards & token budget <= 2000)
|
|
9
|
+
5. False positive suppression
|
|
10
|
+
6. Memory decay (TTL enforcement)
|
|
11
|
+
7. Export and Import roundtrip
|
|
12
|
+
8. Memory compaction (events archive)
|
|
13
|
+
9. Privacy verification (npm tarball & git status)
|
|
14
|
+
10. Memory-augmented confidence scoring
|
|
15
|
+
11. Content-aware diff regression detection (TG-DIFF-004)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import json
|
|
21
|
+
import shutil
|
|
22
|
+
import tempfile
|
|
23
|
+
import datetime
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
# Ensure UTF-8 stdout/stderr on Windows consoles
|
|
27
|
+
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
|
|
28
|
+
try:
|
|
29
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
if sys.stderr and hasattr(sys.stderr, "reconfigure"):
|
|
33
|
+
try:
|
|
34
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
ROOT_DIR = Path(__file__).resolve().parent.parent
|
|
39
|
+
SCRIPTS_DIR = ROOT_DIR / ".torusguard" / "scripts"
|
|
40
|
+
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
41
|
+
|
|
42
|
+
import memory_engine # type: ignore
|
|
43
|
+
import finding_scorer # type: ignore
|
|
44
|
+
import diff_guard # type: ignore
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_v1_0_0_memory_engine():
|
|
48
|
+
print("=" * 80)
|
|
49
|
+
print("TORUSGUARD v1.0.0 ADAPTIVE SECURITY MEMORY ENGINE TEST SUITE")
|
|
50
|
+
print("=" * 80)
|
|
51
|
+
|
|
52
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
53
|
+
test_root = Path(tmp_dir).resolve()
|
|
54
|
+
tg_dir = test_root / ".torusguard"
|
|
55
|
+
tg_dir.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------
|
|
58
|
+
# 1. Test Scaffolding & Memory Directory Structure
|
|
59
|
+
# ---------------------------------------------------------------------
|
|
60
|
+
print("\n--- 1. Testing Memory Scaffolding & Structure ---")
|
|
61
|
+
paths = memory_engine.ensure_memory_structure(root_dir=test_root)
|
|
62
|
+
assert paths["memory"].is_dir(), "Memory directory not created"
|
|
63
|
+
assert paths["events"].is_dir(), "Events directory not created"
|
|
64
|
+
assert paths["gitignore"].is_file(), "Missing memory/.gitignore"
|
|
65
|
+
assert paths["gitignore"].read_text(encoding="utf-8").strip() == "*", "memory/.gitignore must contain *"
|
|
66
|
+
assert paths["patterns"].is_file(), "Missing patterns.json"
|
|
67
|
+
assert paths["context"].is_file(), "Missing context.json"
|
|
68
|
+
assert paths["profile"].is_file(), "Missing profile.json"
|
|
69
|
+
assert paths["decay"].is_file(), "Missing decay.json"
|
|
70
|
+
assert (paths["events"] / ".gitkeep").is_file(), "Missing events/.gitkeep"
|
|
71
|
+
print(" [PASS] Memory directory structure, files, and isolation .gitignore created cleanly")
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------
|
|
74
|
+
# 2. Test Event Recording Across All 6 Types
|
|
75
|
+
# ---------------------------------------------------------------------
|
|
76
|
+
print("\n--- 2. Testing Event Recording (All 6 Event Types) ---")
|
|
77
|
+
event_types = [
|
|
78
|
+
("audit_finding", {"rule_id": "TG-DB-004", "file_path": "src/views.py", "line_number": 42, "severity": "high", "confidence_score": 85, "code_hash": "abc123hash"}),
|
|
79
|
+
("fix_applied", {"rule_id": "TG-DB-004", "file_path": "src/views.py", "line_number": 42, "fix_strategy": "tenant_filter_added"}),
|
|
80
|
+
("fix_verified", {"rule_id": "TG-DB-004", "file_path": "src/views.py", "verification_result": "fixed"}),
|
|
81
|
+
("false_positive", {"rule_id": "TG-INPUT-002", "file_path": "tests/mock.py", "suppression_reason": "Test fixture intentional sink"}),
|
|
82
|
+
("pattern_learned", {"rule_id": "TG-SEC-001", "metadata": {"note": "Learned from manual review"}}),
|
|
83
|
+
("stack_changed", {"metadata": {"old": "Flask", "new": "FastAPI"}})
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
recorded_events = []
|
|
87
|
+
for etype, edata in event_types:
|
|
88
|
+
evt = memory_engine.record_event(etype, edata, root_dir=test_root)
|
|
89
|
+
assert evt.get("event_type") == etype, f"Recorded wrong type: {etype}"
|
|
90
|
+
assert evt.get("event_id", "").startswith("evt-"), "Missing or invalid event_id"
|
|
91
|
+
assert evt.get("timestamp"), "Missing timestamp"
|
|
92
|
+
assert evt.get("version") in ("1.0.0", memory_engine.VERSION), "Version mismatch in event"
|
|
93
|
+
recorded_events.append(evt)
|
|
94
|
+
|
|
95
|
+
all_events = memory_engine.load_all_events(root_dir=test_root)
|
|
96
|
+
assert len(all_events) == 6, f"Expected 6 events, found {len(all_events)}"
|
|
97
|
+
print(f" [PASS] All 6 event types successfully logged and validated ({len(all_events)} events)")
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------
|
|
100
|
+
# 3. Test Pattern Distillation & Confidence Amplification
|
|
101
|
+
# ---------------------------------------------------------------------
|
|
102
|
+
print("\n--- 3. Testing Pattern Distillation & Confidence Amplification ---")
|
|
103
|
+
# Add repeated fixes across multiple files to test amplification
|
|
104
|
+
memory_engine.record_event("audit_finding", {"rule_id": "TG-DB-004", "file_path": "src/models.py", "severity": "high"}, root_dir=test_root)
|
|
105
|
+
memory_engine.record_event("fix_applied", {"rule_id": "TG-DB-004", "file_path": "src/models.py", "fix_strategy": "tenant_filter_added"}, root_dir=test_root)
|
|
106
|
+
memory_engine.record_event("fix_verified", {"rule_id": "TG-DB-004", "file_path": "src/models.py", "verification_result": "fixed"}, root_dir=test_root)
|
|
107
|
+
|
|
108
|
+
patterns = memory_engine.distill_patterns(root_dir=test_root)
|
|
109
|
+
assert len(patterns) >= 2, f"Expected at least 2 patterns, found {len(patterns)}"
|
|
110
|
+
|
|
111
|
+
# Find recurring fix pattern for TG-DB-004
|
|
112
|
+
db_pats = [p for p in patterns if p.get("rule_id") == "TG-DB-004"]
|
|
113
|
+
assert len(db_pats) >= 1, "TG-DB-004 pattern not distilled"
|
|
114
|
+
tg_pat = db_pats[0]
|
|
115
|
+
assert tg_pat.get("occurrences", 0) >= 2, "Expected multiple occurrences"
|
|
116
|
+
assert len(tg_pat.get("affected_files", [])) >= 2, "Expected multi-file tracking"
|
|
117
|
+
assert tg_pat.get("confidence", 0) >= 70, f"Confidence not amplified: {tg_pat.get('confidence')}"
|
|
118
|
+
print(f" [PASS] Distilled {len(patterns)} patterns with multi-file confidence amplification (Score: {tg_pat.get('confidence')})")
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------------
|
|
121
|
+
# 4. Test Pre-Computed Context Window (JSON Cards & Budget Enforcement)
|
|
122
|
+
# ---------------------------------------------------------------------
|
|
123
|
+
print("\n--- 4. Testing Context Window Generation & Token Budget ---")
|
|
124
|
+
ctx = memory_engine.compute_context_window(max_tokens=2000, root_dir=test_root)
|
|
125
|
+
assert ctx.get("version") in ("1.0.0", memory_engine.VERSION), "Context version mismatch"
|
|
126
|
+
assert ctx.get("token_estimate", 0) <= 2000, "Context exceeds 2000 token budget"
|
|
127
|
+
assert len(ctx.get("cards", [])) >= 2, "Context missing intelligence cards"
|
|
128
|
+
|
|
129
|
+
# Check card format: card_id, card_type, priority, title, summary, card_data
|
|
130
|
+
for c in ctx["cards"]:
|
|
131
|
+
assert "card_id" in c and "card_type" in c and "priority" in c
|
|
132
|
+
assert "title" in c and "summary" in c and "card_data" in c
|
|
133
|
+
assert c["card_type"] in ("profile", "pattern", "regression_watch", "false_positive", "false_positive_suppression", "fix_idiom", "common_vulnerability", "golden_recipe")
|
|
134
|
+
|
|
135
|
+
print(f" [PASS] Generated {len(ctx['cards'])} structured JSON cards within budget ({ctx['token_estimate']}/2000 tokens)")
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------
|
|
138
|
+
# 5. Test False Positive Suppression
|
|
139
|
+
# ---------------------------------------------------------------------
|
|
140
|
+
print("\n--- 5. Testing False Positive Suppression ---")
|
|
141
|
+
fp_evt = memory_engine.record_false_positive("TG-SEC-999", file_path="config/test.py", reason="Mock test token", root_dir=test_root)
|
|
142
|
+
assert fp_evt.get("rule_id") == "TG-SEC-999"
|
|
143
|
+
|
|
144
|
+
fp_pats = [p for p in memory_engine.distill_patterns(root_dir=test_root) if p.get("rule_id") == "TG-SEC-999"]
|
|
145
|
+
assert len(fp_pats) == 1, "False positive pattern not found"
|
|
146
|
+
assert fp_pats[0]["pattern_type"] == "false_positive_class"
|
|
147
|
+
print(" [PASS] False positive suppression successfully cataloged into pattern store")
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------
|
|
150
|
+
# 6. Test Memory Decay (TTL Expiration)
|
|
151
|
+
# ---------------------------------------------------------------------
|
|
152
|
+
print("\n--- 6. Testing Memory Decay (TTL Enforcement) ---")
|
|
153
|
+
# Artificially age a pattern's last_seen and decay_checkpoint by 100 days
|
|
154
|
+
pats = json.loads(paths["patterns"].read_text(encoding="utf-8"))
|
|
155
|
+
old_dt = (datetime.datetime.utcnow() - datetime.timedelta(days=100)).isoformat() + "Z"
|
|
156
|
+
for p in pats:
|
|
157
|
+
if p.get("rule_id") == "TG-DB-004":
|
|
158
|
+
p["last_seen"] = old_dt
|
|
159
|
+
p["decay_checkpoint"] = old_dt
|
|
160
|
+
p["confidence"] = 80
|
|
161
|
+
paths["patterns"].write_text(json.dumps(pats, indent=2), encoding="utf-8")
|
|
162
|
+
|
|
163
|
+
decayed = memory_engine.decay_stale_entries(ttl_days=90, root_dir=test_root)
|
|
164
|
+
assert decayed >= 1, f"Expected decay of aged pattern, decayed: {decayed}"
|
|
165
|
+
|
|
166
|
+
pats_after = json.loads(paths["patterns"].read_text(encoding="utf-8"))
|
|
167
|
+
db_after = [p for p in pats_after if p.get("rule_id") == "TG-DB-004"][0]
|
|
168
|
+
assert db_after["confidence"] < 80, f"Confidence did not decay: {db_after['confidence']}"
|
|
169
|
+
print(f" [PASS] Memory decay correctly reduced confidence of 100-day-old pattern ({80} -> {db_after['confidence']})")
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------
|
|
172
|
+
# 7. Test Export and Import Roundtrip
|
|
173
|
+
# ---------------------------------------------------------------------
|
|
174
|
+
print("\n--- 7. Testing Export and Import Roundtrip ---")
|
|
175
|
+
export_file = test_root / "export_test.json"
|
|
176
|
+
exp_res = memory_engine.export_memory(str(export_file), root_dir=test_root)
|
|
177
|
+
assert export_file.is_file(), "Export file not created"
|
|
178
|
+
assert exp_res["exported_events_count"] > 0, "No events exported"
|
|
179
|
+
|
|
180
|
+
with open(export_file, "r", encoding="utf-8") as f:
|
|
181
|
+
exp_data = json.load(f)
|
|
182
|
+
assert exp_data.get("format") == "torusguard-memory-bundle"
|
|
183
|
+
assert len(exp_data.get("events", [])) > 0
|
|
184
|
+
|
|
185
|
+
# Import into fresh workspace
|
|
186
|
+
fresh_root = Path(tempfile.mkdtemp()).resolve()
|
|
187
|
+
try:
|
|
188
|
+
imp_res = memory_engine.import_memory(str(export_file), root_dir=fresh_root)
|
|
189
|
+
assert imp_res["imported_events_count"] == exp_res["exported_events_count"]
|
|
190
|
+
fresh_pats = memory_engine.distill_patterns(root_dir=fresh_root)
|
|
191
|
+
assert len(fresh_pats) > 0, "No patterns distilled after import"
|
|
192
|
+
print(f" [PASS] Export/Import roundtrip verified: {imp_res['imported_events_count']} events transferred cleanly")
|
|
193
|
+
finally:
|
|
194
|
+
shutil.rmtree(fresh_root, ignore_errors=True)
|
|
195
|
+
|
|
196
|
+
# ---------------------------------------------------------------------
|
|
197
|
+
# 8. Test Memory Compaction
|
|
198
|
+
# ---------------------------------------------------------------------
|
|
199
|
+
print("\n--- 8. Testing Memory Compaction ---")
|
|
200
|
+
# Artificially age several loose event files by 40 days
|
|
201
|
+
old_ts = (datetime.datetime.utcnow() - datetime.timedelta(days=40)).isoformat() + "Z"
|
|
202
|
+
for evt_f in paths["events"].glob("*.json"):
|
|
203
|
+
if evt_f.name == "compacted_archive.json":
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
data = json.loads(evt_f.read_text(encoding="utf-8"))
|
|
207
|
+
data["timestamp"] = old_ts
|
|
208
|
+
evt_f.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
compacted = memory_engine.compact_events(older_than_days=30, root_dir=test_root)
|
|
213
|
+
assert compacted > 0, "No events compacted"
|
|
214
|
+
assert paths["compacted"].is_file(), "Missing compacted_archive.json"
|
|
215
|
+
print(f" [PASS] Compacted {compacted} aged event files into single archive")
|
|
216
|
+
|
|
217
|
+
# ---------------------------------------------------------------------
|
|
218
|
+
# 9. Test Memory-Augmented Finding Scorer
|
|
219
|
+
# ---------------------------------------------------------------------
|
|
220
|
+
print("\n--- 9. Testing Memory-Augmented Finding Scorer ---")
|
|
221
|
+
# Case A: False positive rule should receive negative boost (-30)
|
|
222
|
+
score_fp, band_fp, factors_fp = finding_scorer.compute_confidence_score(
|
|
223
|
+
evidence_quality=35,
|
|
224
|
+
reproduction_success=0,
|
|
225
|
+
independent_confirmations=15,
|
|
226
|
+
environmental_clarity=15,
|
|
227
|
+
rule_id="TG-SEC-999",
|
|
228
|
+
root_dir=test_root
|
|
229
|
+
)
|
|
230
|
+
assert factors_fp["memory_boost"] <= -20, f"False positive was not suppressed: {factors_fp}"
|
|
231
|
+
assert score_fp < 50, f"Suppressed rule score too high: {score_fp}"
|
|
232
|
+
|
|
233
|
+
# Case B: Recurring pattern receives positive boost
|
|
234
|
+
score_rec, band_rec, factors_rec = finding_scorer.compute_confidence_score(
|
|
235
|
+
evidence_quality=35,
|
|
236
|
+
reproduction_success=25,
|
|
237
|
+
independent_confirmations=15,
|
|
238
|
+
environmental_clarity=15,
|
|
239
|
+
rule_id="TG-DB-004",
|
|
240
|
+
file_path="src/views.py",
|
|
241
|
+
root_dir=test_root
|
|
242
|
+
)
|
|
243
|
+
assert factors_rec["memory_boost"] >= 10, f"Recurring pattern boost not applied: {factors_rec}"
|
|
244
|
+
print(f" [PASS] Finding scorer correctly applies memory boost: Suppressed={factors_fp['memory_boost']}pts, Recurring=+{factors_rec['memory_boost']}pts")
|
|
245
|
+
|
|
246
|
+
# ---------------------------------------------------------------------
|
|
247
|
+
# 10. Test Diff Guard Regression Detection (TG-DIFF-004)
|
|
248
|
+
# ---------------------------------------------------------------------
|
|
249
|
+
print("\n--- 10. Testing Diff Guard Regression Detection ---")
|
|
250
|
+
# Add regression watch pattern to memory
|
|
251
|
+
memory_engine.record_event("fix_verified", {
|
|
252
|
+
"rule_id": "TG-DB-004",
|
|
253
|
+
"file_path": "src/billing.py",
|
|
254
|
+
"verification_result": "regressed"
|
|
255
|
+
}, root_dir=test_root)
|
|
256
|
+
memory_engine.distill_patterns(root_dir=test_root)
|
|
257
|
+
|
|
258
|
+
# Diff modifying src/billing.py
|
|
259
|
+
regression_diff = """--- a/src/billing.py
|
|
260
|
+
+++ b/src/billing.py
|
|
261
|
+
@@ -12,2 +12,2 @@
|
|
262
|
+
-def get_bill(id, tenant):
|
|
263
|
+
- return Bill.objects.filter(id=id, tenant=tenant)
|
|
264
|
+
+def get_bill(id):
|
|
265
|
+
+ return Bill.objects.get(id=id)
|
|
266
|
+
"""
|
|
267
|
+
res_reg = diff_guard.audit_diff(regression_diff, check_memory=True, root_dir=test_root)
|
|
268
|
+
assert res_reg["status"] == "BLOCKED", f"Expected diff to be blocked by regression check: {res_reg}"
|
|
269
|
+
rule_ids = [v["rule_id"] for v in res_reg["violations"]]
|
|
270
|
+
assert "TG-DIFF-004" in rule_ids, f"Expected TG-DIFF-004 violation, got: {rule_ids}"
|
|
271
|
+
print(f" [PASS] Diff Guard caught TG-DIFF-004 regression on file tracked in memory")
|
|
272
|
+
|
|
273
|
+
# ---------------------------------------------------------------------
|
|
274
|
+
# 11. Test Privacy Invariants (.gitignore and NPM pack isolation)
|
|
275
|
+
# ---------------------------------------------------------------------
|
|
276
|
+
print("\n--- 11. Testing Privacy & NPM Pack Isolation ---")
|
|
277
|
+
root_gi = (ROOT_DIR / ".gitignore").read_text(encoding="utf-8")
|
|
278
|
+
assert ".torusguard/memory/" in root_gi, "Root .gitignore missing .torusguard/memory/"
|
|
279
|
+
|
|
280
|
+
# Check npm pack does not include memory
|
|
281
|
+
import subprocess
|
|
282
|
+
npm_bin = "npm.cmd" if sys.platform == "win32" else "npm"
|
|
283
|
+
pack_res = subprocess.run([npm_bin, "pack", "--dry-run"], cwd=str(ROOT_DIR), capture_output=True, text=True, encoding="utf-8", errors="replace")
|
|
284
|
+
assert pack_res.returncode == 0, f"npm pack failed: {pack_res.stderr}"
|
|
285
|
+
assert ".torusguard/memory" not in pack_res.stdout and ".torusguard/memory" not in pack_res.stderr, "Memory leaked into npm package!"
|
|
286
|
+
print(" [PASS] Memory directory completely excluded from git and npm release package")
|
|
287
|
+
|
|
288
|
+
print("\n" + "=" * 80)
|
|
289
|
+
print(">>> ALL 11 TORUSGUARD v1.0.0 MEMORY ENGINE CHECKS PASSED (100%) <<<")
|
|
290
|
+
print("=" * 80 + "\n")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
try:
|
|
295
|
+
test_v1_0_0_memory_engine()
|
|
296
|
+
sys.exit(0)
|
|
297
|
+
except AssertionError as e:
|
|
298
|
+
print(f"\n[FAIL] {e}")
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
except Exception as e:
|
|
301
|
+
print(f"\n[ERROR] Unexpected error: {e}")
|
|
302
|
+
sys.exit(2)
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard v1.1.0 Advanced Memory Engine Test Suite
|
|
4
|
+
Validates Option B enhancements:
|
|
5
|
+
1. File path and rule-family proximity scoring (compute_proximity_score)
|
|
6
|
+
2. Golden Fix Recipe storage and strict Ponytail bound enforcement (<=35 add, <=25 del)
|
|
7
|
+
3. Golden Fix Recipe schema compliance against golden-recipe.schema.json
|
|
8
|
+
4. Role-tailored context window generation (auditor, remediator, reviewer, all)
|
|
9
|
+
5. Finding confidence scoring with directory and file-proximity modifiers
|
|
10
|
+
6. Git pre-commit hook installation and uninstallation
|
|
11
|
+
7. Git commit history learning (learn_from_git)
|
|
12
|
+
8. Sanitized team export (stripping absolute paths and sensitive tokens)
|
|
13
|
+
9. Diff guard pre-commit inspection (--pre-commit)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import json
|
|
19
|
+
import shutil
|
|
20
|
+
import tempfile
|
|
21
|
+
import datetime
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Dict, Any, List
|
|
24
|
+
|
|
25
|
+
# Ensure UTF-8 stdout/stderr on Windows consoles
|
|
26
|
+
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
|
|
27
|
+
try:
|
|
28
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
if sys.stderr and hasattr(sys.stderr, "reconfigure"):
|
|
32
|
+
try:
|
|
33
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
ROOT_DIR = Path(__file__).resolve().parent.parent
|
|
38
|
+
SCRIPTS_DIR = ROOT_DIR / ".torusguard" / "scripts"
|
|
39
|
+
SCHEMAS_DIR = ROOT_DIR / ".torusguard" / "schemas"
|
|
40
|
+
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
41
|
+
|
|
42
|
+
import memory_engine # type: ignore
|
|
43
|
+
import finding_scorer # type: ignore
|
|
44
|
+
import diff_guard # type: ignore
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_v1_1_0_advanced_memory():
|
|
48
|
+
print("=" * 80)
|
|
49
|
+
print("TORUSGUARD v1.1.0 ADVANCED MEMORY ENGINE & GOVERNANCE TEST SUITE")
|
|
50
|
+
print("=" * 80)
|
|
51
|
+
|
|
52
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
53
|
+
test_root = Path(tmp_dir).resolve()
|
|
54
|
+
tg_dir = test_root / ".torusguard"
|
|
55
|
+
tg_dir.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------
|
|
58
|
+
# 1. Proximity Scoring (File Path, Directory, Extension, Rule Family)
|
|
59
|
+
# ---------------------------------------------------------------------
|
|
60
|
+
print("\n--- 1. Testing Proximity Scoring (compute_proximity_score) ---")
|
|
61
|
+
mock_pattern: Dict[str, Any] = {
|
|
62
|
+
"pattern_id": "pat-test-01",
|
|
63
|
+
"rule_id": "TG-DB-004",
|
|
64
|
+
"affected_files": ["src/api/auth.py", "src/api/users.py"],
|
|
65
|
+
"file_type": ".py"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Exact file match + Exact rule match
|
|
69
|
+
s_exact = memory_engine.compute_proximity_score(
|
|
70
|
+
mock_pattern,
|
|
71
|
+
target_file="src/api/auth.py",
|
|
72
|
+
target_rule_id="TG-DB-004"
|
|
73
|
+
)
|
|
74
|
+
assert s_exact >= 90, f"Expected exact score >= 90, got {s_exact}"
|
|
75
|
+
|
|
76
|
+
# Directory match + Family rule match (TG-DB-001 vs TG-DB-004)
|
|
77
|
+
s_dir = memory_engine.compute_proximity_score(
|
|
78
|
+
mock_pattern,
|
|
79
|
+
target_file="src/api/billing.py",
|
|
80
|
+
target_rule_id="TG-DB-001"
|
|
81
|
+
)
|
|
82
|
+
assert 40 <= s_dir <= 80, f"Expected dir/family score in [40, 80], got {s_dir}"
|
|
83
|
+
|
|
84
|
+
# Extension match only, different directory, unrelated rule
|
|
85
|
+
s_ext = memory_engine.compute_proximity_score(
|
|
86
|
+
mock_pattern,
|
|
87
|
+
target_file="utils/helper.py",
|
|
88
|
+
target_rule_id="TG-SEC-001"
|
|
89
|
+
)
|
|
90
|
+
assert 10 <= s_ext <= 30, f"Expected ext score in [10, 30], got {s_ext}"
|
|
91
|
+
|
|
92
|
+
# Completely unrelated
|
|
93
|
+
s_none = memory_engine.compute_proximity_score(
|
|
94
|
+
mock_pattern,
|
|
95
|
+
target_file="frontend/App.tsx",
|
|
96
|
+
target_rule_id="TG-CLIENT-001"
|
|
97
|
+
)
|
|
98
|
+
assert s_none == 0, f"Expected 0 for unrelated query, got {s_none}"
|
|
99
|
+
print(f" [PASS] Proximity scoring verified: Exact={s_exact}, Dir={s_dir}, Ext={s_ext}, None={s_none}")
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------
|
|
102
|
+
# 2. Golden Fix Recipe Storage & Strict Ponytail Bound Enforcement
|
|
103
|
+
# ---------------------------------------------------------------------
|
|
104
|
+
print("\n--- 2. Testing Golden Fix Recipe Storage & Ponytail Bounds ---")
|
|
105
|
+
memory_engine.ensure_memory_structure(root_dir=test_root)
|
|
106
|
+
|
|
107
|
+
valid_before = "cursor.execute(f'SELECT * FROM users WHERE id = {uid}')"
|
|
108
|
+
valid_after = "cursor.execute('SELECT * FROM users WHERE id = %s', (uid,))"
|
|
109
|
+
valid_diff = "- cursor.execute(f'SELECT * FROM users WHERE id = {uid}')\n+ cursor.execute('SELECT * FROM users WHERE id = %s', (uid,))"
|
|
110
|
+
|
|
111
|
+
recipe = memory_engine.record_golden_recipe(
|
|
112
|
+
rule_id="TG-DB-001",
|
|
113
|
+
before_snippet=valid_before,
|
|
114
|
+
after_snippet=valid_after,
|
|
115
|
+
diff_snippet=valid_diff,
|
|
116
|
+
file_type=".py",
|
|
117
|
+
framework="FastAPI",
|
|
118
|
+
description="Parameterized SQL execution replacing raw string interpolation",
|
|
119
|
+
additions=1,
|
|
120
|
+
deletions=1,
|
|
121
|
+
root_dir=test_root
|
|
122
|
+
)
|
|
123
|
+
assert recipe["pattern_type"] == "golden_fix_recipe"
|
|
124
|
+
assert recipe["rule_id"] == "TG-DB-001"
|
|
125
|
+
assert recipe["confidence"] >= 0.85
|
|
126
|
+
assert recipe["recipe_data"]["verified_count"] == 1
|
|
127
|
+
assert recipe["recipe_data"]["ponytail_metrics"]["additions"] == 1
|
|
128
|
+
|
|
129
|
+
# Second verification of the same rule boosts confidence and increments count
|
|
130
|
+
recipe2 = memory_engine.record_golden_recipe(
|
|
131
|
+
rule_id="TG-DB-001",
|
|
132
|
+
before_snippet=valid_before,
|
|
133
|
+
after_snippet=valid_after,
|
|
134
|
+
diff_snippet=valid_diff,
|
|
135
|
+
file_type=".py",
|
|
136
|
+
framework="FastAPI",
|
|
137
|
+
description="Re-verified parameterized query",
|
|
138
|
+
additions=1,
|
|
139
|
+
deletions=1,
|
|
140
|
+
root_dir=test_root
|
|
141
|
+
)
|
|
142
|
+
assert recipe2["recipe_data"]["verified_count"] == 2
|
|
143
|
+
assert recipe2["confidence"] > recipe["confidence"]
|
|
144
|
+
print(f" [PASS] Golden recipe recorded & verified count incremented to {recipe2['recipe_data']['verified_count']}")
|
|
145
|
+
|
|
146
|
+
# Ponytail Bound Violations (Must raise ValueError)
|
|
147
|
+
try:
|
|
148
|
+
memory_engine.record_golden_recipe(
|
|
149
|
+
rule_id="TG-DB-001",
|
|
150
|
+
before_snippet="code",
|
|
151
|
+
after_snippet="bloated code",
|
|
152
|
+
diff_snippet="giant diff",
|
|
153
|
+
file_type=".py",
|
|
154
|
+
additions=40, # Exceeds 35 additions bound
|
|
155
|
+
deletions=5,
|
|
156
|
+
root_dir=test_root
|
|
157
|
+
)
|
|
158
|
+
assert False, "Failed to reject recipe exceeding additions bound (>35)"
|
|
159
|
+
except ValueError as err:
|
|
160
|
+
assert "Ponytail" in str(err)
|
|
161
|
+
print(" [PASS] Successfully rejected recipe with >35 additions")
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
memory_engine.record_golden_recipe(
|
|
165
|
+
rule_id="TG-DB-001",
|
|
166
|
+
before_snippet="code",
|
|
167
|
+
after_snippet="deleted code",
|
|
168
|
+
diff_snippet="giant diff",
|
|
169
|
+
file_type=".py",
|
|
170
|
+
additions=5,
|
|
171
|
+
deletions=30, # Exceeds 25 deletions bound
|
|
172
|
+
root_dir=test_root
|
|
173
|
+
)
|
|
174
|
+
assert False, "Failed to reject recipe exceeding deletions bound (>25)"
|
|
175
|
+
except ValueError as err:
|
|
176
|
+
assert "Ponytail" in str(err)
|
|
177
|
+
print(" [PASS] Successfully rejected recipe with >25 deletions")
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------------------------------
|
|
180
|
+
# 3. Golden Recipe Schema Compliance
|
|
181
|
+
# ---------------------------------------------------------------------
|
|
182
|
+
print("\n--- 3. Testing Schema Compliance (golden-recipe.schema.json) ---")
|
|
183
|
+
schema_path = SCHEMAS_DIR / "golden-recipe.schema.json"
|
|
184
|
+
assert schema_path.exists(), f"Schema file not found at {schema_path}"
|
|
185
|
+
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
|
186
|
+
assert schema["title"] == "TorusGuard Golden Fix Recipe Schema"
|
|
187
|
+
|
|
188
|
+
# Validate that stored recipe matches schema required fields
|
|
189
|
+
required_fields = schema["required"]
|
|
190
|
+
recipe_data = recipe["recipe_data"]
|
|
191
|
+
for field in required_fields:
|
|
192
|
+
assert field in recipe_data, f"Recipe missing required schema field: {field}"
|
|
193
|
+
assert recipe_data["ponytail_metrics"]["additions"] <= 35
|
|
194
|
+
assert recipe_data["ponytail_metrics"]["deletions"] <= 25
|
|
195
|
+
print(f" [PASS] Recipe structure matches all {len(required_fields)} required schema attributes")
|
|
196
|
+
|
|
197
|
+
# ---------------------------------------------------------------------
|
|
198
|
+
# 4. Role-Tailored Context Windows (Auditor, Remediator, Reviewer)
|
|
199
|
+
# ---------------------------------------------------------------------
|
|
200
|
+
print("\n--- 4. Testing Role-Tailored Context Windows ---")
|
|
201
|
+
# Add diverse patterns to memory
|
|
202
|
+
memory_engine.record_event("audit_finding", {"rule_id": "TG-SEC-001", "file_path": "config/settings.py", "severity": "critical", "confidence_score": 90}, root_dir=test_root)
|
|
203
|
+
memory_engine.record_event("audit_finding", {"rule_id": "TG-SEC-001", "file_path": "config/settings.py", "severity": "critical", "confidence_score": 90}, root_dir=test_root)
|
|
204
|
+
memory_engine.record_event("false_positive", {"rule_id": "TG-INPUT-003", "file_path": "tests/test_api.py", "suppression_reason": "Mock fixture"}, root_dir=test_root)
|
|
205
|
+
memory_engine.record_event("fix_applied", {"rule_id": "TG-SEC-001", "file_path": "config/settings.py"}, root_dir=test_root)
|
|
206
|
+
memory_engine.record_event("audit_finding", {"rule_id": "TG-SEC-001", "file_path": "config/settings.py", "severity": "critical", "confidence_score": 90}, root_dir=test_root) # Triggers regression_watch
|
|
207
|
+
memory_engine.distill_patterns(root_dir=test_root)
|
|
208
|
+
|
|
209
|
+
# 4a. Auditor Persona Context
|
|
210
|
+
ctx_auditor = memory_engine.compute_context_window(root_dir=test_root, target_role="auditor")
|
|
211
|
+
assert ctx_auditor["role"] == "auditor"
|
|
212
|
+
assert ctx_auditor["token_estimate"] <= 2000
|
|
213
|
+
# Auditor should prioritize common_vulnerability and false_positive_class
|
|
214
|
+
auditor_card_types = [c["type"] for c in ctx_auditor["cards"]]
|
|
215
|
+
print(f" Auditor cards: {auditor_card_types}")
|
|
216
|
+
assert any(t in ("common_vulnerability", "false_positive_suppression") for t in auditor_card_types)
|
|
217
|
+
|
|
218
|
+
# 4b. Remediator Persona Context
|
|
219
|
+
ctx_remediator = memory_engine.compute_context_window(root_dir=test_root, target_role="remediator")
|
|
220
|
+
assert ctx_remediator["role"] == "remediator"
|
|
221
|
+
assert ctx_remediator["token_estimate"] <= 2000
|
|
222
|
+
remediator_card_types = [c["type"] for c in ctx_remediator["cards"]]
|
|
223
|
+
print(f" Remediator cards: {remediator_card_types}")
|
|
224
|
+
# Remediator should include golden_recipe cards
|
|
225
|
+
assert "golden_recipe" in remediator_card_types
|
|
226
|
+
|
|
227
|
+
# 4c. Reviewer Persona Context
|
|
228
|
+
ctx_reviewer = memory_engine.compute_context_window(root_dir=test_root, target_role="reviewer")
|
|
229
|
+
assert ctx_reviewer["role"] == "reviewer"
|
|
230
|
+
assert ctx_reviewer["token_estimate"] <= 2000
|
|
231
|
+
|
|
232
|
+
# 4d. Target-Filtered Context
|
|
233
|
+
ctx_filtered = memory_engine.compute_context_window(
|
|
234
|
+
root_dir=test_root,
|
|
235
|
+
target_role="remediator",
|
|
236
|
+
target_file="config/settings.py",
|
|
237
|
+
target_rule_id="TG-SEC-001"
|
|
238
|
+
)
|
|
239
|
+
assert ctx_filtered["target_query"]["file"] == "config/settings.py"
|
|
240
|
+
assert ctx_filtered["target_query"]["rule_id"] == "TG-SEC-001"
|
|
241
|
+
assert len(ctx_filtered["cards"]) > 1
|
|
242
|
+
assert any(c.get("proximity_score", 0) > 0 for c in ctx_filtered["cards"])
|
|
243
|
+
assert ctx_filtered["cards"][1]["proximity_score"] > 0
|
|
244
|
+
print(f" [PASS] Role-tailored context generation validated across auditor, remediator, reviewer (Tokens <= 2000)")
|
|
245
|
+
|
|
246
|
+
# ---------------------------------------------------------------------
|
|
247
|
+
# 5. Finding Scorer Memory & Directory Proximity Boost
|
|
248
|
+
# ---------------------------------------------------------------------
|
|
249
|
+
print("\n--- 5. Testing Finding Scorer Memory & Proximity Modifiers ---")
|
|
250
|
+
# Direct rule match with exact file match
|
|
251
|
+
b_exact = finding_scorer.compute_memory_boost(
|
|
252
|
+
rule_id="TG-DB-001",
|
|
253
|
+
file_path="src/api/auth.py",
|
|
254
|
+
root_dir=test_root
|
|
255
|
+
)
|
|
256
|
+
# Same directory file match
|
|
257
|
+
b_dir = finding_scorer.compute_memory_boost(
|
|
258
|
+
rule_id="TG-DB-001",
|
|
259
|
+
file_path="src/api/orders.py",
|
|
260
|
+
root_dir=test_root
|
|
261
|
+
)
|
|
262
|
+
# Suppressed FP rule
|
|
263
|
+
b_fp = finding_scorer.compute_memory_boost(
|
|
264
|
+
rule_id="TG-INPUT-003",
|
|
265
|
+
file_path="tests/test_api.py",
|
|
266
|
+
root_dir=test_root
|
|
267
|
+
)
|
|
268
|
+
assert b_fp <= -20, f"Expected FP suppression <= -20, got {b_fp}"
|
|
269
|
+
print(f" [PASS] Scorer memory integration verified: Exact={b_exact}, Dir={b_dir}, FP={b_fp}")
|
|
270
|
+
|
|
271
|
+
# ---------------------------------------------------------------------
|
|
272
|
+
# 6. Git Pre-Commit Hook Installation & Uninstallation
|
|
273
|
+
# ---------------------------------------------------------------------
|
|
274
|
+
print("\n--- 6. Testing Git Pre-Commit Hook Installation & Cleanup ---")
|
|
275
|
+
git_dir = test_root / ".git"
|
|
276
|
+
git_dir.mkdir(parents=True, exist_ok=True)
|
|
277
|
+
|
|
278
|
+
res_install = memory_engine.install_git_hook(root_dir=test_root)
|
|
279
|
+
assert res_install["status"] == "installed"
|
|
280
|
+
hook_file = Path(res_install["hook_path"])
|
|
281
|
+
assert hook_file.exists()
|
|
282
|
+
hook_content = hook_file.read_text(encoding="utf-8")
|
|
283
|
+
assert "diff_guard.py" in hook_content
|
|
284
|
+
assert "--pre-commit" in hook_content
|
|
285
|
+
|
|
286
|
+
res_uninstall = memory_engine.uninstall_git_hook(root_dir=test_root)
|
|
287
|
+
assert res_uninstall["status"] == "uninstalled"
|
|
288
|
+
if hook_file.exists():
|
|
289
|
+
assert "diff_guard.py" not in hook_file.read_text(encoding="utf-8")
|
|
290
|
+
print(" [PASS] Git hook installed, verified, and cleanly uninstalled")
|
|
291
|
+
|
|
292
|
+
# ---------------------------------------------------------------------
|
|
293
|
+
# 7. Git Commit History Learning (learn_from_git)
|
|
294
|
+
# ---------------------------------------------------------------------
|
|
295
|
+
print("\n--- 7. Testing Git Commit Learning ---")
|
|
296
|
+
# In a temp directory with no commits, should handle gracefully with 0 learned
|
|
297
|
+
learned = memory_engine.learn_from_git(root_dir=test_root)
|
|
298
|
+
assert "events_recorded" in learned
|
|
299
|
+
print(f" [PASS] learn_from_git executed safely with {learned['events_recorded']} events on empty repo")
|
|
300
|
+
|
|
301
|
+
# ---------------------------------------------------------------------
|
|
302
|
+
# 8. Sanitized Memory Export (Safe Team Sharing)
|
|
303
|
+
# ---------------------------------------------------------------------
|
|
304
|
+
print("\n--- 8. Testing Sanitized Memory Export ---")
|
|
305
|
+
raw_export_file = test_root / "raw-export.json"
|
|
306
|
+
san_export_file = test_root / "sanitized-export.json"
|
|
307
|
+
|
|
308
|
+
memory_engine.export_memory(target_file=raw_export_file, sanitized=False, root_dir=test_root)
|
|
309
|
+
memory_engine.export_memory(target_file=san_export_file, sanitized=True, root_dir=test_root)
|
|
310
|
+
|
|
311
|
+
san_data = json.loads(san_export_file.read_text(encoding="utf-8"))
|
|
312
|
+
assert san_data.get("sanitized") is True
|
|
313
|
+
# Verify affected_files are relative or basenames, not absolute host paths
|
|
314
|
+
for pat in san_data.get("patterns", []):
|
|
315
|
+
for aff in pat.get("affected_files", []):
|
|
316
|
+
assert not os.path.isabs(aff), f"Found absolute path in sanitized export: {aff}"
|
|
317
|
+
assert "Users" not in aff and "home" not in aff
|
|
318
|
+
print(" [PASS] Sanitized export verified: 100% free of local host absolute paths and secrets")
|
|
319
|
+
|
|
320
|
+
# ---------------------------------------------------------------------
|
|
321
|
+
# 9. Diff Guard Pre-Commit Inspection
|
|
322
|
+
# ---------------------------------------------------------------------
|
|
323
|
+
print("\n--- 9. Testing Diff Guard Pre-Commit Mode ---")
|
|
324
|
+
clean_diff = "diff --git a/app.py b/app.py\n--- a/app.py\n+++ b/app.py\n@@ -1,3 +1,3 @@\n-x = 1\n+x = 2\n"
|
|
325
|
+
res_clean = diff_guard.audit_diff(clean_diff)
|
|
326
|
+
assert res_clean["status"] == "PASSED"
|
|
327
|
+
assert len(res_clean["violations"]) == 0
|
|
328
|
+
|
|
329
|
+
# Run diff_guard on diff with security bypass comment (# nosec)
|
|
330
|
+
bypass_diff = "diff --git a/app.py b/app.py\n--- a/app.py\n+++ b/app.py\n@@ -1,3 +1,3 @@\n+db.query(user_input) # nosec\n"
|
|
331
|
+
res_bypass = diff_guard.audit_diff(bypass_diff)
|
|
332
|
+
assert res_bypass["status"] == "BLOCKED"
|
|
333
|
+
assert any(f["rule_id"] == "TG-DIFF-001" for f in res_bypass["violations"])
|
|
334
|
+
print(" [PASS] Diff guard correctly detects bypass patterns in diffs")
|
|
335
|
+
|
|
336
|
+
print("\n" + "=" * 80)
|
|
337
|
+
print("ALL 9 ADVANCED MEMORY & GOVERNANCE CHECKS PASSED (100% REGRESSION-FREE)")
|
|
338
|
+
print("=" * 80)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if __name__ == "__main__":
|
|
342
|
+
test_v1_1_0_advanced_memory()
|