torusguard 1.0.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. package/.torusguard/.manifest.json +196 -78
  2. package/.torusguard/references/csharp-security.md +41 -0
  3. package/.torusguard/references/go-security.md +41 -0
  4. package/.torusguard/references/java-security.md +40 -0
  5. package/.torusguard/references/polyglot-security-matrix.md +25 -0
  6. package/.torusguard/references/rust-security.md +40 -0
  7. package/.torusguard/rules/README.md +93 -62
  8. package/.torusguard/rules/TG-AUTH-001-weak-password-storage.md +103 -0
  9. package/.torusguard/rules/TG-AUTH-002-client-only-authorization.md +85 -0
  10. package/.torusguard/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
  11. package/.torusguard/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
  12. package/.torusguard/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
  13. package/.torusguard/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
  14. package/.torusguard/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
  15. package/.torusguard/rules/TG-DB-001-frontend-database-query.md +83 -0
  16. package/.torusguard/rules/TG-DB-002-privileged-database-credential.md +86 -0
  17. package/.torusguard/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
  18. package/.torusguard/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
  19. package/.torusguard/rules/TG-INPUT-001-missing-server-validation.md +98 -0
  20. package/.torusguard/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
  21. package/.torusguard/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
  22. package/.torusguard/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
  23. package/.torusguard/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
  24. package/.torusguard/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
  25. package/.torusguard/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
  26. package/.torusguard/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
  27. package/.torusguard/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
  28. package/.torusguard/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
  29. package/.torusguard/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
  30. package/.torusguard/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
  31. package/.torusguard/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
  32. package/.torusguard/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
  33. package/.torusguard/rules/TG-SEC-002-public-environment-secrets.md +83 -0
  34. package/.torusguard/rules/TG-SEC-003-tracked-env-file.md +79 -0
  35. package/.torusguard/rules/TG-SEC-004-sensitive-logging.md +83 -0
  36. package/.torusguard/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
  37. package/.torusguard/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
  38. package/.torusguard/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
  39. package/.torusguard/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
  40. package/.torusguard/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
  41. package/.torusguard/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
  42. package/.torusguard/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
  43. package/.torusguard/rules/active/TG-DB-001-frontend-database-query.md +83 -0
  44. package/.torusguard/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
  45. package/.torusguard/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
  46. package/.torusguard/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
  47. package/.torusguard/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
  48. package/.torusguard/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
  49. package/.torusguard/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
  50. package/.torusguard/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
  51. package/.torusguard/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
  52. package/.torusguard/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
  53. package/.torusguard/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
  54. package/.torusguard/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
  55. package/.torusguard/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
  56. package/.torusguard/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
  57. package/.torusguard/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
  58. package/.torusguard/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
  59. package/.torusguard/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
  60. package/.torusguard/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
  61. package/.torusguard/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
  62. package/.torusguard/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
  63. package/.torusguard/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
  64. package/.torusguard/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
  65. package/.torusguard/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
  66. package/.torusguard/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
  67. package/.torusguard/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
  68. package/.torusguard/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
  69. package/.torusguard/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
  70. package/.torusguard/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
  71. package/.torusguard/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
  72. package/.torusguard/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
  73. package/.torusguard/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
  74. package/.torusguard/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
  75. package/.torusguard/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
  76. package/.torusguard/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
  77. package/.torusguard/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
  78. package/.torusguard/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
  79. package/.torusguard/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
  80. package/.torusguard/rules/custom/.gitkeep +1 -0
  81. package/.torusguard/rules/custom/README.md +30 -0
  82. package/.torusguard/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
  83. package/.torusguard/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
  84. package/.torusguard/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
  85. package/.torusguard/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
  86. package/.torusguard/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
  87. package/.torusguard/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
  88. package/.torusguard/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
  89. package/.torusguard/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
  90. package/.torusguard/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
  91. package/.torusguard/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
  92. package/.torusguard/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
  93. package/.torusguard/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
  94. package/.torusguard/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
  95. package/.torusguard/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
  96. package/.torusguard/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
  97. package/.torusguard/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
  98. package/.torusguard/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
  99. package/.torusguard/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
  100. package/.torusguard/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
  101. package/.torusguard/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
  102. package/.torusguard/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
  103. package/.torusguard/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
  104. package/.torusguard/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
  105. package/.torusguard/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
  106. package/.torusguard/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
  107. package/.torusguard/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
  108. package/.torusguard/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
  109. package/.torusguard/runs/report-latest.html +370 -0
  110. package/.torusguard/runs/run-20260908-112648-audit/findings.json +1520 -0
  111. package/.torusguard/runs/run-20260908-112648-audit/findings.md +560 -0
  112. package/.torusguard/runs/run-20260908-112648-audit/manifest.json +12 -0
  113. package/.torusguard/runs/run-20260908-112648-audit/summary.md +5 -0
  114. package/.torusguard/runs/run-20260908-112722-audit/findings.json +1520 -0
  115. package/.torusguard/runs/run-20260908-112722-audit/findings.md +560 -0
  116. package/.torusguard/runs/run-20260908-112722-audit/manifest.json +12 -0
  117. package/.torusguard/runs/run-20260908-112722-audit/summary.md +5 -0
  118. package/.torusguard/runs/run-20260908-113004-audit/findings.json +1520 -0
  119. package/.torusguard/runs/run-20260908-113004-audit/findings.md +560 -0
  120. package/.torusguard/runs/run-20260908-113004-audit/manifest.json +12 -0
  121. package/.torusguard/runs/run-20260908-113004-audit/summary.md +5 -0
  122. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
  123. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
  124. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
  125. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
  126. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
  127. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
  128. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
  129. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
  130. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
  131. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
  132. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
  133. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
  134. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
  135. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
  136. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
  137. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
  138. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
  139. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
  140. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
  141. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
  142. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
  143. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
  144. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
  145. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
  146. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
  147. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
  148. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
  149. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
  150. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
  151. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
  152. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
  153. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
  154. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
  155. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
  156. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
  157. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
  158. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
  159. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
  160. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
  161. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
  162. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
  163. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
  164. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
  165. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
  166. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
  167. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
  168. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
  169. package/.torusguard/runs/run-20260908-113127-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
  170. package/.torusguard/runs/run-20260908-113127-audit/findings.json +1520 -0
  171. package/.torusguard/runs/run-20260908-113127-audit/findings.md +560 -0
  172. package/.torusguard/runs/run-20260908-113127-audit/manifest.json +13 -0
  173. package/.torusguard/runs/run-20260908-113127-audit/remediation.md +262 -0
  174. package/.torusguard/runs/run-20260908-113127-audit/summary.md +5 -0
  175. package/.torusguard/runs/run-20260908-115240-audit/apply_plan.md +6 -0
  176. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
  177. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
  178. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
  179. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
  180. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
  181. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
  182. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
  183. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
  184. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
  185. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/metadata.json +15 -0
  186. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/minimal_patch_plan.md +21 -0
  187. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-334-cb819d/patch.diff +9 -0
  188. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
  189. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
  190. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
  191. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
  192. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
  193. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
  194. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
  195. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
  196. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
  197. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
  198. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
  199. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
  200. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/metadata.json +15 -0
  201. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/minimal_patch_plan.md +21 -0
  202. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-002-232-1b45d1/patch.diff +9 -0
  203. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
  204. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
  205. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
  206. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/metadata.json +15 -0
  207. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/minimal_patch_plan.md +21 -0
  208. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-platform-001-168-5027cb/patch.diff +9 -0
  209. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
  210. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
  211. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
  212. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
  213. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
  214. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
  215. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
  216. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
  217. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
  218. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
  219. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
  220. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
  221. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
  222. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
  223. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
  224. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
  225. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
  226. package/.torusguard/runs/run-20260908-115240-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
  227. package/.torusguard/runs/run-20260908-115240-audit/diff_summary.md +221 -0
  228. package/.torusguard/runs/run-20260908-115240-audit/findings.json +1553 -0
  229. package/.torusguard/runs/run-20260908-115240-audit/findings.md +572 -0
  230. package/.torusguard/runs/run-20260908-115240-audit/manifest.json +14 -0
  231. package/.torusguard/runs/run-20260908-115240-audit/recheck.md +91 -0
  232. package/.torusguard/runs/run-20260908-115240-audit/remediation.md +278 -0
  233. package/.torusguard/runs/run-20260908-115240-audit/summary.md +5 -0
  234. package/.torusguard/schemas/golden-recipe.schema.json +75 -0
  235. package/.torusguard/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
  236. package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
  237. package/.torusguard/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
  238. package/.torusguard/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
  239. package/.torusguard/scripts/__pycache__/run_manager.cpython-311.pyc +0 -0
  240. package/.torusguard/scripts/__pycache__/stack_detect.cpython-311.pyc +0 -0
  241. package/.torusguard/scripts/apply_runner.py +332 -0
  242. package/.torusguard/scripts/audit_runner.py +550 -0
  243. package/.torusguard/scripts/diff_guard.py +183 -8
  244. package/.torusguard/scripts/finding_scorer.py +91 -5
  245. package/.torusguard/scripts/harden_runner.py +353 -0
  246. package/.torusguard/scripts/html_reporter.py +643 -0
  247. package/.torusguard/scripts/manifest_builder.py +7 -5
  248. package/.torusguard/scripts/memory_engine.py +525 -99
  249. package/.torusguard/scripts/monorepo_detector.py +123 -10
  250. package/.torusguard/scripts/recheck_runner.py +221 -0
  251. package/.torusguard/scripts/recipes_runner.py +130 -0
  252. package/.torusguard/scripts/rules_sync.py +321 -0
  253. package/.torusguard/scripts/run_manager.py +17 -5
  254. package/.torusguard/scripts/stack_detect.py +458 -26
  255. package/.torusguard/skills/torusguard/SKILL.md +42 -42
  256. package/.torusguard/skills/torusguard/references/csharp-security.md +41 -0
  257. package/.torusguard/skills/torusguard/references/go-security.md +41 -0
  258. package/.torusguard/skills/torusguard/references/java-security.md +40 -0
  259. package/.torusguard/skills/torusguard/references/polyglot-security-matrix.md +25 -0
  260. package/.torusguard/skills/torusguard/references/rust-security.md +40 -0
  261. package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/client/src/App.jsx.bak +63 -0
  262. package/.torusguard/snapshots/run-20260908-115240-audit/examples/vulnerable-react-express/server/index.js.bak +82 -0
  263. package/.torusguard/snapshots/run-20260908-115240-audit/harness/engine/fixture_manager.py.bak +245 -0
  264. package/.torusguard/snapshots/run-20260908-115240-audit/harness/master_validation.py.bak +698 -0
  265. package/.torusguard/snapshots/run-20260908-115240-audit/harness/runner.py.bak +551 -0
  266. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_e2e.py.bak +498 -0
  267. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_qa_v0_6_0.py.bak +572 -0
  268. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_1_scale.py.bak +371 -0
  269. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_2_modern_stacks.py.bak +462 -0
  270. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v0_6_3_hardening.py.bak +444 -0
  271. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_0_0_memory.py.bak +302 -0
  272. package/.torusguard/snapshots/run-20260908-115240-audit/harness/validate_v1_1_0_advanced_memory.py.bak +342 -0
  273. package/.torusguard/snapshots/run-20260908-115240-audit/skills/torusguard/payload/scripts/harden_runner.py.bak +353 -0
  274. package/.torusguard/snapshots/run-20260908-115240-audit/tests/test_v0_6_0_governed_remediation.py.bak +353 -0
  275. package/README.md +344 -410
  276. package/bin/torusguard.js +195 -21
  277. package/package.json +1 -1
  278. package/skills/torusguard/SKILL.md +42 -42
  279. package/skills/torusguard/bootstrap.py +149 -70
  280. package/skills/torusguard/payload/.manifest.json +196 -78
  281. package/skills/torusguard/payload/references/csharp-security.md +41 -0
  282. package/skills/torusguard/payload/references/go-security.md +41 -0
  283. package/skills/torusguard/payload/references/java-security.md +40 -0
  284. package/skills/torusguard/payload/references/polyglot-security-matrix.md +25 -0
  285. package/skills/torusguard/payload/references/rust-security.md +40 -0
  286. package/skills/torusguard/payload/rules/README.md +93 -62
  287. package/skills/torusguard/payload/rules/TG-AUTH-001-weak-password-storage.md +103 -0
  288. package/skills/torusguard/payload/rules/TG-AUTH-002-client-only-authorization.md +85 -0
  289. package/skills/torusguard/payload/rules/TG-AUTH-003-missing-object-authorization.md +85 -0
  290. package/skills/torusguard/payload/rules/TG-AUTH-004-insecure-session-cookie.md +96 -0
  291. package/skills/torusguard/payload/rules/TG-AUTH-005-unsafe-password-reset.md +104 -0
  292. package/skills/torusguard/payload/rules/TG-CLIENT-001-public-production-source-maps.md +102 -0
  293. package/skills/torusguard/payload/rules/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
  294. package/skills/torusguard/payload/rules/TG-DB-001-frontend-database-query.md +83 -0
  295. package/skills/torusguard/payload/rules/TG-DB-002-privileged-database-credential.md +86 -0
  296. package/skills/torusguard/payload/rules/TG-DB-003-frontend-admin-sdk.md +91 -0
  297. package/skills/torusguard/payload/rules/TG-DB-004-missing-tenant-query-isolation.md +103 -0
  298. package/skills/torusguard/payload/rules/TG-INPUT-001-missing-server-validation.md +98 -0
  299. package/skills/torusguard/payload/rules/TG-INPUT-002-raw-sql-concatenation.md +94 -0
  300. package/skills/torusguard/payload/rules/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
  301. package/skills/torusguard/payload/rules/TG-INPUT-004-unrestricted-file-upload.md +92 -0
  302. package/skills/torusguard/payload/rules/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
  303. package/skills/torusguard/payload/rules/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
  304. package/skills/torusguard/payload/rules/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
  305. package/skills/torusguard/payload/rules/TG-PLATFORM-002-missing-security-headers.md +95 -0
  306. package/skills/torusguard/payload/rules/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
  307. package/skills/torusguard/payload/rules/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
  308. package/skills/torusguard/payload/rules/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
  309. package/skills/torusguard/payload/rules/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
  310. package/skills/torusguard/payload/rules/TG-RATE-003-unbounded-resource-consumption.md +105 -0
  311. package/skills/torusguard/payload/rules/TG-SEC-001-hardcoded-secrets.md +83 -0
  312. package/skills/torusguard/payload/rules/TG-SEC-002-public-environment-secrets.md +83 -0
  313. package/skills/torusguard/payload/rules/TG-SEC-003-tracked-env-file.md +79 -0
  314. package/skills/torusguard/payload/rules/TG-SEC-004-sensitive-logging.md +83 -0
  315. package/skills/torusguard/payload/rules/active/TG-AUTH-001-weak-password-storage.md +103 -0
  316. package/skills/torusguard/payload/rules/active/TG-AUTH-002-client-only-authorization.md +85 -0
  317. package/skills/torusguard/payload/rules/active/TG-AUTH-003-missing-object-authorization.md +85 -0
  318. package/skills/torusguard/payload/rules/active/TG-AUTH-004-insecure-session-cookie.md +96 -0
  319. package/skills/torusguard/payload/rules/active/TG-AUTH-005-unsafe-password-reset.md +104 -0
  320. package/skills/torusguard/payload/rules/active/TG-CLIENT-001-public-production-source-maps.md +102 -0
  321. package/skills/torusguard/payload/rules/active/TG-CLIENT-002-sensitive-client-bundle-content.md +102 -0
  322. package/skills/torusguard/payload/rules/active/TG-DB-001-frontend-database-query.md +83 -0
  323. package/skills/torusguard/payload/rules/active/TG-DB-002-privileged-database-credential.md +86 -0
  324. package/skills/torusguard/payload/rules/active/TG-DB-003-frontend-admin-sdk.md +91 -0
  325. package/skills/torusguard/payload/rules/active/TG-DB-004-missing-tenant-query-isolation.md +103 -0
  326. package/skills/torusguard/payload/rules/active/TG-INPUT-001-missing-server-validation.md +98 -0
  327. package/skills/torusguard/payload/rules/active/TG-INPUT-002-raw-sql-concatenation.md +94 -0
  328. package/skills/torusguard/payload/rules/active/TG-INPUT-003-unsafe-html-or-code-execution.md +102 -0
  329. package/skills/torusguard/payload/rules/active/TG-INPUT-004-unrestricted-file-upload.md +92 -0
  330. package/skills/torusguard/payload/rules/active/TG-INPUT-005-unsafe-template-rendering-and-escaping.md +102 -0
  331. package/skills/torusguard/payload/rules/active/TG-INPUT-006-unsafe-file-path-traversal.md +131 -0
  332. package/skills/torusguard/payload/rules/active/TG-PLATFORM-001-wildcard-cors-with-credentials.md +101 -0
  333. package/skills/torusguard/payload/rules/active/TG-PLATFORM-002-missing-security-headers.md +95 -0
  334. package/skills/torusguard/payload/rules/active/TG-PLATFORM-003-production-stack-trace-exposure.md +94 -0
  335. package/skills/torusguard/payload/rules/active/TG-PLATFORM-004-missing-request-size-limits.md +102 -0
  336. package/skills/torusguard/payload/rules/active/TG-RATE-001-unlimited-auth-endpoint.md +112 -0
  337. package/skills/torusguard/payload/rules/active/TG-RATE-002-unlimited-public-write-endpoint.md +109 -0
  338. package/skills/torusguard/payload/rules/active/TG-RATE-003-unbounded-resource-consumption.md +105 -0
  339. package/skills/torusguard/payload/rules/active/TG-SEC-001-hardcoded-secrets.md +83 -0
  340. package/skills/torusguard/payload/rules/active/TG-SEC-002-public-environment-secrets.md +83 -0
  341. package/skills/torusguard/payload/rules/active/TG-SEC-003-tracked-env-file.md +79 -0
  342. package/skills/torusguard/payload/rules/active/TG-SEC-004-sensitive-logging.md +83 -0
  343. package/skills/torusguard/payload/rules/agent/TG-AGENT-001-prompt-injection-system-context.md +56 -0
  344. package/skills/torusguard/payload/rules/agent/TG-AGENT-002-unsafe-tool-dispatch-unsandboxed-execution.md +68 -0
  345. package/skills/torusguard/payload/rules/agent/TG-AGENT-003-broad-mcp-tool-scoping.md +53 -0
  346. package/skills/torusguard/payload/rules/agent/TG-AGENT-004-persistent-memory-cross-session-leakage.md +67 -0
  347. package/skills/torusguard/payload/rules/authorization/TG-AUTH-006-mass-assignment.md +70 -0
  348. package/skills/torusguard/payload/rules/authorization/TG-AUTH-007-missing-property-level-authorization.md +71 -0
  349. package/skills/torusguard/payload/rules/authorization/TG-AUTH-008-untrusted-role-header-injection.md +112 -0
  350. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +37 -0
  351. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +37 -0
  352. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +37 -0
  353. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +37 -0
  354. package/skills/torusguard/payload/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +37 -0
  355. package/skills/torusguard/payload/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +37 -0
  356. package/skills/torusguard/payload/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +37 -0
  357. package/skills/torusguard/payload/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +37 -0
  358. package/skills/torusguard/payload/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +37 -0
  359. package/skills/torusguard/payload/rules/custom/.gitkeep +1 -0
  360. package/skills/torusguard/payload/rules/custom/README.md +30 -0
  361. package/skills/torusguard/payload/rules/edge/TG-EDGE-001-serverless-global-state-leak.md +83 -0
  362. package/skills/torusguard/payload/rules/edge/TG-EDGE-003-aws-lambda-ephemeral-cold-start.md +76 -0
  363. package/skills/torusguard/payload/rules/graphql/TG-GQL-001-missing-query-depth-or-complexity-limits.md +64 -0
  364. package/skills/torusguard/payload/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +37 -0
  365. package/skills/torusguard/payload/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +37 -0
  366. package/skills/torusguard/payload/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +37 -0
  367. package/skills/torusguard/payload/rules/secrets/TG-SEC-005-secret-in-git-history.md +37 -0
  368. package/skills/torusguard/payload/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +37 -0
  369. package/skills/torusguard/payload/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +37 -0
  370. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-001-user-controlled-server-side-url-fetch.md +68 -0
  371. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +37 -0
  372. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +37 -0
  373. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +37 -0
  374. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +37 -0
  375. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-002-vulnerable-dependency-review-missing.md +94 -0
  376. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +37 -0
  377. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +37 -0
  378. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +37 -0
  379. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-006-container-build-secret-persistence.md +61 -0
  380. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +37 -0
  381. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +37 -0
  382. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +37 -0
  383. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +37 -0
  384. package/skills/torusguard/payload/rules/websocket/TG-WS-001-unauthenticated-websocket-handshake.md +82 -0
  385. package/skills/torusguard/payload/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +37 -0
  386. package/skills/torusguard/payload/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +37 -0
  387. package/skills/torusguard/payload/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +37 -0
  388. package/skills/torusguard/payload/schemas/golden-recipe.schema.json +75 -0
  389. package/skills/torusguard/payload/scripts/apply_runner.py +332 -0
  390. package/skills/torusguard/payload/scripts/audit_runner.py +550 -0
  391. package/skills/torusguard/payload/scripts/diff_guard.py +183 -8
  392. package/skills/torusguard/payload/scripts/finding_scorer.py +91 -5
  393. package/skills/torusguard/payload/scripts/harden_runner.py +353 -0
  394. package/skills/torusguard/payload/scripts/html_reporter.py +643 -0
  395. package/skills/torusguard/payload/scripts/manifest_builder.py +7 -5
  396. package/skills/torusguard/payload/scripts/memory_engine.py +525 -99
  397. package/skills/torusguard/payload/scripts/monorepo_detector.py +123 -10
  398. package/skills/torusguard/payload/scripts/recheck_runner.py +221 -0
  399. package/skills/torusguard/payload/scripts/recipes_runner.py +130 -0
  400. package/skills/torusguard/payload/scripts/rules_sync.py +321 -0
  401. package/skills/torusguard/payload/scripts/run_manager.py +17 -5
  402. package/skills/torusguard/payload/scripts/stack_detect.py +458 -26
  403. package/skills/torusguard/payload/skills/torusguard/SKILL.md +42 -42
  404. package/skills/torusguard/payload/skills/torusguard/bootstrap.py +149 -70
  405. package/skills/torusguard/payload/skills/torusguard/references/csharp-security.md +41 -0
  406. package/skills/torusguard/payload/skills/torusguard/references/go-security.md +41 -0
  407. package/skills/torusguard/payload/skills/torusguard/references/java-security.md +40 -0
  408. package/skills/torusguard/payload/skills/torusguard/references/polyglot-security-matrix.md +25 -0
  409. package/skills/torusguard/payload/skills/torusguard/references/rust-security.md +40 -0
  410. package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
  411. package/skills/torusguard/__pycache__/bootstrap.cpython-311.pyc +0 -0
@@ -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()