torusguard 1.3.0 → 1.3.1
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 +112 -8
- 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 +332 -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 +353 -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/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 +89 -19
- 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 +332 -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 +353 -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,550 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Autonomous Static Security Audit Engine (v1.3.0)
|
|
4
|
+
Multi-language static pattern & AST security scanner.
|
|
5
|
+
Evaluates source trees against canonical TorusGuard rule families,
|
|
6
|
+
augments confidence via the persistent security memory subsystem,
|
|
7
|
+
groups findings into root-cause clusters, and emits isolated run folders.
|
|
8
|
+
|
|
9
|
+
Pure Python 3.10+ standard library (zero external dependencies).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import json
|
|
16
|
+
import hashlib
|
|
17
|
+
import argparse
|
|
18
|
+
import datetime
|
|
19
|
+
import unicodedata
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Dict, List, Any, Optional, Tuple, TypedDict, Pattern
|
|
22
|
+
|
|
23
|
+
# Windows console UTF-8 support
|
|
24
|
+
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
|
|
25
|
+
try:
|
|
26
|
+
getattr(sys.stdout, "reconfigure")(encoding="utf-8", errors="replace")
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
29
|
+
if sys.stderr and hasattr(sys.stderr, "reconfigure"):
|
|
30
|
+
try:
|
|
31
|
+
getattr(sys.stderr, "reconfigure")(encoding="utf-8", errors="replace")
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
# ─── Timezone Configuration: Indian Standard Time (IST, UTC+05:30) ───────────
|
|
36
|
+
IST = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
|
|
37
|
+
|
|
38
|
+
def get_ist_now() -> datetime.datetime:
|
|
39
|
+
"""Return timezone-aware datetime strictly in Indian Standard Time (IST)."""
|
|
40
|
+
return datetime.datetime.now(IST)
|
|
41
|
+
|
|
42
|
+
# ─── ANSI Colors & Formatter ───────────────────────────────────────────────────
|
|
43
|
+
BOLD = "\033[1m"
|
|
44
|
+
DIM = "\033[2m"
|
|
45
|
+
RESET = "\033[0m"
|
|
46
|
+
CYAN = "\033[36m"
|
|
47
|
+
GREEN = "\033[32m"
|
|
48
|
+
YELLOW = "\033[33m"
|
|
49
|
+
WHITE = "\033[97m"
|
|
50
|
+
GRAY = "\033[90m"
|
|
51
|
+
RED = "\033[31m"
|
|
52
|
+
MAGENTA = "\033[35m"
|
|
53
|
+
|
|
54
|
+
ANSI_REGEX = re.compile(r'\033\[[0-9;]*m')
|
|
55
|
+
|
|
56
|
+
def get_visual_width(text: str) -> int:
|
|
57
|
+
"""Calculate the printable display width of a string (ANSI and emoji aware)."""
|
|
58
|
+
clean = ANSI_REGEX.sub('', text)
|
|
59
|
+
width = 0
|
|
60
|
+
for ch in clean:
|
|
61
|
+
ea = unicodedata.east_asian_width(ch)
|
|
62
|
+
if ea in ('W', 'F'):
|
|
63
|
+
width += 2
|
|
64
|
+
elif ord(ch) >= 0x1F300:
|
|
65
|
+
width += 2
|
|
66
|
+
else:
|
|
67
|
+
width += 1
|
|
68
|
+
return width
|
|
69
|
+
|
|
70
|
+
def format_box_line(content: str, width: int = 71, border: str = "│", border_color: str = CYAN) -> str:
|
|
71
|
+
"""Pad content so the right border is consistently placed at visual column 75."""
|
|
72
|
+
vis = get_visual_width(content)
|
|
73
|
+
pad = " " * max(0, width - vis)
|
|
74
|
+
return f" {border_color}{border}{RESET} {content}{pad}{border_color}{border}{RESET}"
|
|
75
|
+
|
|
76
|
+
# ─── Rule Definitions & Sinks ─────────────────────────────────────────────────
|
|
77
|
+
class RulePattern(TypedDict):
|
|
78
|
+
rule_id: str
|
|
79
|
+
title: str
|
|
80
|
+
severity: str
|
|
81
|
+
category: str
|
|
82
|
+
cluster: str
|
|
83
|
+
patterns: List[Tuple[Pattern[str], str]]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
RULE_PATTERNS: List[RulePattern] = [
|
|
87
|
+
# ── Secrets & Credentials (TG-SEC) ──────────────────────────────────────────
|
|
88
|
+
{
|
|
89
|
+
"rule_id": "TG-SEC-001",
|
|
90
|
+
"title": "Hardcoded Secret or API Key in Tracked Source",
|
|
91
|
+
"severity": "Critical",
|
|
92
|
+
"category": "secrets",
|
|
93
|
+
"cluster": "cluster-credentials-exposure",
|
|
94
|
+
"patterns": [
|
|
95
|
+
(re.compile(r'\b(?:sk_live|ak_live)_[0-9a-zA-Z]{20,}\b'), "Live payment or cloud API secret key"),
|
|
96
|
+
(re.compile(r'\bgh[pousr]_[0-9a-zA-Z]{36}\b'), "GitHub personal access token"),
|
|
97
|
+
(re.compile(r'-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----'), "Hardcoded private key block"),
|
|
98
|
+
(re.compile(r'\b(?:jwt_secret|jwtSecret|JWT_SECRET)\s*[:=]\s*["\'][a-zA-Z0-9_\-]{16,}["\']'), "Hardcoded JWT signing secret"),
|
|
99
|
+
(re.compile(r'(?:password|passwd|api_key|apiKey|secret_key)\s*[:=]\s*["\'][A-Za-z0-9@#$%^&+=_\-]{10,}["\']', re.IGNORECASE), "Hardcoded credential or API secret string")
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"rule_id": "TG-SEC-002",
|
|
104
|
+
"title": "Public Client Environment Variable Secret Exposure",
|
|
105
|
+
"severity": "High",
|
|
106
|
+
"category": "secrets",
|
|
107
|
+
"cluster": "cluster-credentials-exposure",
|
|
108
|
+
"patterns": [
|
|
109
|
+
(re.compile(r'\b(?:NEXT_PUBLIC_|VITE_|REACT_APP_)[A-Z0-9_]*(?:SECRET|PRIVATE|KEY|PASSWORD|TOKEN)\b'), "Client-exposed environment variable containing secret naming pattern")
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
# ── Database Safety & Multi-Tenant Query Isolation (TG-DB) ────────────────
|
|
113
|
+
{
|
|
114
|
+
"rule_id": "TG-DB-004",
|
|
115
|
+
"title": "Missing Multi-Tenant Isolation in Scoped Query",
|
|
116
|
+
"severity": "High",
|
|
117
|
+
"category": "database",
|
|
118
|
+
"cluster": "cluster-tenant-isolation",
|
|
119
|
+
"patterns": [
|
|
120
|
+
(re.compile(r'\.objects\.get\s*\(\s*id\s*=\s*[^,\)]+\)'), "Django ORM .get(id=...) query lacking explicit tenant scope filter"),
|
|
121
|
+
(re.compile(r'\.findById\s*\(\s*(?:req\.params|id)[^\)]*\)'), "Direct findById query lacking organization/tenant boundaries")
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"rule_id": "TG-DB-001",
|
|
126
|
+
"title": "Frontend / Client-Side Direct Database Query",
|
|
127
|
+
"severity": "Critical",
|
|
128
|
+
"category": "database",
|
|
129
|
+
"cluster": "cluster-client-leak",
|
|
130
|
+
"patterns": [
|
|
131
|
+
(re.compile(r'\b(?:createClient|supabase)\.from\([^)]+\)\.delete\(\)'), "Dangerous direct database mutation in client component"),
|
|
132
|
+
(re.compile(r'\bimport\s+.*(?:pg|mysql2|prisma|typeorm).*\s+from'), "Server database driver imported in client file")
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
# ── Input Validation & Code / SQL Injection (TG-INPUT) ─────────────────────
|
|
136
|
+
{
|
|
137
|
+
"rule_id": "TG-INPUT-002",
|
|
138
|
+
"title": "Unescaped Raw SQL Query Concatenation",
|
|
139
|
+
"severity": "Critical",
|
|
140
|
+
"category": "injection",
|
|
141
|
+
"cluster": "cluster-injection",
|
|
142
|
+
"patterns": [
|
|
143
|
+
(re.compile(r'(?:query|execute|raw)\s*\(\s*f["\'].*SELECT.*\{', re.IGNORECASE), "Python f-string interpolation in raw SQL query"),
|
|
144
|
+
(re.compile(r'(?:query|execute|raw)\s*\(\s*[`"\'].*SELECT.*\$\{', re.IGNORECASE), "JavaScript template literal interpolation in SQL query"),
|
|
145
|
+
(re.compile(r'(?:query|execute|raw)\s*\(\s*["\'].*SELECT.*\+\s*(?:req|input|params)', re.IGNORECASE), "String concatenation in raw SQL query")
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"rule_id": "TG-INPUT-003",
|
|
150
|
+
"title": "Unsafe Dynamic Code or HTML Execution",
|
|
151
|
+
"severity": "High",
|
|
152
|
+
"category": "injection",
|
|
153
|
+
"cluster": "cluster-injection",
|
|
154
|
+
"patterns": [
|
|
155
|
+
(re.compile(r'\beval\s*\('), "Direct eval() execution on untrusted input"),
|
|
156
|
+
(re.compile(r'\bexec\s*\('), "Direct exec() execution on untrusted input"),
|
|
157
|
+
(re.compile(r'\bdangerouslySetInnerHTML\s*='), "React dangerouslySetInnerHTML rendering raw unescaped HTML"),
|
|
158
|
+
(re.compile(r'\.innerHTML\s*='), "Direct DOM innerHTML assignment vulnerable to XSS")
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"rule_id": "TG-INPUT-006",
|
|
163
|
+
"title": "Unvalidated User Input in File Path Resolution",
|
|
164
|
+
"severity": "High",
|
|
165
|
+
"category": "filesystem",
|
|
166
|
+
"cluster": "cluster-injection",
|
|
167
|
+
"patterns": [
|
|
168
|
+
(re.compile(r'(?:open|readFile|readFileSync)\s*\([^)]*\+\s*(?:req|params|query)'), "File read operation using unsanitized user request path")
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
# ── Authentication & Session Safety (TG-AUTH) ──────────────────────────────
|
|
172
|
+
{
|
|
173
|
+
"rule_id": "TG-AUTH-004",
|
|
174
|
+
"title": "Insecure Session Cookie or Disabled CSRF Protection",
|
|
175
|
+
"severity": "High",
|
|
176
|
+
"category": "auth",
|
|
177
|
+
"cluster": "cluster-auth-bypass",
|
|
178
|
+
"patterns": [
|
|
179
|
+
(re.compile(r'\bSESSION_COOKIE_SECURE\s*=\s*False\b'), "Session cookie secure flag disabled in Django settings"),
|
|
180
|
+
(re.compile(r'\bsecure\s*:\s*false\b', re.IGNORECASE), "Cookie secure attribute set to false"),
|
|
181
|
+
(re.compile(r'\bhttpOnly\s*:\s*false\b', re.IGNORECASE), "Cookie httpOnly attribute set to false (XSS readable)"),
|
|
182
|
+
(re.compile(r'@(?:csrf_exempt|allow_anonymous)\b'), "Decorator disabling route-level authentication or CSRF protection"),
|
|
183
|
+
(re.compile(r'\[(?:AllowAnonymous)\]'), "Attribute disabling ASP.NET authentication"),
|
|
184
|
+
(re.compile(r'\.csrf\(\)\.disable\(\)'), "Spring Security CSRF protection explicitly disabled")
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
# ── Security Bypasses & Transport Invariants (TG-DIFF / TG-PLATFORM) ───────
|
|
188
|
+
{
|
|
189
|
+
"rule_id": "TG-DIFF-001",
|
|
190
|
+
"title": "Disabled TLS / SSL Certificate Validation",
|
|
191
|
+
"severity": "Critical",
|
|
192
|
+
"category": "transport",
|
|
193
|
+
"cluster": "cluster-transport-security",
|
|
194
|
+
"patterns": [
|
|
195
|
+
(re.compile(r'\bverify\s*=\s*False\b'), "Disabled TLS certificate verification (verify=False)"),
|
|
196
|
+
(re.compile(r'\bInsecureSkipVerify\s*:\s*true\b'), "Disabled Go TLS certificate verification (InsecureSkipVerify: true)"),
|
|
197
|
+
(re.compile(r'\bNODE_TLS_REJECT_UNAUTHORIZED\s*=\s*[\'"]?0[\'"]?'), "Disabled Node.js TLS verification (NODE_TLS_REJECT_UNAUTHORIZED=0)"),
|
|
198
|
+
(re.compile(r'\brejectUnauthorized\s*:\s*false\b'), "Disabled Node.js TLS rejectUnauthorized"),
|
|
199
|
+
(re.compile(r'\bCURLOPT_SSL_VERIFYPEER\s*(?:=>|,)\s*(?:false|0)\b', re.IGNORECASE), "Disabled PHP curl SSL verification")
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"rule_id": "TG-PLATFORM-001",
|
|
204
|
+
"title": "Wildcard Permissive CORS Configuration with Credentials",
|
|
205
|
+
"severity": "High",
|
|
206
|
+
"category": "platform",
|
|
207
|
+
"cluster": "cluster-auth-bypass",
|
|
208
|
+
"patterns": [
|
|
209
|
+
(re.compile(r'\bCORS_ALLOW_ALL_ORIGINS\s*=\s*True\b'), "Wildcard CORS origin enabled across entire application"),
|
|
210
|
+
(re.compile(r'@CrossOrigin\s*\(\s*["\']\*["\']\s*\)'), "Wildcard CORS origin annotation in Java/Spring")
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
# File extensions to scan
|
|
216
|
+
SOURCE_EXTENSIONS = {
|
|
217
|
+
".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
218
|
+
".go", ".java", ".cs", ".php", ".rb", ".json", ".yaml", ".yml"
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# Directories to skip
|
|
222
|
+
SKIP_DIRS = {
|
|
223
|
+
".git", "node_modules", ".venv", "venv", "env", "__pycache__",
|
|
224
|
+
"dist", "build", ".next", ".nuxt", "coverage", ".torusguard",
|
|
225
|
+
".idea", ".vscode", "target", "bin", "obj"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
def is_test_file(path_str: str) -> bool:
|
|
229
|
+
"""Check if file belongs to tests, fixtures, or mocks."""
|
|
230
|
+
clean = path_str.replace("\\", "/").lower()
|
|
231
|
+
markers = ["/test/", "/tests/", "/spec/", "/specs/", "/fixtures/", "/mock/", "/mocks/",
|
|
232
|
+
"__tests__", "test_", "_test.", ".test.", ".spec."]
|
|
233
|
+
return any(m in clean for m in markers)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def find_files_to_scan(target_root: Path) -> List[Path]:
|
|
237
|
+
"""Recursively collect source files eligible for static security audit."""
|
|
238
|
+
files = []
|
|
239
|
+
for root, dirs, filenames in os.walk(target_root):
|
|
240
|
+
# Mutate dirs in-place to skip ignored folders
|
|
241
|
+
dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith(".")]
|
|
242
|
+
for f in filenames:
|
|
243
|
+
ext = os.path.splitext(f)[1].lower()
|
|
244
|
+
if ext in SOURCE_EXTENSIONS:
|
|
245
|
+
files.append(Path(root) / f)
|
|
246
|
+
return sorted(files)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
250
|
+
"""Scan a single source file against the TorusGuard rule families."""
|
|
251
|
+
findings = []
|
|
252
|
+
try:
|
|
253
|
+
content = file_path.read_text(encoding="utf-8", errors="replace")
|
|
254
|
+
except Exception:
|
|
255
|
+
return findings
|
|
256
|
+
|
|
257
|
+
lines = content.splitlines()
|
|
258
|
+
rel_path = str(file_path.relative_to(target_root)).replace("\\", "/")
|
|
259
|
+
is_test = is_test_file(rel_path)
|
|
260
|
+
|
|
261
|
+
for rule in RULE_PATTERNS:
|
|
262
|
+
rule_id = rule["rule_id"]
|
|
263
|
+
patterns: List[Tuple[Pattern[str], str]] = rule["patterns"]
|
|
264
|
+
for regex, desc in patterns:
|
|
265
|
+
for idx, line in enumerate(lines):
|
|
266
|
+
stripped = line.strip()
|
|
267
|
+
if stripped.startswith(("#", "//", "/*", "*")):
|
|
268
|
+
if "bypass" not in stripped.lower() and "todo" not in stripped.lower():
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
match = regex.search(line)
|
|
272
|
+
if match:
|
|
273
|
+
line_num = idx + 1
|
|
274
|
+
seed = f"{rule_id}:{rel_path}:{line_num}:{stripped}"
|
|
275
|
+
fp = f"{rule_id}-{hashlib.sha256(seed.encode('utf-8')).hexdigest()[:8]}"
|
|
276
|
+
|
|
277
|
+
start_ctx = max(0, idx - 2)
|
|
278
|
+
end_ctx = min(len(lines), idx + 3)
|
|
279
|
+
snippet = "\n".join(lines[start_ctx:end_ctx])
|
|
280
|
+
|
|
281
|
+
findings.append({
|
|
282
|
+
"finding_id": fp,
|
|
283
|
+
"rule_id": rule_id,
|
|
284
|
+
"title": rule["title"],
|
|
285
|
+
"description": desc,
|
|
286
|
+
"severity": "Low" if is_test else rule["severity"],
|
|
287
|
+
"category": rule["category"],
|
|
288
|
+
"cluster": rule["cluster"],
|
|
289
|
+
"file_path": rel_path,
|
|
290
|
+
"line_number": line_num,
|
|
291
|
+
"matched_text": match.group(0)[:80],
|
|
292
|
+
"snippet": snippet,
|
|
293
|
+
"is_test": is_test,
|
|
294
|
+
"target": {
|
|
295
|
+
"file_path": rel_path,
|
|
296
|
+
"start_line": line_num,
|
|
297
|
+
"end_line": line_num
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
break
|
|
301
|
+
return findings
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def score_and_cluster_findings(findings: List[Dict[str, Any]], target_root: Path) -> Tuple[List[Dict[str, Any]], Dict[str, List[Dict[str, Any]]]]:
|
|
305
|
+
"""
|
|
306
|
+
Score each finding using finding_scorer.py and persistent memory patterns.
|
|
307
|
+
Returns (scored_findings, clusters_map).
|
|
308
|
+
"""
|
|
309
|
+
scripts_dir = Path(__file__).resolve().parent
|
|
310
|
+
if str(scripts_dir) not in sys.path:
|
|
311
|
+
sys.path.insert(0, str(scripts_dir))
|
|
312
|
+
try:
|
|
313
|
+
import finding_scorer
|
|
314
|
+
except Exception:
|
|
315
|
+
finding_scorer = None
|
|
316
|
+
|
|
317
|
+
scored = []
|
|
318
|
+
clusters: Dict[str, List[Dict[str, Any]]] = {}
|
|
319
|
+
|
|
320
|
+
for f in findings:
|
|
321
|
+
rule_id = f["rule_id"]
|
|
322
|
+
file_path = f["file_path"]
|
|
323
|
+
|
|
324
|
+
if finding_scorer and hasattr(finding_scorer, "compute_confidence_score"):
|
|
325
|
+
total, band, factors = finding_scorer.compute_confidence_score(
|
|
326
|
+
evidence_quality=35,
|
|
327
|
+
reproduction_success=0,
|
|
328
|
+
independent_confirmations=5,
|
|
329
|
+
environmental_clarity=15,
|
|
330
|
+
manual_review_status=0,
|
|
331
|
+
rule_id=rule_id,
|
|
332
|
+
file_path=file_path,
|
|
333
|
+
root_dir=target_root
|
|
334
|
+
)
|
|
335
|
+
else:
|
|
336
|
+
total = 70
|
|
337
|
+
band = "High Confidence"
|
|
338
|
+
factors = {"total_score": total, "classification_band": band}
|
|
339
|
+
|
|
340
|
+
f["confidence_score"] = total
|
|
341
|
+
f["confidence_band"] = band
|
|
342
|
+
f["confidence_factors"] = factors
|
|
343
|
+
|
|
344
|
+
scored.append(f)
|
|
345
|
+
cluster_id = f.get("cluster", "cluster-general")
|
|
346
|
+
clusters.setdefault(cluster_id, []).append(f)
|
|
347
|
+
|
|
348
|
+
return scored, clusters
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def emit_run_artifacts(run_folder: Path, scored_findings: List[Dict[str, Any]], clusters: Dict[str, List[Dict[str, Any]]], target_root: Path) -> None:
|
|
352
|
+
"""Write findings.json, findings.md, and summary.md into isolated run directory."""
|
|
353
|
+
run_folder.mkdir(parents=True, exist_ok=True)
|
|
354
|
+
|
|
355
|
+
# 1. findings.json
|
|
356
|
+
findings_file = run_folder / "findings.json"
|
|
357
|
+
with open(findings_file, "w", encoding="utf-8") as f:
|
|
358
|
+
json.dump(scored_findings, f, indent=2)
|
|
359
|
+
|
|
360
|
+
# 2. findings.md
|
|
361
|
+
now_ist = get_ist_now()
|
|
362
|
+
md_lines = [
|
|
363
|
+
f"# TorusGuard Static Security Audit Report",
|
|
364
|
+
f"**Run ID:** `{run_folder.name}` ",
|
|
365
|
+
f"**Timestamp:** `{now_ist.strftime('%Y-%m-%d %H:%M:%S IST')}` ",
|
|
366
|
+
f"**Target:** `{target_root}` ",
|
|
367
|
+
f"**Total Findings:** `{len(scored_findings)}` \n",
|
|
368
|
+
"---",
|
|
369
|
+
"## Findings Catalog\n"
|
|
370
|
+
]
|
|
371
|
+
|
|
372
|
+
for item in scored_findings:
|
|
373
|
+
md_lines.append(f"### [{item['rule_id']}] {item['title']}")
|
|
374
|
+
md_lines.append(f"- **Severity:** `{item['severity']}` | **Confidence:** `{item['confidence_score']}/100 ({item['confidence_band']})`")
|
|
375
|
+
md_lines.append(f"- **File:** `{item['file_path']}:{item['line_number']}` | **Cluster:** `{item['cluster']}`")
|
|
376
|
+
md_lines.append(f"- **Description:** {item['description']}")
|
|
377
|
+
md_lines.append("```")
|
|
378
|
+
code_snippet = str(item.get("snippet") or item.get("matched_text") or "")
|
|
379
|
+
md_lines.append(code_snippet)
|
|
380
|
+
md_lines.append("```\n")
|
|
381
|
+
|
|
382
|
+
(run_folder / "findings.md").write_text("\n".join(md_lines), encoding="utf-8")
|
|
383
|
+
|
|
384
|
+
# 3. summary.md
|
|
385
|
+
summary_lines = [
|
|
386
|
+
f"# TorusGuard Audit Summary: {run_folder.name}",
|
|
387
|
+
f"- Scanned Scope: {target_root}",
|
|
388
|
+
f"- Total Findings: {len(scored_findings)}",
|
|
389
|
+
f"- Root-Cause Clusters: {len(clusters)}",
|
|
390
|
+
f"- Generated: {now_ist.strftime('%Y-%m-%d %H:%M:%S IST')}"
|
|
391
|
+
]
|
|
392
|
+
(run_folder / "summary.md").write_text("\n".join(summary_lines), encoding="utf-8")
|
|
393
|
+
|
|
394
|
+
# 4. Update manifest.json status
|
|
395
|
+
manifest_file = run_folder / "manifest.json"
|
|
396
|
+
if manifest_file.exists():
|
|
397
|
+
try:
|
|
398
|
+
with open(manifest_file, "r", encoding="utf-8") as mf:
|
|
399
|
+
mdata = json.load(mf)
|
|
400
|
+
mdata["status"] = "completed"
|
|
401
|
+
mdata["findings_count"] = len(scored_findings)
|
|
402
|
+
with open(manifest_file, "w", encoding="utf-8") as mf:
|
|
403
|
+
json.dump(mdata, mf, indent=2)
|
|
404
|
+
except Exception:
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def print_audit_dashboard(target_root: Path, file_count: int, scored_findings: List[Dict[str, Any]], clusters: Dict[str, List[Dict[str, Any]]], run_folder: Path, detected_stack: Optional[Dict[str, Any]] = None) -> None:
|
|
409
|
+
"""Print the unified 75-column TorusGuard Static Audit results card."""
|
|
410
|
+
crit_count = sum(1 for f in scored_findings if f["severity"] == "Critical")
|
|
411
|
+
high_count = sum(1 for f in scored_findings if f["severity"] == "High")
|
|
412
|
+
med_count = sum(1 for f in scored_findings if f["severity"] in ("Medium", "Low"))
|
|
413
|
+
|
|
414
|
+
stack_str = "Auto-detected"
|
|
415
|
+
if detected_stack and detected_stack.get("framework") and detected_stack.get("framework") != "None":
|
|
416
|
+
stack_str = f"{detected_stack.get('framework')} ({detected_stack.get('language')})"
|
|
417
|
+
|
|
418
|
+
print(f"""
|
|
419
|
+
{CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}
|
|
420
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
421
|
+
{CYAN}│{RESET} {BOLD}{WHITE}🛡️ TORUSGUARD STATIC SECURITY AUDIT{RESET} {GRAY}v1.3.0{RESET} {CYAN}│{RESET}
|
|
422
|
+
{CYAN}│{RESET} {DIM}Autonomous AST & Invariant Security Scanner{RESET} {CYAN}│{RESET}
|
|
423
|
+
{CYAN}│{RESET} {CYAN}│{RESET}
|
|
424
|
+
{CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}
|
|
425
|
+
""")
|
|
426
|
+
|
|
427
|
+
print(f" {CYAN}┌─ {BOLD}Audit Execution Scope{RESET} {CYAN}─────────────────────────────────────────────────┐{RESET}")
|
|
428
|
+
print(format_box_line(f"{BOLD}Target:{RESET} {WHITE}{str(target_root):<56}{RESET}"))
|
|
429
|
+
print(format_box_line(f"{BOLD}Stack:{RESET} {GREEN}{stack_str:<56}{RESET}"))
|
|
430
|
+
print(format_box_line(f"{BOLD}Files:{RESET} {WHITE}{file_count} files evaluated across 11 canonical families{RESET}"))
|
|
431
|
+
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
432
|
+
|
|
433
|
+
# Findings Box
|
|
434
|
+
status_icon = f"{RED}✖ CRITICAL FINDINGS DETECTED{RESET}" if crit_count > 0 else (f"{YELLOW}⚠ HIGH VULNERABILITIES DETECTED{RESET}" if high_count > 0 else f"{GREEN}✔ SECURITY POSTURE CLEAN{RESET}")
|
|
435
|
+
print(f" {CYAN}┌─ {BOLD}Findings & Confidence Scores{RESET} {CYAN}─────────────────────────────────────────┐{RESET}")
|
|
436
|
+
print(format_box_line(f"Status: {status_icon}"))
|
|
437
|
+
print(format_box_line(f"Findings: {RED}{crit_count} Critical{RESET} {YELLOW}{high_count} High{RESET} {CYAN}{med_count} Medium/Low{RESET} ({len(scored_findings)} total)"))
|
|
438
|
+
print(format_box_line(f"Clusters: {WHITE}{len(clusters)} architectural root causes identified{RESET}"))
|
|
439
|
+
print(f" {CYAN}├─────────────────────────────────────────────────────────────────────────┤{RESET}")
|
|
440
|
+
|
|
441
|
+
if not scored_findings:
|
|
442
|
+
print(format_box_line(f"{GREEN}✔ Zero security invariant violations detected.{RESET}"))
|
|
443
|
+
else:
|
|
444
|
+
for idx, f in enumerate(scored_findings[:5]):
|
|
445
|
+
sev_color = RED if f["severity"] == "Critical" else (YELLOW if f["severity"] == "High" else CYAN)
|
|
446
|
+
line_str = f"[{sev_color}{f['severity']}{RESET}] {BOLD}{f['rule_id']}{RESET} at {f['file_path']}:{f['line_number']}"
|
|
447
|
+
print(format_box_line(line_str))
|
|
448
|
+
desc_str = f" └─ {DIM}{f['description'][:60]}{RESET} ({CYAN}{f['confidence_score']}/100{RESET})"
|
|
449
|
+
print(format_box_line(desc_str))
|
|
450
|
+
if len(scored_findings) > 5:
|
|
451
|
+
print(format_box_line(f"{DIM}... and {len(scored_findings) - 5} more findings in report file{RESET}"))
|
|
452
|
+
|
|
453
|
+
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
454
|
+
|
|
455
|
+
# Run Artifacts & Next Steps Box
|
|
456
|
+
rel_run = str(run_folder.relative_to(target_root)) if str(run_folder).startswith(str(target_root)) else str(run_folder)
|
|
457
|
+
print(f" {GREEN}╔═ {BOLD}Artifacts & Next Actions{RESET} {GREEN}══════════════════════════════════════════════════╗{RESET}")
|
|
458
|
+
print(format_box_line(f"{BOLD}Run Artifact:{RESET} {WHITE}{rel_run}/findings.md{RESET}", border="║", border_color=GREEN))
|
|
459
|
+
print(format_box_line(f"{BOLD}Visual Dash:{RESET} {CYAN}npx torusguard report --html{RESET}", border="║", border_color=GREEN))
|
|
460
|
+
print(format_box_line(f"{BOLD}Surgical Fix:{RESET} In AI Chat, run {CYAN}/torusguard-harden{RESET}", border="║", border_color=GREEN))
|
|
461
|
+
print(f" {GREEN}╚═════════════════════════════════════════════════════════════════════════╝{RESET}\n")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def execute_audit(target_root: Path, severity_floor: str = "medium", json_output: bool = False) -> Dict[str, Any]:
|
|
465
|
+
"""Execute the full TorusGuard static security audit."""
|
|
466
|
+
target_root = target_root.resolve()
|
|
467
|
+
scripts_dir = Path(__file__).resolve().parent
|
|
468
|
+
|
|
469
|
+
# 1. Stack detection
|
|
470
|
+
detected_stack = None
|
|
471
|
+
stack_script = scripts_dir / "stack_detect.py"
|
|
472
|
+
if stack_script.is_file():
|
|
473
|
+
try:
|
|
474
|
+
import importlib.util
|
|
475
|
+
spec = importlib.util.spec_from_file_location("stack_detect", str(stack_script))
|
|
476
|
+
if spec and spec.loader:
|
|
477
|
+
sd = importlib.util.module_from_spec(spec)
|
|
478
|
+
spec.loader.exec_module(sd)
|
|
479
|
+
if hasattr(sd, "detect_stack"):
|
|
480
|
+
detected_stack = sd.detect_stack(target_root)
|
|
481
|
+
except Exception:
|
|
482
|
+
pass
|
|
483
|
+
|
|
484
|
+
# 2. Collect files & scan
|
|
485
|
+
files = find_files_to_scan(target_root)
|
|
486
|
+
all_findings = []
|
|
487
|
+
for f in files:
|
|
488
|
+
all_findings.extend(scan_file(f, target_root))
|
|
489
|
+
|
|
490
|
+
# 3. Score & Cluster
|
|
491
|
+
scored_findings, clusters = score_and_cluster_findings(all_findings, target_root)
|
|
492
|
+
|
|
493
|
+
# 4. Allocate run folder
|
|
494
|
+
runs_dir = target_root / ".torusguard" / "runs"
|
|
495
|
+
runs_dir.mkdir(parents=True, exist_ok=True)
|
|
496
|
+
|
|
497
|
+
if str(scripts_dir) not in sys.path:
|
|
498
|
+
sys.path.insert(0, str(scripts_dir))
|
|
499
|
+
try:
|
|
500
|
+
import run_manager
|
|
501
|
+
run_folder = run_manager.create_run(runs_dir, "audit", target_root.name)
|
|
502
|
+
except Exception:
|
|
503
|
+
timestamp = get_ist_now().strftime("%Y%m%d-%H%M%S")
|
|
504
|
+
run_folder = runs_dir / f"run-{timestamp}-audit"
|
|
505
|
+
run_folder.mkdir(parents=True, exist_ok=True)
|
|
506
|
+
|
|
507
|
+
# 5. Emit artifacts & sync memory
|
|
508
|
+
emit_run_artifacts(run_folder, scored_findings, clusters, target_root)
|
|
509
|
+
try:
|
|
510
|
+
if "run_manager" in sys.modules:
|
|
511
|
+
sys.modules["run_manager"].sync_run_to_memory(run_folder, scored_findings)
|
|
512
|
+
except Exception:
|
|
513
|
+
pass
|
|
514
|
+
|
|
515
|
+
result = {
|
|
516
|
+
"status": "completed",
|
|
517
|
+
"target_root": str(target_root),
|
|
518
|
+
"files_scanned": len(files),
|
|
519
|
+
"total_findings": len(scored_findings),
|
|
520
|
+
"critical_count": sum(1 for f in scored_findings if f["severity"] == "Critical"),
|
|
521
|
+
"high_count": sum(1 for f in scored_findings if f["severity"] == "High"),
|
|
522
|
+
"medium_count": sum(1 for f in scored_findings if f["severity"] in ("Medium", "Low")),
|
|
523
|
+
"clusters_count": len(clusters),
|
|
524
|
+
"run_folder": str(run_folder),
|
|
525
|
+
"findings": scored_findings
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if json_output:
|
|
529
|
+
print(json.dumps(result, indent=2))
|
|
530
|
+
else:
|
|
531
|
+
print_audit_dashboard(target_root, len(files), scored_findings, clusters, run_folder, detected_stack)
|
|
532
|
+
|
|
533
|
+
return result
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def main():
|
|
537
|
+
parser = argparse.ArgumentParser(description="TorusGuard Static Security Audit Engine")
|
|
538
|
+
parser.add_argument("path", nargs="?", default=".", help="Target project root directory to audit")
|
|
539
|
+
parser.add_argument("--scope", "-s", help="Alternative path to target project")
|
|
540
|
+
parser.add_argument("--severity", choices=["critical", "high", "medium", "low"], default="medium", help="Severity floor")
|
|
541
|
+
parser.add_argument("--json", action="store_true", help="Output raw JSON")
|
|
542
|
+
args = parser.parse_args()
|
|
543
|
+
|
|
544
|
+
target = Path(args.scope or args.path).resolve()
|
|
545
|
+
res = execute_audit(target, severity_floor=args.severity, json_output=args.json)
|
|
546
|
+
sys.exit(0 if res["critical_count"] == 0 else 1)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
if __name__ == "__main__":
|
|
550
|
+
main()
|
|
@@ -117,6 +117,50 @@ def check_memory_regressions(
|
|
|
117
117
|
return violations
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
def is_exempt_diff_path(file_path: Optional[str]) -> bool:
|
|
121
|
+
"""
|
|
122
|
+
Detects whether a file path is exempt from diff guard checks.
|
|
123
|
+
Documentation, rule manifests, AI editor prompts, test fixtures, and scanner
|
|
124
|
+
definitions are exempt to prevent false alarms on security instructions and mock data.
|
|
125
|
+
"""
|
|
126
|
+
if not file_path:
|
|
127
|
+
return False
|
|
128
|
+
clean = file_path.replace("\\", "/").lower().strip()
|
|
129
|
+
if clean in ("unknown", "", "none"):
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
# Documentation, changelogs, markdown, plain text
|
|
133
|
+
if clean.endswith((".md", ".txt", ".rst", ".jsonl")):
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
# AI editor prompt and rule instruction files
|
|
137
|
+
if clean.endswith((".cursorrules", ".windsurfrules")) or clean in ("claude.md", "agents.md"):
|
|
138
|
+
return True
|
|
139
|
+
if ".agent/rules" in clean or ".cursor/rules" in clean:
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
# Security rule catalog, schemas, and cryptographic manifests
|
|
143
|
+
if "/rules/" in clean or clean.startswith("rules/") or "/schemas/" in clean or clean.startswith("schemas/"):
|
|
144
|
+
return True
|
|
145
|
+
if clean.endswith((".schema.json", ".manifest.json")):
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
# Test fixtures, test suites, mocks, and test harness
|
|
149
|
+
test_markers = [
|
|
150
|
+
"/test/", "/tests/", "/spec/", "/specs/", "/fixtures/", "/mock/", "/mocks/",
|
|
151
|
+
"/e2e/", "__tests__", "test_", "_test.", ".test.", ".spec.", "testcase",
|
|
152
|
+
"harness/"
|
|
153
|
+
]
|
|
154
|
+
if any(m in clean for m in test_markers) or clean.endswith(("_test.go", "test.java", "test.py", ".spec.ts", ".test.ts", ".spec.js", ".test.js")):
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
# TorusGuard security engine scripts that define scanner regexes / templates
|
|
158
|
+
if ("/scripts/" in clean or clean.startswith("scripts/")) and any(s in clean for s in ("diff_guard", "harden_runner", "finding_scorer", "rules_sync", "memory_engine", "audit_runner", "apply_runner", "recheck_runner", "manifest_builder")):
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
120
164
|
def audit_diff(
|
|
121
165
|
diff_content: str,
|
|
122
166
|
check_memory: bool = False,
|
|
@@ -133,7 +177,7 @@ def audit_diff(
|
|
|
133
177
|
current_line = 0
|
|
134
178
|
|
|
135
179
|
additions: List[tuple[int, str]] = []
|
|
136
|
-
deletions: List[tuple[int, str]] = []
|
|
180
|
+
deletions: List[tuple[str, int, str]] = []
|
|
137
181
|
|
|
138
182
|
for line_idx, line in enumerate(lines, 1):
|
|
139
183
|
if line.startswith('+++ b/'):
|
|
@@ -153,6 +197,9 @@ def audit_diff(
|
|
|
153
197
|
added_content = line[1:]
|
|
154
198
|
additions.append((current_line, added_content))
|
|
155
199
|
|
|
200
|
+
if is_exempt_diff_path(current_file):
|
|
201
|
+
continue
|
|
202
|
+
|
|
156
203
|
# Check bypass patterns
|
|
157
204
|
for pat, desc in BYPASS_PATTERNS:
|
|
158
205
|
if pat.search(added_content):
|
|
@@ -186,10 +233,11 @@ def audit_diff(
|
|
|
186
233
|
|
|
187
234
|
elif line.startswith('-') and not line.startswith('---'):
|
|
188
235
|
deleted_content = line[1:]
|
|
189
|
-
|
|
236
|
+
if not is_exempt_diff_path(current_file):
|
|
237
|
+
deletions.append((current_file, line_idx, deleted_content))
|
|
190
238
|
|
|
191
239
|
# Check for tenant filter deletion without replacement
|
|
192
|
-
for del_idx, del_line in deletions:
|
|
240
|
+
for del_file, del_idx, del_line in deletions:
|
|
193
241
|
for t_pat in TENANT_FILTER_PATTERNS:
|
|
194
242
|
if t_pat.search(del_line):
|
|
195
243
|
# Verify if an equivalent tenant filter exists in additions
|
|
@@ -199,7 +247,7 @@ def audit_diff(
|
|
|
199
247
|
"rule_id": "TG-DIFF-003",
|
|
200
248
|
"category": "Tenant Filter Removal",
|
|
201
249
|
"severity": "HIGH",
|
|
202
|
-
"file":
|
|
250
|
+
"file": del_file,
|
|
203
251
|
"line": del_idx,
|
|
204
252
|
"content": del_line.strip(),
|
|
205
253
|
"description": "Tenant isolation filter was removed without being restored in additions"
|