torusguard 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.torusguard/.manifest.json +112 -8
- package/.torusguard/rules/README.md +93 -62
- package/.torusguard/rules/TG-AUTH-001-weak-password-storage.md +103 -0
- package/.torusguard/rules/TG-AUTH-002-client-only-authorization.md +85 -0
- package/.torusguard/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/.torusguard/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/.torusguard/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/.torusguard/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/.torusguard/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/.torusguard/rules/TG-DB-001-frontend-database-query.md +83 -0
- package/.torusguard/rules/TG-DB-002-privileged-database-credential.md +86 -0
- package/.torusguard/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/.torusguard/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/.torusguard/rules/TG-INPUT-001-missing-server-validation.md +98 -0
- package/.torusguard/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/.torusguard/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/.torusguard/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/.torusguard/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/.torusguard/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/.torusguard/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/.torusguard/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/.torusguard/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/.torusguard/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/.torusguard/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/.torusguard/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/.torusguard/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/.torusguard/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/.torusguard/rules/TG-SEC-002-public-environment-secrets.md +83 -0
- package/.torusguard/rules/TG-SEC-003-tracked-env-file.md +79 -0
- package/.torusguard/rules/TG-SEC-004-sensitive-logging.md +83 -0
- package/.torusguard/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
- package/.torusguard/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
- package/.torusguard/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/.torusguard/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/.torusguard/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/.torusguard/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/.torusguard/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/.torusguard/rules/active/TG-DB-001-frontend-database-query.md +83 -0
- package/.torusguard/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
- package/.torusguard/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/.torusguard/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/.torusguard/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
- package/.torusguard/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/.torusguard/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/.torusguard/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/.torusguard/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/.torusguard/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/.torusguard/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/.torusguard/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/.torusguard/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/.torusguard/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/.torusguard/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/.torusguard/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/.torusguard/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/.torusguard/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/.torusguard/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
- package/.torusguard/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
- package/.torusguard/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
- package/.torusguard/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
- package/.torusguard/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
- package/.torusguard/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
- package/.torusguard/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
- package/.torusguard/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
- package/.torusguard/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
- package/.torusguard/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
- package/.torusguard/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
- package/.torusguard/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
- package/.torusguard/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
- package/.torusguard/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
- package/.torusguard/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
- package/.torusguard/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
- package/.torusguard/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
- package/.torusguard/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
- package/.torusguard/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
- package/.torusguard/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
- package/.torusguard/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
- package/.torusguard/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
- package/.torusguard/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
- package/.torusguard/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
- package/.torusguard/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
- package/.torusguard/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
- package/.torusguard/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
- package/.torusguard/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
- package/.torusguard/runs/report-latest.html +62 -20
- package/.torusguard/runs/run-20260908-112648-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-112648-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-112648-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-112648-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-112722-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-112722-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-112722-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-112722-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-113004-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-113004-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-113004-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260908-113004-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-113127-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260908-113127-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260908-113127-audit/manifest.json +13 -0
- package/.torusguard/runs/run-20260908-113127-audit/remediation.md +262 -0
- package/.torusguard/runs/run-20260908-113127-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260908-115240-audit/apply_plan.md +6 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
- package/.torusguard/runs/run-20260908-115240-audit/diff_summary.md +221 -0
- package/.torusguard/runs/run-20260908-115240-audit/findings.json +1553 -0
- package/.torusguard/runs/run-20260908-115240-audit/findings.md +572 -0
- package/.torusguard/runs/run-20260908-115240-audit/manifest.json +14 -0
- package/.torusguard/runs/run-20260908-115240-audit/recheck.md +91 -0
- package/.torusguard/runs/run-20260908-115240-audit/remediation.md +278 -0
- package/.torusguard/runs/run-20260908-115240-audit/summary.md +5 -0
- package/.torusguard/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/run_manager.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/stack_detect.cpython-311.pyc +0 -0
- package/.torusguard/scripts/apply_runner.py +332 -0
- package/.torusguard/scripts/audit_runner.py +550 -0
- package/.torusguard/scripts/diff_guard.py +52 -4
- package/.torusguard/scripts/finding_scorer.py +58 -0
- package/.torusguard/scripts/harden_runner.py +353 -0
- package/.torusguard/scripts/html_reporter.py +25 -10
- package/.torusguard/scripts/manifest_builder.py +2 -0
- package/.torusguard/scripts/memory_engine.py +4 -0
- package/.torusguard/scripts/recheck_runner.py +221 -0
- package/.torusguard/scripts/recipes_runner.py +130 -0
- package/.torusguard/scripts/run_manager.py +17 -5
- package/.torusguard/skills/torusguard/SKILL.md +42 -42
- package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/client/src/App.jsx.bak +63 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/server/index.js.bak +82 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/engine/fixture_manager.py.bak +245 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/master_validation.py.bak +698 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/runner.py.bak +551 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_e2e.py.bak +498 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_qa_v0_6_0.py.bak +572 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_1_scale.py.bak +371 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_2_modern_stacks.py.bak +462 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_3_hardening.py.bak +444 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_0_0_memory.py.bak +302 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_1_0_advanced_memory.py.bak +342 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/skills/torusguard/payload/scripts/harden_runner.py.bak +353 -0
- package/.torusguard/snapshots/run-20260908-115240-audit/tests/test_v0_6_0_governed_remediation.py.bak +353 -0
- package/README.md +70 -33
- package/bin/torusguard.js +89 -19
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/bootstrap.py +147 -68
- package/skills/torusguard/payload/.manifest.json +113 -9
- package/skills/torusguard/payload/rules/README.md +93 -62
- package/skills/torusguard/payload/rules/TG-AUTH-001-weak-password-storage.md +103 -0
- package/skills/torusguard/payload/rules/TG-AUTH-002-client-only-authorization.md +85 -0
- package/skills/torusguard/payload/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/skills/torusguard/payload/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/skills/torusguard/payload/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/skills/torusguard/payload/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/skills/torusguard/payload/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/skills/torusguard/payload/rules/TG-DB-001-frontend-database-query.md +83 -0
- package/skills/torusguard/payload/rules/TG-DB-002-privileged-database-credential.md +86 -0
- package/skills/torusguard/payload/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/skills/torusguard/payload/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/skills/torusguard/payload/rules/TG-INPUT-001-missing-server-validation.md +98 -0
- package/skills/torusguard/payload/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/skills/torusguard/payload/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/skills/torusguard/payload/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/skills/torusguard/payload/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/skills/torusguard/payload/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/skills/torusguard/payload/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/skills/torusguard/payload/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/skills/torusguard/payload/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/skills/torusguard/payload/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/skills/torusguard/payload/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/skills/torusguard/payload/rules/TG-SEC-002-public-environment-secrets.md +83 -0
- package/skills/torusguard/payload/rules/TG-SEC-003-tracked-env-file.md +79 -0
- package/skills/torusguard/payload/rules/TG-SEC-004-sensitive-logging.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
- package/skills/torusguard/payload/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
- package/skills/torusguard/payload/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-DB-001-frontend-database-query.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
- package/skills/torusguard/payload/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
- package/skills/torusguard/payload/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
- package/skills/torusguard/payload/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
- package/skills/torusguard/payload/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
- package/skills/torusguard/payload/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
- package/skills/torusguard/payload/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
- package/skills/torusguard/payload/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
- package/skills/torusguard/payload/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
- package/skills/torusguard/payload/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
- package/skills/torusguard/payload/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
- package/skills/torusguard/payload/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
- package/skills/torusguard/payload/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
- package/skills/torusguard/payload/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
- package/skills/torusguard/payload/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
- package/skills/torusguard/payload/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
- package/skills/torusguard/payload/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
- package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
- package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
- package/skills/torusguard/payload/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
- package/skills/torusguard/payload/scripts/apply_runner.py +332 -0
- package/skills/torusguard/payload/scripts/audit_runner.py +550 -0
- package/skills/torusguard/payload/scripts/diff_guard.py +52 -4
- package/skills/torusguard/payload/scripts/finding_scorer.py +58 -0
- package/skills/torusguard/payload/scripts/harden_runner.py +353 -0
- package/skills/torusguard/payload/scripts/html_reporter.py +25 -10
- package/skills/torusguard/payload/scripts/manifest_builder.py +2 -0
- package/skills/torusguard/payload/scripts/memory_engine.py +4 -0
- package/skills/torusguard/payload/scripts/recheck_runner.py +221 -0
- package/skills/torusguard/payload/scripts/recipes_runner.py +130 -0
- package/skills/torusguard/payload/scripts/run_manager.py +17 -5
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +42 -42
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +147 -68
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# TG-DB-002: Privileged Database Credential in Browser Context
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
Critical
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Browser-executed code in frontend apps and static assets
|
|
8
|
+
- Public environment variables consumed by client bundles
|
|
9
|
+
- Mobile web, desktop web, and embedded webview clients
|
|
10
|
+
- Shared modules imported into client-side runtime
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
Privileged database credentials in browser-accessible code grant attackers direct or indirect control over sensitive data stores.
|
|
14
|
+
Service-role keys and admin database URLs typically bypass row-level access controls and can expose full datasets.
|
|
15
|
+
Because frontend artifacts are publicly retrievable, embedded privileged credentials should be treated as compromised.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- Service-role keys and admin tokens referenced in frontend code
|
|
19
|
+
- Database URLs containing usernames/passwords in browser-executed modules
|
|
20
|
+
- Public env variables with names such as:
|
|
21
|
+
- `NEXT_PUBLIC_DATABASE_URL`
|
|
22
|
+
- `VITE_SUPABASE_SERVICE_ROLE_KEY`
|
|
23
|
+
- `REACT_APP_ADMIN_DB_TOKEN`
|
|
24
|
+
- Client initialization using elevated credentials intended for server trust zones
|
|
25
|
+
- Fallback credential constants used when env vars are missing
|
|
26
|
+
|
|
27
|
+
## Unsafe Example
|
|
28
|
+
```ts
|
|
29
|
+
// src/lib/dbClient.ts
|
|
30
|
+
import { createClient } from "@supabase/supabase-js";
|
|
31
|
+
|
|
32
|
+
const url = process.env.NEXT_PUBLIC_DATABASE_URL!;
|
|
33
|
+
const serviceRole = process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!;
|
|
34
|
+
|
|
35
|
+
export const db = createClient(url, serviceRole, {
|
|
36
|
+
auth: { persistSession: false },
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Safe Example
|
|
41
|
+
```ts
|
|
42
|
+
// src/lib/publicClient.ts
|
|
43
|
+
import { createClient } from "@supabase/supabase-js";
|
|
44
|
+
|
|
45
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
46
|
+
const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
47
|
+
|
|
48
|
+
export const publicDb = createClient(url, anonKey);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
// server/lib/adminDb.ts
|
|
53
|
+
import { createClient } from "@supabase/supabase-js";
|
|
54
|
+
|
|
55
|
+
export const adminDb = createClient(
|
|
56
|
+
process.env.SUPABASE_URL!,
|
|
57
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Remediation (numbered)
|
|
62
|
+
1. Remove privileged DB credentials from all frontend and public env sources.
|
|
63
|
+
2. Rotate exposed service-role keys, passwords, and privileged tokens immediately.
|
|
64
|
+
3. Migrate privileged operations to backend-only services and APIs.
|
|
65
|
+
4. Use least-privilege client credentials (anonymous/public keys) in browser code.
|
|
66
|
+
5. Audit access logs for misuse during exposure window.
|
|
67
|
+
6. Enforce CI policy blocking privileged credential names in public scopes.
|
|
68
|
+
7. Add architecture documentation defining credential trust boundaries.
|
|
69
|
+
|
|
70
|
+
## Verification
|
|
71
|
+
- Inspect client bundles and source maps for privileged credential artifacts.
|
|
72
|
+
- Confirm frontend uses only non-privileged credentials and constrained APIs.
|
|
73
|
+
- Validate server-only secrets are loaded from private runtime environments.
|
|
74
|
+
- Test rotated privileged credentials and verify old keys are rejected.
|
|
75
|
+
- Review monitoring for anomalous database access during and after remediation.
|
|
76
|
+
|
|
77
|
+
## False Positives and Exceptions
|
|
78
|
+
- Public anonymous keys may look secret-like but are intentionally non-privileged.
|
|
79
|
+
- Documentation examples can trigger detections; verify they are non-operational.
|
|
80
|
+
- Local-only sandbox credentials are still risky if bundled into distributed builds.
|
|
81
|
+
- Exceptions require security approval, owner assignment, and expiration date.
|
|
82
|
+
|
|
83
|
+
## Related Rules
|
|
84
|
+
- [TG-SEC-001](./TG-SEC-001-hardcoded-secrets.md) - Hardcoded secrets in tracked source
|
|
85
|
+
- [TG-SEC-002](./TG-SEC-002-public-environment-secrets.md) - Secrets in public env namespaces
|
|
86
|
+
- [TG-DB-003](./TG-DB-003-frontend-admin-sdk.md) - Admin SDK usage in frontend runtime
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# TG-DB-003: Frontend Use of Admin or Server-Only Database SDKs
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
Critical
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Frontend directories such as `src/`, `app/`, `pages/`, `components/`, `client/`, `public/`
|
|
8
|
+
- Browser bundles in React, Next.js client components, Vite, and CRA
|
|
9
|
+
- Shared package imports consumed by client entry points
|
|
10
|
+
- Build systems that can accidentally include server-only dependencies
|
|
11
|
+
|
|
12
|
+
## Why It Matters
|
|
13
|
+
Admin SDKs and server drivers are designed for trusted environments and often assume unrestricted credentials and network access.
|
|
14
|
+
Using packages like Firebase Admin, Prisma, or `pg` in frontend paths risks exposing privileged behavior, credentials, and internal data models.
|
|
15
|
+
Even failed browser execution can leak sensitive implementation details and weaken architectural boundaries.
|
|
16
|
+
|
|
17
|
+
## What TorusGuard Looks For
|
|
18
|
+
- Imports of server-only SDKs in frontend-targeted files:
|
|
19
|
+
- `firebase-admin`
|
|
20
|
+
- `@prisma/client`
|
|
21
|
+
- `pg`
|
|
22
|
+
- Initialization logic for admin clients in UI components or client utilities
|
|
23
|
+
- Conditional imports attempting to bypass bundler checks in browser code
|
|
24
|
+
- Code patterns that create server DB connections in frontend modules
|
|
25
|
+
- Shared files that instantiate privileged clients and are imported by frontend
|
|
26
|
+
|
|
27
|
+
## Unsafe Example
|
|
28
|
+
```ts
|
|
29
|
+
// src/app/dashboard/adminMetrics.tsx
|
|
30
|
+
import { PrismaClient } from "@prisma/client";
|
|
31
|
+
import { Client } from "pg";
|
|
32
|
+
|
|
33
|
+
const prisma = new PrismaClient();
|
|
34
|
+
const pgClient = new Client({
|
|
35
|
+
connectionString: process.env.NEXT_PUBLIC_DATABASE_URL,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export async function loadMetrics() {
|
|
39
|
+
await pgClient.connect();
|
|
40
|
+
const result = await prisma.user.count();
|
|
41
|
+
return { users: result };
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Safe Example
|
|
46
|
+
```ts
|
|
47
|
+
// src/app/dashboard/adminMetrics.tsx
|
|
48
|
+
export async function loadMetrics() {
|
|
49
|
+
const res = await fetch("/api/admin/metrics", { method: "GET" });
|
|
50
|
+
return res.json();
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// server/routes/adminMetrics.ts
|
|
56
|
+
import { PrismaClient } from "@prisma/client";
|
|
57
|
+
|
|
58
|
+
const prisma = new PrismaClient();
|
|
59
|
+
|
|
60
|
+
app.get("/api/admin/metrics", async (_req, res) => {
|
|
61
|
+
const users = await prisma.user.count();
|
|
62
|
+
res.json({ users });
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Remediation (numbered)
|
|
67
|
+
1. Remove server-only SDK imports from frontend and browser-targeted modules.
|
|
68
|
+
2. Relocate privileged data access to backend routes, jobs, or server actions.
|
|
69
|
+
3. Ensure frontend calls authenticated backend endpoints for required data.
|
|
70
|
+
4. Restrict server-only dependencies to backend package boundaries.
|
|
71
|
+
5. Configure bundler/lint rules to block admin SDK imports in frontend paths.
|
|
72
|
+
6. Rotate credentials if admin SDK usage exposed privileged config values.
|
|
73
|
+
7. Add architecture tests validating strict client/server dependency separation.
|
|
74
|
+
|
|
75
|
+
## Verification
|
|
76
|
+
- Scan frontend directories for imports of `firebase-admin`, Prisma, and `pg`.
|
|
77
|
+
- Confirm build output excludes server-only database packages in client chunks.
|
|
78
|
+
- Validate admin operations execute only on server infrastructure.
|
|
79
|
+
- Run access-control tests against backend endpoints exposing admin data.
|
|
80
|
+
- Review dependency graphs to ensure no frontend transitive path to admin SDKs.
|
|
81
|
+
|
|
82
|
+
## False Positives and Exceptions
|
|
83
|
+
- Type-only imports can appear suspicious; verify they are erased at build time.
|
|
84
|
+
- Documentation snippets in markdown are non-runtime and may be acceptable.
|
|
85
|
+
- Server components must be verified carefully to avoid accidental client inclusion.
|
|
86
|
+
- Exceptions require documented risk acceptance and periodic security review.
|
|
87
|
+
|
|
88
|
+
## Related Rules
|
|
89
|
+
- [TG-DB-001](./TG-DB-001-frontend-database-query.md) - Frontend SQL/query logic anti-pattern
|
|
90
|
+
- [TG-DB-002](./TG-DB-002-privileged-database-credential.md) - Privileged credential exposure in browser
|
|
91
|
+
- [TG-SEC-001](./TG-SEC-001-hardcoded-secrets.md) - Hardcoded sensitive literals in source
|
|
@@ -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)
|