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,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: TG-DB-004
|
|
3
|
+
title: Missing Tenant Query Isolation in Multi-Tenant Models
|
|
4
|
+
category: data-access-orm
|
|
5
|
+
severity: Critical
|
|
6
|
+
confidence: Confirmed
|
|
7
|
+
frameworks:
|
|
8
|
+
- django
|
|
9
|
+
- drf
|
|
10
|
+
- fastapi
|
|
11
|
+
- flask
|
|
12
|
+
- sqlalchemy
|
|
13
|
+
cwe: CWE-284
|
|
14
|
+
asvs_v4: V4.1.3
|
|
15
|
+
nist_ssdf: PW.5.1
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# TG-DB-004: Missing Tenant Query Isolation in Multi-Tenant Models
|
|
19
|
+
|
|
20
|
+
## ๐จ Problem Statement
|
|
21
|
+
Applications built on a multi-tenant architecture often fail to scope database lookups (`SELECT`, `UPDATE`, `DELETE`) by the current authenticated tenant. Allowing a direct primary key (ID) lookup without a composite tenant filter allows Insecure Direct Object Reference (IDOR) attacks, enabling users to access or modify data belonging to other tenants.
|
|
22
|
+
|
|
23
|
+
> **Tuning Guardrails (v0.5.6):**
|
|
24
|
+
> - **DO NOT flag** every primary-key lookup.
|
|
25
|
+
> - **ESCALATE** when tenant-owned data can be loaded or modified without a verified tenant scope.
|
|
26
|
+
> - **DETECT** established tenant-scoped managers, repositories, `get_queryset()`, dependency injection, or parent-resource ownership checks.
|
|
27
|
+
> - **MARK** complex multi-layer scope flows as `Needs Review` instead of `Confirmed` when source-only proof is inadequate.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ๐ฅ Adversarial Threat & Exploitation
|
|
32
|
+
An attacker from Tenant B supplies the record UUID of a confidential document belonging to Tenant A:
|
|
33
|
+
```http
|
|
34
|
+
GET /api/v1/invoices/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d HTTP/1.1
|
|
35
|
+
Host: saas.example.com
|
|
36
|
+
Authorization: Bearer <Tenant_B_JWT>
|
|
37
|
+
```
|
|
38
|
+
If the backend performs `session.get(Invoice, invoice_id)` or `Invoice.objects.get(id=invoice_id)` without scoping to `tenant_id=current_user.tenant_id`, the invoice belonging to Tenant A is returned to the attacker.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ๐ ๏ธ Framework-Native Remediations
|
|
43
|
+
|
|
44
|
+
### ๐ SQLAlchemy (Scoped Tenant Queries)
|
|
45
|
+
|
|
46
|
+
#### โ Unsafe Pattern
|
|
47
|
+
```python
|
|
48
|
+
# Unsafe: Global primary key lookup without tenant filter
|
|
49
|
+
@app.get("/invoices/{invoice_id}")
|
|
50
|
+
def get_invoice(invoice_id: str, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
|
|
51
|
+
invoice = db.query(Invoice).filter(Invoice.id == invoice_id).first()
|
|
52
|
+
if not invoice:
|
|
53
|
+
raise HTTPException(status_code=404, detail="Invoice not found")
|
|
54
|
+
return invoice
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### โ
Safe Remediation
|
|
58
|
+
```python
|
|
59
|
+
# Safe: Enforce tenant scoping on every query filter
|
|
60
|
+
@app.get("/invoices/{invoice_id}")
|
|
61
|
+
def get_invoice(invoice_id: str, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
|
|
62
|
+
invoice = (
|
|
63
|
+
db.query(Invoice)
|
|
64
|
+
.filter(Invoice.id == invoice_id, Invoice.tenant_id == user.tenant_id)
|
|
65
|
+
.first()
|
|
66
|
+
)
|
|
67
|
+
if not invoice:
|
|
68
|
+
raise HTTPException(status_code=404, detail="Invoice not found")
|
|
69
|
+
return invoice
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### ๐ Django ORM (Scoped Manager / ViewSet Queryset)
|
|
75
|
+
|
|
76
|
+
#### โ Unsafe Pattern
|
|
77
|
+
```python
|
|
78
|
+
# Unsafe: Global queryset in DRF ViewSet
|
|
79
|
+
class InvoiceViewSet(viewsets.ModelViewSet):
|
|
80
|
+
queryset = Invoice.objects.all() # Exposes all tenant records
|
|
81
|
+
serializer_class = InvoiceSerializer
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### โ
Safe Remediation
|
|
85
|
+
```python
|
|
86
|
+
# Safe: Override get_queryset to enforce tenant boundary on every request
|
|
87
|
+
class InvoiceViewSet(viewsets.ModelViewSet):
|
|
88
|
+
serializer_class = InvoiceSerializer
|
|
89
|
+
permission_classes = [permissions.IsAuthenticated]
|
|
90
|
+
|
|
91
|
+
def get_queryset(self):
|
|
92
|
+
# Strictly scope lookups to authenticated user's organization
|
|
93
|
+
return Invoice.objects.filter(tenant=self.request.user.tenant)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## ๐งช Verification & Reproduction
|
|
99
|
+
1. Send request with valid Tenant B token requesting Tenant A resource ID:
|
|
100
|
+
```bash
|
|
101
|
+
curl -H "Authorization: Bearer <Tenant_B_Token>" http://localhost:8000/invoices/<Tenant_A_UUID>
|
|
102
|
+
```
|
|
103
|
+
2. **Assertion:** Request must return `404 Not Found`, denying access to cross-tenant data.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# TG-INPUT-001: Missing Server-Side Validation
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- REST and GraphQL API handlers
|
|
8
|
+
- Form-processing backend endpoints
|
|
9
|
+
- Message queue consumers that accept external payloads
|
|
10
|
+
- Admin/import endpoints that trust UI validation
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
Client-side checks are easy to bypass because attackers can send crafted requests directly to backend endpoints.
|
|
14
|
+
When the server does not validate required fields, data type, length, format, range, and allowed values, malicious input can trigger account takeover paths, integrity failures, and downstream injection vulnerabilities.
|
|
15
|
+
Server-side validation is also required to keep business rules enforceable when multiple clients, scripts, and integrations call the same APIs.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- Request data read from `req.body`, `req.query`, `req.params`, headers, or raw payloads without schema validation.
|
|
19
|
+
- Validation done only in frontend files while backend endpoint accepts the same fields blindly.
|
|
20
|
+
- Direct persistence of user-provided objects with broad spread operators.
|
|
21
|
+
- Type coercion and fallback logic that silently accepts invalid values.
|
|
22
|
+
- Missing allowlists for enum-like fields such as role, status, country code, or operation type.
|
|
23
|
+
|
|
24
|
+
## Unsafe Example
|
|
25
|
+
```js
|
|
26
|
+
// Express route that trusts client validation
|
|
27
|
+
app.post("/api/profile", async (req, res) => {
|
|
28
|
+
const { displayName, age, newsletter, role } = req.body;
|
|
29
|
+
|
|
30
|
+
// No server-side validation:
|
|
31
|
+
// - displayName could be extremely long or contain control chars
|
|
32
|
+
// - age could be negative or non-numeric
|
|
33
|
+
// - newsletter could be arbitrary string
|
|
34
|
+
// - role could be escalated to "admin"
|
|
35
|
+
const updated = await db.user.update({
|
|
36
|
+
where: { id: req.user.id },
|
|
37
|
+
data: { displayName, age, newsletter, role }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
res.json({ ok: true, user: updated });
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Safe Example
|
|
45
|
+
```js
|
|
46
|
+
import { z } from "zod";
|
|
47
|
+
|
|
48
|
+
const updateProfileSchema = z.object({
|
|
49
|
+
displayName: z.string().min(2).max(60).regex(/^[a-zA-Z0-9 _.-]+$/),
|
|
50
|
+
age: z.number().int().min(13).max(120),
|
|
51
|
+
newsletter: z.boolean(),
|
|
52
|
+
role: z.enum(["user"]) // client cannot self-assign privileged roles
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
app.post("/api/profile", async (req, res) => {
|
|
56
|
+
const parsed = updateProfileSchema.safeParse(req.body);
|
|
57
|
+
if (!parsed.success) {
|
|
58
|
+
return res.status(400).json({
|
|
59
|
+
ok: false,
|
|
60
|
+
error: "Invalid input",
|
|
61
|
+
details: parsed.error.issues
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { displayName, age, newsletter } = parsed.data;
|
|
66
|
+
const updated = await db.user.update({
|
|
67
|
+
where: { id: req.user.id },
|
|
68
|
+
data: { displayName, age, newsletter }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
res.json({ ok: true, user: updated });
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Remediation
|
|
76
|
+
1. Define strict server-side schemas for every externally reachable input surface.
|
|
77
|
+
2. Validate before business logic, authorization decisions, and database writes.
|
|
78
|
+
3. Enforce field allowlists; reject unexpected keys rather than silently ignoring them.
|
|
79
|
+
4. Apply normalization after validation (trim, case-fold, canonical forms) where needed.
|
|
80
|
+
5. Centralize reusable validators for shared payloads to prevent drift across endpoints.
|
|
81
|
+
6. Return clear but non-sensitive validation errors, and log rejection metrics for monitoring.
|
|
82
|
+
|
|
83
|
+
## Verification
|
|
84
|
+
- Send malformed payloads directly with `curl` or API clients, bypassing the web UI.
|
|
85
|
+
- Confirm invalid types, out-of-range values, and unknown fields return `400`.
|
|
86
|
+
- Attempt privilege-related field tampering and verify values are rejected or ignored safely.
|
|
87
|
+
- Run integration tests covering boundary values and negative cases.
|
|
88
|
+
- Ensure validation runs for all entry paths, including batch/import and background worker routes.
|
|
89
|
+
|
|
90
|
+
## False Positives and Exceptions
|
|
91
|
+
- Internal service-to-service endpoints may appear unvalidated but can be acceptable if strong contract validation is enforced at the gateway and mutual trust boundaries are documented.
|
|
92
|
+
- Auto-generated API frameworks sometimes perform implicit validation; keep explicit schema definitions for auditability.
|
|
93
|
+
- Legacy endpoints under migration can be temporarily exempted only with compensating controls and a tracked deprecation timeline.
|
|
94
|
+
|
|
95
|
+
## Related Rules
|
|
96
|
+
- [TG-INPUT-002](./TG-INPUT-002-raw-sql-concatenation.md)
|
|
97
|
+
- [TG-INPUT-003](./TG-INPUT-003-unsafe-html-or-code-execution.md)
|
|
98
|
+
- [TG-AUTH-003](./TG-AUTH-003-missing-object-authorization.md)
|
|
@@ -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.
|