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