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
package/skills/torusguard/payload/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# TG-WS-001: Unauthenticated WebSocket Handshake
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High. Accepting WebSocket upgrade handshakes without validating authentication cookies or Bearer tokens allows unauthenticated clients to establish persistent sockets and consume server resources.
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- `ws`, `Socket.io`, `fastapi.WebSocket`, `channels` (Django), `aiohttp`
|
|
8
|
+
|
|
9
|
+
## Why It Matters
|
|
10
|
+
HTTP WebSocket upgrades (`Upgrade: websocket`) begin as standard HTTP requests. If authentication is omitted during the initial HTTP upgrade:
|
|
11
|
+
1. Attackers can flood the server with unbounded persistent open TCP connections, exhausting socket file descriptors.
|
|
12
|
+
2. If authorization is deferred to post-connect messages, unauthenticated clients can linger in memory indefinitely.
|
|
13
|
+
3. Cross-site WebSocket hijacking (CSWSH) can occur if the `Origin` header is trusted blindly.
|
|
14
|
+
|
|
15
|
+
## What TorusGuard Looks For
|
|
16
|
+
1. WebSocket servers accepting the upgrade connection before validating session cookies, headers, or query tokens.
|
|
17
|
+
2. Missing `Origin` header validation during handshake.
|
|
18
|
+
3. Runtime probe: Attempting a WebSocket handshake without an `Authorization` header or session cookie; flagging if the connection status returns `101 Switching Protocols` instead of `401 Unauthorized`.
|
|
19
|
+
|
|
20
|
+
## Unsafe Example
|
|
21
|
+
```typescript
|
|
22
|
+
// UNSAFE: Accepting WebSocket upgrade without verifying session
|
|
23
|
+
import { WebSocketServer } from "ws";
|
|
24
|
+
|
|
25
|
+
const wss = new WebSocketServer({ port: 8080 });
|
|
26
|
+
|
|
27
|
+
wss.on("connection", (ws, req) => {
|
|
28
|
+
// Handshake already succeeded without verifying authentication!
|
|
29
|
+
ws.send("Welcome to real-time events");
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Safe Example
|
|
34
|
+
```typescript
|
|
35
|
+
// SAFE: Validating authentication and origin during the HTTP upgrade
|
|
36
|
+
import http from "http";
|
|
37
|
+
import { WebSocketServer } from "ws";
|
|
38
|
+
import { verifyJwtToken } from "./auth";
|
|
39
|
+
|
|
40
|
+
const server = http.createServer();
|
|
41
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
42
|
+
|
|
43
|
+
server.on("upgrade", (req, socket, head) => {
|
|
44
|
+
// 1. Verify Origin header
|
|
45
|
+
const origin = req.headers.origin;
|
|
46
|
+
if (origin !== "https://app.example.com") {
|
|
47
|
+
socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
48
|
+
socket.destroy();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 2. Authenticate token before completing WebSocket handshake
|
|
53
|
+
const token = req.headers["sec-websocket-protocol"] || req.headers.cookie;
|
|
54
|
+
const user = verifyJwtToken(token);
|
|
55
|
+
if (!user) {
|
|
56
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
57
|
+
socket.destroy();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Complete upgrade only when authenticated
|
|
62
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
63
|
+
wss.emit("connection", ws, req, user);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Bounded Runtime Probe Protocol
|
|
69
|
+
When probing WebSocket endpoints under `/torusguard web-validate`:
|
|
70
|
+
1. Dispatch an HTTP GET request with `Upgrade: websocket` and `Connection: Upgrade` headers **without authentication credentials**.
|
|
71
|
+
2. Assert that the server returns `HTTP 401` or `HTTP 403` and terminates the socket.
|
|
72
|
+
3. If the server returns `HTTP 101 Switching Protocols`, log a high-confidence finding for unauthenticated handshake.
|
|
73
|
+
|
|
74
|
+
## Remediation
|
|
75
|
+
1. **Authenticate on HTTP Upgrade:** Reject unauthenticated connections with standard HTTP 401/403 status codes before calling `handleUpgrade`.
|
|
76
|
+
2. **Enforce Strict Origin Checking:** Validate that `req.headers.origin` matches your trusted application domain.
|
|
77
|
+
3. **Set Connection Timeouts:** Automatically disconnect sockets that fail message-level handshakes within 10 seconds.
|
|
78
|
+
|
|
79
|
+
## Related Rules
|
|
80
|
+
- `TG-WS-002`: Missing Channel-Level Authorization
|
|
81
|
+
- `TG-AUTH-004`: Insecure Session Cookie
|
|
82
|
+
- `TG-RATE-003`: Unbounded Resource Consumption
|
package/skills/torusguard/payload/rules/websocket/TG-WS-002-missing-channel-level-authorization.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TG-WS-002: Missing Channel-Level Authorization
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High by default. Raise to Critical when applicable.
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Relevant endpoints and services
|
|
8
|
+
|
|
9
|
+
## Why It Matters
|
|
10
|
+
Explaining the risk of this vulnerability.
|
|
11
|
+
|
|
12
|
+
## What TorusGuard Looks For
|
|
13
|
+
- Specific code patterns or configurations
|
|
14
|
+
|
|
15
|
+
## Unsafe Example
|
|
16
|
+
```javascript
|
|
17
|
+
// Unsafe code example
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Safe Example
|
|
21
|
+
```javascript
|
|
22
|
+
// Safe code example
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Remediation
|
|
26
|
+
1. Step one
|
|
27
|
+
2. Step two
|
|
28
|
+
|
|
29
|
+
## Verification
|
|
30
|
+
- Test case 1
|
|
31
|
+
- Test case 2
|
|
32
|
+
|
|
33
|
+
## False Positives and Exceptions
|
|
34
|
+
An exception requires documented review.
|
|
35
|
+
|
|
36
|
+
## Related Rules
|
|
37
|
+
- TG-OTHER-001
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TG-WS-003: Unvalidated WebSocket Message
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High by default. Raise to Critical when applicable.
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Relevant endpoints and services
|
|
8
|
+
|
|
9
|
+
## Why It Matters
|
|
10
|
+
Explaining the risk of this vulnerability.
|
|
11
|
+
|
|
12
|
+
## What TorusGuard Looks For
|
|
13
|
+
- Specific code patterns or configurations
|
|
14
|
+
|
|
15
|
+
## Unsafe Example
|
|
16
|
+
```javascript
|
|
17
|
+
// Unsafe code example
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Safe Example
|
|
21
|
+
```javascript
|
|
22
|
+
// Safe code example
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Remediation
|
|
26
|
+
1. Step one
|
|
27
|
+
2. Step two
|
|
28
|
+
|
|
29
|
+
## Verification
|
|
30
|
+
- Test case 1
|
|
31
|
+
- Test case 2
|
|
32
|
+
|
|
33
|
+
## False Positives and Exceptions
|
|
34
|
+
An exception requires documented review.
|
|
35
|
+
|
|
36
|
+
## Related Rules
|
|
37
|
+
- TG-OTHER-001
|
package/skills/torusguard/payload/rules/websocket/TG-WS-004-unbounded-websocket-connections.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TG-WS-004: Unbounded WebSocket Connections
|
|
2
|
+
|
|
3
|
+
## Severity
|
|
4
|
+
High by default. Raise to Critical when applicable.
|
|
5
|
+
|
|
6
|
+
## Applies To
|
|
7
|
+
- Relevant endpoints and services
|
|
8
|
+
|
|
9
|
+
## Why It Matters
|
|
10
|
+
Explaining the risk of this vulnerability.
|
|
11
|
+
|
|
12
|
+
## What TorusGuard Looks For
|
|
13
|
+
- Specific code patterns or configurations
|
|
14
|
+
|
|
15
|
+
## Unsafe Example
|
|
16
|
+
```javascript
|
|
17
|
+
// Unsafe code example
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Safe Example
|
|
21
|
+
```javascript
|
|
22
|
+
// Safe code example
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Remediation
|
|
26
|
+
1. Step one
|
|
27
|
+
2. Step two
|
|
28
|
+
|
|
29
|
+
## Verification
|
|
30
|
+
- Test case 1
|
|
31
|
+
- Test case 2
|
|
32
|
+
|
|
33
|
+
## False Positives and Exceptions
|
|
34
|
+
An exception requires documented review.
|
|
35
|
+
|
|
36
|
+
## Related Rules
|
|
37
|
+
- TG-OTHER-001
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Governed Patch Application & Snapshot Engine (v1.3.0)
|
|
4
|
+
Interactive Human Gate authorization, pre-apply .bak rollback snapshots,
|
|
5
|
+
atomic patch application, and Golden Fix Recipe distillation into persistent memory.
|
|
6
|
+
|
|
7
|
+
Pure Python 3.10+ standard library (zero external dependencies).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import json
|
|
14
|
+
import shutil
|
|
15
|
+
import hashlib
|
|
16
|
+
import argparse
|
|
17
|
+
import datetime
|
|
18
|
+
import unicodedata
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Dict, List, Any, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
# Windows console UTF-8 support
|
|
23
|
+
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
|
|
24
|
+
try:
|
|
25
|
+
getattr(sys.stdout, "reconfigure")(encoding="utf-8", errors="replace")
|
|
26
|
+
except Exception:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
# ─── Timezone Configuration: Indian Standard Time (IST, UTC+05:30) ───────────
|
|
30
|
+
IST = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
|
|
31
|
+
|
|
32
|
+
def get_ist_now() -> datetime.datetime:
|
|
33
|
+
return datetime.datetime.now(IST)
|
|
34
|
+
|
|
35
|
+
# ─── ANSI Colors & Formatter ───────────────────────────────────────────────────
|
|
36
|
+
BOLD = "\033[1m"
|
|
37
|
+
DIM = "\033[2m"
|
|
38
|
+
RESET = "\033[0m"
|
|
39
|
+
CYAN = "\033[36m"
|
|
40
|
+
GREEN = "\033[32m"
|
|
41
|
+
YELLOW = "\033[33m"
|
|
42
|
+
WHITE = "\033[97m"
|
|
43
|
+
GRAY = "\033[90m"
|
|
44
|
+
RED = "\033[31m"
|
|
45
|
+
|
|
46
|
+
ANSI_REGEX = re.compile(r'\033\[[0-9;]*m')
|
|
47
|
+
|
|
48
|
+
def get_visual_width(text: str) -> int:
|
|
49
|
+
clean = ANSI_REGEX.sub('', text)
|
|
50
|
+
width = 0
|
|
51
|
+
for ch in clean:
|
|
52
|
+
ea = unicodedata.east_asian_width(ch)
|
|
53
|
+
if ea in ('W', 'F'):
|
|
54
|
+
width += 2
|
|
55
|
+
elif ord(ch) >= 0x1F300:
|
|
56
|
+
width += 2
|
|
57
|
+
else:
|
|
58
|
+
width += 1
|
|
59
|
+
return width
|
|
60
|
+
|
|
61
|
+
def format_box_line(content: str, width: int = 71, border: str = "│", border_color: str = CYAN) -> str:
|
|
62
|
+
vis = get_visual_width(content)
|
|
63
|
+
pad = " " * max(0, width - vis)
|
|
64
|
+
return f" {border_color}{border}{RESET} {content}{pad}{border_color}{border}{RESET}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def find_latest_audit_run(runs_dir: Path) -> Optional[Path]:
|
|
68
|
+
if not runs_dir.is_dir():
|
|
69
|
+
return None
|
|
70
|
+
runs = sorted(runs_dir.glob("run-*-audit"), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
71
|
+
for r in runs:
|
|
72
|
+
if (r / "bundles").is_dir() and list((r / "bundles").iterdir()):
|
|
73
|
+
return r
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def execute_rollback(target_root: Path, run_id_arg: Optional[str] = None) -> None:
|
|
78
|
+
"""Restore all target files from the pre-apply backup snapshots."""
|
|
79
|
+
snapshots_base = target_root / ".torusguard" / "snapshots"
|
|
80
|
+
if not snapshots_base.is_dir():
|
|
81
|
+
print(f"\n {YELLOW}ℹ No snapshots directory found at: {snapshots_base}{RESET}\n")
|
|
82
|
+
sys.exit(0)
|
|
83
|
+
|
|
84
|
+
if run_id_arg:
|
|
85
|
+
snapshot_dir = snapshots_base / run_id_arg
|
|
86
|
+
else:
|
|
87
|
+
snapshots = sorted([d for d in snapshots_base.iterdir() if d.is_dir()], key=lambda p: p.stat().st_mtime, reverse=True)
|
|
88
|
+
snapshot_dir = snapshots[0] if snapshots else None
|
|
89
|
+
|
|
90
|
+
if not snapshot_dir or not snapshot_dir.is_dir():
|
|
91
|
+
print(f"\n {RED}✖ No rollback snapshot found for run: {run_id_arg or 'latest'}{RESET}\n")
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
restored_files = []
|
|
95
|
+
for bak_file in snapshot_dir.rglob("*.bak"):
|
|
96
|
+
rel_path = bak_file.relative_to(snapshot_dir)
|
|
97
|
+
orig_rel = str(rel_path)[:-4] # Strip .bak
|
|
98
|
+
dest_file = target_root / orig_rel
|
|
99
|
+
|
|
100
|
+
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
shutil.copy2(bak_file, dest_file)
|
|
102
|
+
restored_files.append(orig_rel)
|
|
103
|
+
|
|
104
|
+
print(f"\n {CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}")
|
|
105
|
+
print(format_box_line(f"{BOLD}🛡️ TORUSGUARD ROLLBACK RESTORATION v1.3.0{RESET}", border_color=CYAN))
|
|
106
|
+
print(format_box_line(f"{DIM}Restored original files from pre-apply snapshot{RESET}", border_color=CYAN))
|
|
107
|
+
print(f" {CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}\n")
|
|
108
|
+
|
|
109
|
+
print(f" {CYAN}┌─ Rollback Snapshot Restored ────────────────────────────────────────────┐{RESET}")
|
|
110
|
+
print(format_box_line(f"Snapshot Run: {WHITE}{snapshot_dir.name}{RESET}"))
|
|
111
|
+
print(format_box_line(f"Files Restored: {GREEN}{len(restored_files)} file(s) cleanly reverted{RESET}"))
|
|
112
|
+
for rf in restored_files:
|
|
113
|
+
print(format_box_line(f" ✔ {rf}"))
|
|
114
|
+
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
115
|
+
sys.exit(0)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_approve: bool = False) -> Dict[str, Any]:
|
|
119
|
+
runs_dir = target_root / ".torusguard" / "runs"
|
|
120
|
+
if run_id_arg:
|
|
121
|
+
run_folder = runs_dir / run_id_arg
|
|
122
|
+
else:
|
|
123
|
+
run_folder = find_latest_audit_run(runs_dir)
|
|
124
|
+
|
|
125
|
+
if not run_folder or not run_folder.is_dir():
|
|
126
|
+
print(f"\n {RED}✖ No candidate remediation bundles found to apply.{RESET}")
|
|
127
|
+
print(f" Run {CYAN}npx torusguard harden{RESET} first to formulate candidate patches.\n")
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
|
|
130
|
+
bundles_dir = run_folder / "bundles"
|
|
131
|
+
if not bundles_dir.is_dir():
|
|
132
|
+
print(f"\n {RED}✖ Missing bundles directory in run folder: {run_folder}{RESET}\n")
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
|
|
135
|
+
bundle_meta_files = list(bundles_dir.glob("*/metadata.json"))
|
|
136
|
+
if not bundle_meta_files:
|
|
137
|
+
print(f"\n {YELLOW}ℹ Zero candidate bundles available in {run_folder.name}.{RESET}\n")
|
|
138
|
+
sys.exit(0)
|
|
139
|
+
|
|
140
|
+
bundles = []
|
|
141
|
+
for mf in bundle_meta_files:
|
|
142
|
+
try:
|
|
143
|
+
bundles.append(json.loads(mf.read_text(encoding="utf-8")))
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
# Snapshot directory
|
|
148
|
+
snapshot_dir = target_root / ".torusguard" / "snapshots" / run_folder.name
|
|
149
|
+
snapshot_dir.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
|
|
151
|
+
# Header Card
|
|
152
|
+
print(f"\n {CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}")
|
|
153
|
+
print(f" {CYAN}│ │{RESET}")
|
|
154
|
+
print(format_box_line(f"{BOLD}🛡️ TORUSGUARD GOVERNED PATCH APPLIER v1.3.0{RESET}", border_color=CYAN))
|
|
155
|
+
print(format_box_line(f"{DIM}Human-Gate Authorization, Snapshots & Golden Recipe Distillation{RESET}", border_color=CYAN))
|
|
156
|
+
print(f" {CYAN}│ │{RESET}")
|
|
157
|
+
print(f" {CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}\n")
|
|
158
|
+
|
|
159
|
+
print(f" {CYAN}┌─ Candidate Bundles Ready for Application ───────────────────────────────┐{RESET}")
|
|
160
|
+
print(format_box_line(f"Active Run: {WHITE}{run_folder.name}{RESET}"))
|
|
161
|
+
print(format_box_line(f"Total Bundles: {GREEN}{len(bundles)} candidate surgical patches formulated{RESET}"))
|
|
162
|
+
print(format_box_line(f"Rollback Safety:Automatic pre-apply snapshot to .torusguard/snapshots/{run_folder.name}"))
|
|
163
|
+
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
164
|
+
|
|
165
|
+
applied_count = 0
|
|
166
|
+
skipped_count = 0
|
|
167
|
+
all_approved = auto_approve
|
|
168
|
+
diff_summary_lines = [f"# TorusGuard Applied Patch Summary: {run_folder.name}\n"]
|
|
169
|
+
applied_bundles = []
|
|
170
|
+
|
|
171
|
+
# Import memory_engine for Golden Recipe distillation
|
|
172
|
+
scripts_dir = Path(__file__).resolve().parent
|
|
173
|
+
if str(scripts_dir) not in sys.path:
|
|
174
|
+
sys.path.insert(0, str(scripts_dir))
|
|
175
|
+
import memory_engine
|
|
176
|
+
|
|
177
|
+
for idx, b in enumerate(bundles, 1):
|
|
178
|
+
rule_id = b.get("rule_id", "TG-SEC")
|
|
179
|
+
rel_path = b.get("target_file", "")
|
|
180
|
+
line_no = b.get("line_number", 1)
|
|
181
|
+
diff = b.get("proposed_diff", "")
|
|
182
|
+
desc = b.get("what_should_change", "")
|
|
183
|
+
patched_content = b.get("patched_content")
|
|
184
|
+
|
|
185
|
+
print(f" {YELLOW}─── [{idx}/{len(bundles)}] Candidate Patch: [{rule_id}] on {rel_path}:{line_no} ───{RESET}")
|
|
186
|
+
print(f" Strategy: {WHITE}{desc}{RESET}")
|
|
187
|
+
print(f" Ponytail: {GREEN}+{b.get('additions', 0)} / -{b.get('deletions', 0)}{RESET}\n")
|
|
188
|
+
|
|
189
|
+
# Display syntax-highlighted diff
|
|
190
|
+
for dline in diff.splitlines()[:12]:
|
|
191
|
+
if dline.startswith("+") and not dline.startswith("+++"):
|
|
192
|
+
print(f" {GREEN}{dline}{RESET}")
|
|
193
|
+
elif dline.startswith("-") and not dline.startswith("---"):
|
|
194
|
+
print(f" {RED}{dline}{RESET}")
|
|
195
|
+
elif dline.startswith("@"):
|
|
196
|
+
print(f" {CYAN}{dline}{RESET}")
|
|
197
|
+
else:
|
|
198
|
+
print(f" {DIM}{dline}{RESET}")
|
|
199
|
+
print()
|
|
200
|
+
|
|
201
|
+
if not all_approved:
|
|
202
|
+
try:
|
|
203
|
+
choice = input(f" {BOLD}Apply this patch?{RESET} ([y]es / [n]o / [a]ll / [q]uit): ").strip().lower()
|
|
204
|
+
except (EOFError, KeyboardInterrupt):
|
|
205
|
+
print(f"\n {YELLOW}Aborted by user.{RESET}\n")
|
|
206
|
+
break
|
|
207
|
+
|
|
208
|
+
if choice in ("q", "quit"):
|
|
209
|
+
print(f"\n {YELLOW}Exited review.{RESET}\n")
|
|
210
|
+
break
|
|
211
|
+
elif choice in ("a", "all"):
|
|
212
|
+
all_approved = True
|
|
213
|
+
elif choice not in ("y", "yes", ""):
|
|
214
|
+
print(f" {GRAY}Skipped.{RESET}\n")
|
|
215
|
+
skipped_count += 1
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
# Execute Application
|
|
219
|
+
target_file = target_root / rel_path
|
|
220
|
+
if not target_file.is_file():
|
|
221
|
+
print(f" {RED}✖ Target file not found: {target_file}{RESET}\n")
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
# 1. Pre-apply Snapshot (.bak)
|
|
225
|
+
bak_dest = snapshot_dir / (rel_path + ".bak")
|
|
226
|
+
bak_dest.parent.mkdir(parents=True, exist_ok=True)
|
|
227
|
+
shutil.copy2(target_file, bak_dest)
|
|
228
|
+
|
|
229
|
+
# 2. Write Patched Content
|
|
230
|
+
if patched_content:
|
|
231
|
+
target_file.write_text(patched_content, encoding="utf-8")
|
|
232
|
+
else:
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
# 3. Distill into Golden Fix Recipe in Memory Engine
|
|
236
|
+
try:
|
|
237
|
+
fn = getattr(memory_engine, "record_golden_recipe", getattr(memory_engine, "store_golden_recipe", None))
|
|
238
|
+
if fn:
|
|
239
|
+
fn(
|
|
240
|
+
rule_id=rule_id,
|
|
241
|
+
before_snippet=target_file.read_text(encoding="utf-8").splitlines()[max(0, line_no-1)],
|
|
242
|
+
after_snippet=desc,
|
|
243
|
+
diff_snippet=diff,
|
|
244
|
+
file_type=target_file.suffix,
|
|
245
|
+
description=desc,
|
|
246
|
+
root_dir=target_root
|
|
247
|
+
)
|
|
248
|
+
# Record event
|
|
249
|
+
memory_engine.record_event("fix_applied", {
|
|
250
|
+
"rule_id": rule_id,
|
|
251
|
+
"file_path": rel_path,
|
|
252
|
+
"line_number": line_no,
|
|
253
|
+
"bundle_id": b.get("bundle_id")
|
|
254
|
+
}, root_dir=target_root)
|
|
255
|
+
except Exception:
|
|
256
|
+
pass
|
|
257
|
+
|
|
258
|
+
applied_count += 1
|
|
259
|
+
applied_bundles.append(b)
|
|
260
|
+
diff_summary_lines.append(f"## Applied: `[{rule_id}]` on `{rel_path}:{line_no}`")
|
|
261
|
+
diff_summary_lines.append("```diff")
|
|
262
|
+
diff_summary_lines.append(diff.strip())
|
|
263
|
+
diff_summary_lines.append("```\n")
|
|
264
|
+
print(f" {GREEN}✔ Applied successfully.{RESET} (Snapshot saved: {bak_dest.relative_to(target_root)})\n")
|
|
265
|
+
|
|
266
|
+
# Write apply artifacts in run folder
|
|
267
|
+
(run_folder / "diff_summary.md").write_text("\n".join(diff_summary_lines), encoding="utf-8")
|
|
268
|
+
apply_plan_lines = [
|
|
269
|
+
f"# TorusGuard Governed Application Plan: {run_folder.name}",
|
|
270
|
+
f"- **Total Candidate Patches:** {len(bundles)}",
|
|
271
|
+
f"- **Successfully Applied:** {applied_count}",
|
|
272
|
+
f"- **Skipped by Human Gate:** {skipped_count}",
|
|
273
|
+
f"- **Execution Timestamp:** {get_ist_now().strftime('%Y-%m-%d %H:%M:%S IST')}",
|
|
274
|
+
f"- **Rollback Directory:** `.torusguard/snapshots/{run_folder.name}`"
|
|
275
|
+
]
|
|
276
|
+
(run_folder / "apply_plan.md").write_text("\n".join(apply_plan_lines), encoding="utf-8")
|
|
277
|
+
|
|
278
|
+
# Update manifest
|
|
279
|
+
manifest_file = run_folder / "manifest.json"
|
|
280
|
+
if manifest_file.is_file():
|
|
281
|
+
try:
|
|
282
|
+
mdata = json.loads(manifest_file.read_text(encoding="utf-8"))
|
|
283
|
+
mdata["status"] = "patches_applied"
|
|
284
|
+
mdata["remediated"] = applied_count
|
|
285
|
+
manifest_file.write_text(json.dumps(mdata, indent=2), encoding="utf-8")
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
# Summary Box
|
|
290
|
+
print(f" {CYAN}┌─ Application Summary ───────────────────────────────────────────────────┐{RESET}")
|
|
291
|
+
print(format_box_line(f"Applied Patches:{GREEN}{applied_count} applied cleanly{RESET}"))
|
|
292
|
+
print(format_box_line(f"Skipped / Pass: {GRAY}{skipped_count} skipped{RESET}"))
|
|
293
|
+
print(format_box_line(f"Golden Recipes: {GREEN}{applied_count} distilled into persistent memory{RESET}"))
|
|
294
|
+
print(format_box_line(f"Snapshot Safety:.torusguard/snapshots/{run_folder.name}"))
|
|
295
|
+
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
296
|
+
|
|
297
|
+
print(f" {GREEN}╔═ Next Governed Action ═════════════════════════════════════════════════════╗{RESET}")
|
|
298
|
+
print(format_box_line(f"Differential Recheck: {BOLD}{WHITE}npx torusguard recheck{RESET} (Verify fix closure)", border="║", border_color=GREEN))
|
|
299
|
+
print(format_box_line(f"View Golden Recipes: {CYAN}npx torusguard recipes{RESET} (Inspect memory)", border="║", border_color=GREEN))
|
|
300
|
+
print(format_box_line(f"Emergency Rollback: {YELLOW}npx torusguard rollback{RESET} (Instant revert)", border="║", border_color=GREEN))
|
|
301
|
+
print(f" {GREEN}╚═════════════════════════════════════════════════════════════════════════════╝{RESET}\n")
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
"applied_count": applied_count,
|
|
305
|
+
"skipped_count": skipped_count,
|
|
306
|
+
"applied_bundles": applied_bundles,
|
|
307
|
+
"snapshot_dir": str(snapshot_dir)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def main():
|
|
312
|
+
parser = argparse.ArgumentParser(description="TorusGuard Governed Patch Application Engine")
|
|
313
|
+
parser.add_argument("path", nargs="?", default=".", help="Target project root directory")
|
|
314
|
+
parser.add_argument("--run", "-r", help="Explicit run ID to apply")
|
|
315
|
+
parser.add_argument("--yes", "-y", action="store_true", help="Auto-approve all patches (non-interactive)")
|
|
316
|
+
parser.add_argument("--rollback", action="store_true", help="Rollback all applied patches using snapshots")
|
|
317
|
+
parser.add_argument("--json", action="store_true", help="Output raw JSON")
|
|
318
|
+
args = parser.parse_args()
|
|
319
|
+
|
|
320
|
+
target = Path(args.path).resolve()
|
|
321
|
+
|
|
322
|
+
if args.rollback:
|
|
323
|
+
execute_rollback(target, run_id_arg=args.run)
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
result = execute_apply(target, run_id_arg=args.run, auto_approve=args.yes)
|
|
327
|
+
if args.json:
|
|
328
|
+
print(json.dumps(result, indent=2))
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
if __name__ == "__main__":
|
|
332
|
+
main()
|