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
@@ -162,6 +162,8 @@ def compute_confidence_score(
162
162
 
163
163
  def main():
164
164
  parser = argparse.ArgumentParser(description="TorusGuard Confidence Scorer")
165
+ parser.add_argument("--dir", type=str, help="Target project root directory to scan and score")
166
+ parser.add_argument("--run", type=str, help="Path to run directory to evaluate/score")
165
167
  parser.add_argument("--eq", type=int, default=35, help="Evidence quality score (0-35)")
166
168
  parser.add_argument("--rs", type=int, default=0, help="Reproduction success score (0-25)")
167
169
  parser.add_argument("--ic", type=int, default=5, help="Independent confirmations score (0-15)")
@@ -173,6 +175,62 @@ def main():
173
175
  parser.add_argument("--json", action="store_true", help="Output JSON")
174
176
  args = parser.parse_args()
175
177
 
178
+ # Bridge: when --dir is provided, delegate to audit_runner
179
+ if args.dir:
180
+ scripts_dir = Path(__file__).resolve().parent
181
+ if str(scripts_dir) not in sys.path:
182
+ sys.path.insert(0, str(scripts_dir))
183
+ try:
184
+ import audit_runner
185
+ res = audit_runner.execute_audit(Path(args.dir), json_output=args.json)
186
+ sys.exit(0 if res.get("critical_count", 0) == 0 else 1)
187
+ except Exception as e:
188
+ print(f"[ERROR] Finding Scorer: Failed to run audit on {args.dir}: {e}", file=sys.stderr)
189
+ sys.exit(1)
190
+
191
+ # Bridge: when --run is provided, score findings in run folder
192
+ if args.run:
193
+ run_p = Path(args.run).resolve()
194
+ f_file = run_p / "findings.json"
195
+ if not f_file.is_file():
196
+ print(f"[ERROR] Run findings not found at: {f_file}", file=sys.stderr)
197
+ sys.exit(1)
198
+ try:
199
+ with open(f_file, "r", encoding="utf-8") as f:
200
+ findings_data = json.load(f)
201
+ except Exception as e:
202
+ print(f"[ERROR] Malformed findings JSON: {e}", file=sys.stderr)
203
+ sys.exit(1)
204
+
205
+ total_score = 0
206
+ scored_items = []
207
+ for item in findings_data:
208
+ s, b, factors = compute_confidence_score(
209
+ evidence_quality=item.get("evidence_quality", 35),
210
+ reproduction_success=item.get("reproduction_success", 0),
211
+ independent_confirmations=item.get("independent_confirmations", 5),
212
+ environmental_clarity=item.get("environmental_clarity", 15),
213
+ manual_review_status=item.get("manual_review_status", 0),
214
+ rule_id=item.get("rule_id"),
215
+ file_path=item.get("file_path")
216
+ )
217
+ item["confidence_score"] = s
218
+ item["confidence_band"] = b
219
+ item["confidence_factors"] = factors
220
+ scored_items.append(item)
221
+ total_score += s
222
+
223
+ avg_score = int(total_score / max(1, len(scored_items)))
224
+ with open(f_file, "w", encoding="utf-8") as f:
225
+ json.dump(scored_items, f, indent=2)
226
+
227
+ if args.json:
228
+ print(json.dumps({"run": str(run_p), "findings_count": len(scored_items), "average_confidence": avg_score}, indent=2))
229
+ else:
230
+ print(f"[SUCCESS] Scored {len(scored_items)} findings in {run_p.name}")
231
+ print(f"Average Confidence: {avg_score}/100")
232
+ sys.exit(0)
233
+
176
234
  total, band, factors = compute_confidence_score(
177
235
  evidence_quality=args.eq,
178
236
  reproduction_success=args.rs,
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard Autonomous Remediation Engine (v1.3.0)
4
+ Evaluates audit findings against canonical patch templates, enforces Ponytail bounds
5
+ (<= 35 additions, <= 25 deletions), packages structured candidate remediation bundles,
6
+ and renders unified patch diffs.
7
+
8
+ Pure Python 3.10+ standard library (zero external dependencies).
9
+ """
10
+
11
+ import sys
12
+ import os
13
+ import re
14
+ import json
15
+ import difflib
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
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
+
30
+ # ─── Timezone Configuration: Indian Standard Time (IST, UTC+05:30) ───────────
31
+ IST = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
32
+
33
+ def get_ist_now() -> datetime.datetime:
34
+ return datetime.datetime.now(IST)
35
+
36
+ # ─── ANSI Colors & Formatter ───────────────────────────────────────────────────
37
+ BOLD = "\033[1m"
38
+ DIM = "\033[2m"
39
+ RESET = "\033[0m"
40
+ CYAN = "\033[36m"
41
+ GREEN = "\033[32m"
42
+ YELLOW = "\033[33m"
43
+ WHITE = "\033[97m"
44
+ GRAY = "\033[90m"
45
+ RED = "\033[31m"
46
+
47
+ ANSI_REGEX = re.compile(r'\033\[[0-9;]*m')
48
+
49
+ def get_visual_width(text: str) -> int:
50
+ clean = ANSI_REGEX.sub('', text)
51
+ width = 0
52
+ for ch in clean:
53
+ ea = unicodedata.east_asian_width(ch)
54
+ if ea in ('W', 'F'):
55
+ width += 2
56
+ elif ord(ch) >= 0x1F300:
57
+ width += 2
58
+ else:
59
+ width += 1
60
+ return width
61
+
62
+ def format_box_line(content: str, width: int = 71, border: str = "│", border_color: str = CYAN) -> str:
63
+ vis = get_visual_width(content)
64
+ pad = " " * max(0, width - vis)
65
+ return f" {border_color}{border}{RESET} {content}{pad}{border_color}{border}{RESET}"
66
+
67
+
68
+ def find_latest_audit_run(runs_dir: Path) -> Optional[Path]:
69
+ """Locate the most recent audit run folder containing findings.json."""
70
+ if not runs_dir.is_dir():
71
+ return None
72
+ runs = sorted(runs_dir.glob("run-*-audit"), key=lambda p: p.stat().st_mtime, reverse=True)
73
+ for r in runs:
74
+ if (r / "findings.json").is_file():
75
+ return r
76
+ return None
77
+
78
+
79
+ def generate_patch_for_finding(finding: Dict[str, Any], target_root: Path) -> Optional[Dict[str, Any]]:
80
+ """
81
+ Formulate a minimal, surgical fix for a finding adhering to Ponytail bounds
82
+ (<= 35 additions, <= 25 deletions).
83
+ """
84
+ rule_id = finding.get("rule_id", "")
85
+ file_path = finding.get("file_path", "")
86
+ line_number = finding.get("line_number", 1)
87
+ description = finding.get("description", "")
88
+
89
+ full_target = target_root / file_path
90
+ if not full_target.is_file():
91
+ return None
92
+
93
+ try:
94
+ content = full_target.read_text(encoding="utf-8", errors="replace")
95
+ except Exception:
96
+ return None
97
+
98
+ lines = content.splitlines()
99
+ if not (1 <= line_number <= len(lines)):
100
+ return None
101
+
102
+ target_line = lines[line_number - 1]
103
+ indent = re.match(r'^\s*', target_line).group(0) # type: ignore
104
+ new_lines = list(lines)
105
+ applied_rule = False
106
+ fix_explanation = ""
107
+
108
+ # Strategy 1: Hardcoded Secrets (TG-SEC-001 / TG-SEC-002)
109
+ if rule_id in ("TG-SEC-001", "TG-SEC-002"):
110
+ if file_path.endswith((".js", ".jsx", ".ts", ".tsx", ".mjs")):
111
+ # const secret = "..." -> const secret = process.env.JWT_SECRET || "...";
112
+ if re.search(r'(jwt_secret|jwtSecret|JWT_SECRET)', target_line, re.IGNORECASE):
113
+ replacement = f'{indent}const secret = process.env.JWT_SECRET || "";'
114
+ new_lines[line_number - 1] = replacement
115
+ applied_rule = True
116
+ fix_explanation = "Migrated hardcoded JWT secret to environment variable process.env.JWT_SECRET"
117
+ elif re.search(r'(:=|=)\s*["\'][A-Za-z0-9_\-]{8,}["\']', target_line):
118
+ var_match = re.search(r'(const|let|var)?\s*([A-Za-z0-9_]+)\s*[:=]', target_line)
119
+ var_name = var_match.group(2) if var_match else "API_KEY"
120
+ env_key = re.sub(r'(?<!^)(?=[A-Z])', '_', var_name).upper()
121
+ replacement = f'{indent}const {var_name} = process.env.{env_key} || "";'
122
+ new_lines[line_number - 1] = replacement
123
+ applied_rule = True
124
+ fix_explanation = f"Replaced hardcoded credential with process.env.{env_key}"
125
+ elif file_path.endswith(".py"):
126
+ if re.search(r'(:=|=)\s*["\'][A-Za-z0-9_\-]{8,}["\']', target_line):
127
+ var_match = re.search(r'([A-Za-z0-9_]+)\s*=', target_line)
128
+ var_name = var_match.group(1) if var_match else "SECRET_KEY"
129
+ env_key = var_name.upper()
130
+ replacement = f'{indent}{var_name} = os.environ.get("{env_key}", "")'
131
+ new_lines[line_number - 1] = replacement
132
+ applied_rule = True
133
+ fix_explanation = f"Replaced hardcoded credential with os.environ.get('{env_key}')"
134
+
135
+ # Strategy 2: Dangerous React HTML rendering (TG-INPUT-003)
136
+ elif rule_id == "TG-INPUT-003":
137
+ if "dangerouslySetInnerHTML" in target_line:
138
+ # <div dangerouslySetInnerHTML={{ __html: bio }} /> -> <div>{bio}</div>
139
+ var_match = re.search(r'__html\s*:\s*([A-Za-z0-9_]+)', target_line)
140
+ var_name = var_match.group(1) if var_match else "content"
141
+ replacement = f'{indent}<div>{{{var_name}}}</div>'
142
+ new_lines[line_number - 1] = replacement
143
+ applied_rule = True
144
+ fix_explanation = "Replaced raw dangerouslySetInnerHTML injection with safe React text interpolation"
145
+
146
+ # Strategy 3: Raw SQL Concatenation (TG-INPUT-002)
147
+ elif rule_id == "TG-INPUT-002":
148
+ if file_path.endswith(".py") and ("f\"SELECT" in target_line or "f'SELECT" in target_line):
149
+ # Replace f-string injection with parameterized query
150
+ fixed_line = re.sub(r'f(["\']SELECT.*?WHERE.*?)(\{[^}]+\})', r'\1%s', target_line)
151
+ if fixed_line != target_line:
152
+ new_lines[line_number - 1] = fixed_line
153
+ applied_rule = True
154
+ fix_explanation = "Replaced unsafe Python SQL f-string interpolation with parameterized SQL query placeholder"
155
+
156
+ # Strategy 4: Multi-Tenant Query Scoping (TG-DB-004)
157
+ elif rule_id == "TG-DB-004":
158
+ if ".objects.get(" in target_line and "tenant" not in target_line.lower() and "org" not in target_line.lower():
159
+ # .objects.get(id=...) -> .objects.get(id=..., organization_id=request.user.organization_id)
160
+ fixed_line = target_line.replace(".objects.get(", ".objects.get(organization_id=request.user.organization_id, ")
161
+ if fixed_line != target_line:
162
+ new_lines[line_number - 1] = fixed_line
163
+ applied_rule = True
164
+ fix_explanation = "Added explicit organization_id tenant boundary filter to ORM query"
165
+
166
+ # Strategy 5: Permissive CORS with Credentials (TG-PLATFORM-001)
167
+ elif rule_id == "TG-PLATFORM-001":
168
+ if "CORS_ALLOW_ALL_ORIGINS = True" in target_line:
169
+ replacement = f'{indent}CORS_ALLOWED_ORIGINS = [os.environ.get("ALLOWED_ORIGIN", "https://app.example.com")]'
170
+ new_lines[line_number - 1] = replacement
171
+ applied_rule = True
172
+ fix_explanation = "Replaced wildcard CORS configuration with strict origin whitelist environment variable"
173
+
174
+ if not applied_rule:
175
+ return None
176
+
177
+ # Calculate unified diff
178
+ orig_lines = [l + "\n" for l in lines]
179
+ patched_lines = [l + "\n" for l in new_lines]
180
+ diff = "".join(difflib.unified_diff(
181
+ orig_lines, patched_lines,
182
+ fromfile=f"a/{file_path}", tofile=f"b/{file_path}",
183
+ n=2
184
+ ))
185
+
186
+ additions = sum(1 for l in diff.splitlines() if l.startswith("+") and not l.startswith("+++"))
187
+ deletions = sum(1 for l in diff.splitlines() if l.startswith("-") and not l.startswith("---"))
188
+
189
+ # Ponytail bounds validation
190
+ if additions > 35 or deletions > 25:
191
+ return None
192
+
193
+ bundle_id = f"bnd-{rule_id.lower()}-{line_number}-{hashlib.sha256(diff.encode('utf-8')).hexdigest()[:6]}"
194
+
195
+ return {
196
+ "bundle_id": bundle_id,
197
+ "finding_id": finding.get("finding_id", f"{rule_id}-{line_number}"),
198
+ "rule_id": rule_id,
199
+ "title": finding.get("title", rule_id),
200
+ "target_file": file_path,
201
+ "line_number": line_number,
202
+ "what_is_wrong": description,
203
+ "why_it_matters": "Security vulnerability violating TorusGuard strict production safety invariant.",
204
+ "what_should_change": fix_explanation,
205
+ "proposed_diff": diff,
206
+ "additions": additions,
207
+ "deletions": deletions,
208
+ "patched_content": "\n".join(new_lines)
209
+ }
210
+
211
+
212
+ def execute_harden(target_root: Path, run_id_arg: Optional[str] = None) -> Dict[str, Any]:
213
+ """Execute autonomous remediation formulation and package candidate bundles."""
214
+ runs_dir = target_root / ".torusguard" / "runs"
215
+ if run_id_arg:
216
+ run_folder = runs_dir / run_id_arg
217
+ else:
218
+ run_folder = find_latest_audit_run(runs_dir)
219
+
220
+ if not run_folder or not run_folder.is_dir():
221
+ print(f"\n {RED}✖ No audit runs found to harden.{RESET}")
222
+ print(f" Run {CYAN}npx torusguard audit{RESET} first to discover actionable findings.\n")
223
+ sys.exit(1)
224
+
225
+ findings_file = run_folder / "findings.json"
226
+ if not findings_file.is_file():
227
+ print(f"\n {RED}✖ Missing findings.json in run folder:{RESET} {run_folder}\n")
228
+ sys.exit(1)
229
+
230
+ try:
231
+ findings = json.loads(findings_file.read_text(encoding="utf-8"))
232
+ except Exception as e:
233
+ print(f"\n {RED}✖ Failed to parse findings.json:{RESET} {e}\n")
234
+ sys.exit(1)
235
+
236
+ bundles_dir = run_folder / "bundles"
237
+ bundles_dir.mkdir(parents=True, exist_ok=True)
238
+
239
+ bundles = []
240
+ for f in findings:
241
+ candidate = generate_patch_for_finding(f, target_root)
242
+ if candidate:
243
+ bundles.append(candidate)
244
+ b_dir = bundles_dir / candidate["bundle_id"]
245
+ b_dir.mkdir(parents=True, exist_ok=True)
246
+
247
+ # 1. patch.diff
248
+ (b_dir / "patch.diff").write_text(candidate["proposed_diff"], encoding="utf-8")
249
+
250
+ # 2. minimal_patch_plan.md
251
+ plan_content = [
252
+ f"# Remediation Plan: {candidate['bundle_id']}",
253
+ f"- **Rule ID:** `{candidate['rule_id']}`",
254
+ f"- **Target File:** `{candidate['target_file']}:{candidate['line_number']}`",
255
+ f"- **Ponytail Churn:** `+{candidate['additions']} / -{candidate['deletions']}` (Compliant <=35/<=25)",
256
+ f"\n## Proposed Change\n{candidate['what_should_change']}\n",
257
+ "## Unified Diff Preview\n```diff",
258
+ candidate["proposed_diff"],
259
+ "```"
260
+ ]
261
+ (b_dir / "minimal_patch_plan.md").write_text("\n".join(plan_content), encoding="utf-8")
262
+
263
+ # 3. metadata.json
264
+ with open(b_dir / "metadata.json", "w", encoding="utf-8") as mf:
265
+ json.dump(candidate, mf, indent=2)
266
+
267
+ # Render run-level remediation.md
268
+ remediation_lines = [
269
+ f"# TorusGuard Governed Remediation Catalog",
270
+ f"**Run ID:** `{run_folder.name}` ",
271
+ f"**Generated:** `{get_ist_now().strftime('%Y-%m-%d %H:%M:%S IST')}` ",
272
+ f"**Candidate Bundles Formulated:** `{len(bundles)}` \n",
273
+ "---",
274
+ "## Formulated Candidate Patches\n"
275
+ ]
276
+
277
+ for b in bundles:
278
+ remediation_lines.append(f"### [{b['rule_id']}] {b['title']}")
279
+ remediation_lines.append(f"- **Target:** `{b['target_file']}:{b['line_number']}` | **Bundle ID:** `{b['bundle_id']}`")
280
+ remediation_lines.append(f"- **Strategy:** {b['what_should_change']}")
281
+ remediation_lines.append(f"- **Ponytail Churn:** `+{b['additions']} / -{b['deletions']}`")
282
+ remediation_lines.append("```diff")
283
+ remediation_lines.append(b["proposed_diff"].strip())
284
+ remediation_lines.append("```\n")
285
+
286
+ (run_folder / "remediation.md").write_text("\n".join(remediation_lines), encoding="utf-8")
287
+
288
+ # Update manifest
289
+ manifest_file = run_folder / "manifest.json"
290
+ if manifest_file.is_file():
291
+ try:
292
+ mdata = json.loads(manifest_file.read_text(encoding="utf-8"))
293
+ mdata["remediation_bundles_count"] = len(bundles)
294
+ manifest_file.write_text(json.dumps(mdata, indent=2), encoding="utf-8")
295
+ except Exception:
296
+ pass
297
+
298
+ # Print 75-column Terminal Card
299
+ print(f"\n {CYAN}╭─────────────────────────────────────────────────────────────────────────╮{RESET}")
300
+ print(f" {CYAN}│ │{RESET}")
301
+ print(format_box_line(f"{BOLD}🛡️ TORUSGUARD GOVERNED REMEDIATION ENGINE v1.3.0{RESET}", border_color=CYAN))
302
+ print(format_box_line(f"{DIM}Autonomous Minimal Patch Formulation & Ponytail Packaging{RESET}", border_color=CYAN))
303
+ print(f" {CYAN}│ │{RESET}")
304
+ print(f" {CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}\n")
305
+
306
+ print(f" {CYAN}┌─ Remediation Scope ─────────────────────────────────────────────────────┐{RESET}")
307
+ print(format_box_line(f"Active Run: {WHITE}{run_folder.name}{RESET}"))
308
+ print(format_box_line(f"Target Scope: {WHITE}{target_root}{RESET}"))
309
+ print(format_box_line(f"Total Findings: {len(findings)} evaluated | {GREEN}{len(bundles)} surgical patches formulated{RESET}"))
310
+ print(format_box_line(f"Ponytail Guard: {GREEN}All patches strictly <= 35 additions, <= 25 deletions{RESET}"))
311
+ print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
312
+
313
+ if bundles:
314
+ print(f" {CYAN}┌─ Formulated Candidate Patches ({len(bundles)}) ───────────────────────────────────┐{RESET}")
315
+ for b in bundles[:5]:
316
+ print(format_box_line(f"{YELLOW}[{b['rule_id']}]{RESET} {WHITE}{b['target_file']}:{b['line_number']}{RESET} (+{b['additions']}/-{b['deletions']})"))
317
+ print(format_box_line(f" └─ {DIM}{b['what_should_change']}{RESET}"))
318
+ if len(bundles) > 5:
319
+ print(format_box_line(f" ... and {len(bundles) - 5} more patches cataloged in remediation.md"))
320
+ print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
321
+ else:
322
+ print(f" {YELLOW}ℹ No automatic patch templates matched current findings.{RESET}\n")
323
+
324
+ print(f" {GREEN}╔═ Next Governed Action ═════════════════════════════════════════════════════╗{RESET}")
325
+ print(format_box_line(f"Review & Apply: {BOLD}{WHITE}npx torusguard apply{RESET} (CLI Human Gate)", border="║", border_color=GREEN))
326
+ print(format_box_line(f"Auto-Apply Flag: {CYAN}npx torusguard apply --yes{RESET} (Automated mode)", border="║", border_color=GREEN))
327
+ print(format_box_line(f"AI IDE Chat: Run {CYAN}/torusguard-apply{RESET} in your AI chat", border="║", border_color=GREEN))
328
+ print(f" {GREEN}╚═════════════════════════════════════════════════════════════════════════════╝{RESET}\n")
329
+
330
+ return {
331
+ "status": "success",
332
+ "run_folder": str(run_folder),
333
+ "bundles_count": len(bundles),
334
+ "bundles": bundles
335
+ }
336
+
337
+
338
+ def main():
339
+ parser = argparse.ArgumentParser(description="TorusGuard Autonomous Governed Remediation Engine")
340
+ parser.add_argument("path", nargs="?", default=".", help="Target project root directory")
341
+ parser.add_argument("--run", "-r", help="Explicit run ID to harden (default: latest audit run)")
342
+ parser.add_argument("--json", action="store_true", help="Output raw JSON")
343
+ args = parser.parse_args()
344
+
345
+ target = Path(args.path).resolve()
346
+ result = execute_harden(target, run_id_arg=args.run)
347
+
348
+ if args.json:
349
+ print(json.dumps(result, indent=2))
350
+
351
+
352
+ if __name__ == "__main__":
353
+ main()
@@ -88,10 +88,12 @@ def load_report_telemetry(root_dir: Path) -> dict[str, Any]:
88
88
  except Exception:
89
89
  stack_info = {"language": "Universal", "framework": "None", "data_layer": "None"}
90
90
 
91
+ ist_tz = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
92
+ now_ist = datetime.datetime.now(ist_tz)
91
93
  return {
92
94
  "project_name": root_dir.name or "Project",
93
95
  "root_dir": str(root_dir),
94
- "generated_at": datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC"),
96
+ "generated_at": now_ist.strftime("%Y-%m-%d %H:%M:%S IST"),
95
97
  "config": cfg,
96
98
  "profile": profile,
97
99
  "patterns": patterns,
@@ -167,7 +169,7 @@ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
167
169
  else:
168
170
  recipe_cards_html = """
169
171
  <div class="empty-state">
170
- <p>No golden fix recipes captured yet. Run <code>/torusguard-apply</code> on verified remediations to distill reusable recipes.</p>
172
+ <p>No golden fix recipes captured yet. Run <code>npx torusguard apply</code> (CLI) or <code>/torusguard-apply</code> (AI Chat) on verified remediations to distill reusable recipes.</p>
171
173
  </div>
172
174
  """
173
175
 
@@ -207,6 +209,19 @@ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
207
209
 
208
210
  stack_info = telemetry.get("detected_stack", {})
209
211
  primary_lang = stack_info.get("language", "Universal")
212
+ if primary_lang in ("Unknown", "None", "", None):
213
+ root_path = Path(telemetry.get("root_dir", "."))
214
+ if (root_path / "tsconfig.json").is_file() or list(root_path.glob("*.ts")) or list((root_path / "src").glob("*.ts*")):
215
+ primary_lang = "TypeScript"
216
+ elif (root_path / "package.json").is_file():
217
+ primary_lang = "JavaScript / TypeScript"
218
+ elif (root_path / "pyproject.toml").is_file() or (root_path / "requirements.txt").is_file() or list(root_path.glob("*.py")):
219
+ primary_lang = "Python"
220
+ elif (root_path / "go.mod").is_file():
221
+ primary_lang = "Go"
222
+ else:
223
+ primary_lang = "Universal Polyglot"
224
+
210
225
  primary_fw = stack_info.get("framework", "None")
211
226
  primary_orm = stack_info.get("data_layer", "None")
212
227
  sub_stacks = stack_info.get("sub_stacks", [])
@@ -514,17 +529,17 @@ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
514
529
  <div class="pipeline-step">
515
530
  <div class="step-circle active">1</div>
516
531
  <div class="step-name">Scan</div>
517
- <div class="step-cmd">/audit</div>
532
+ <div class="step-cmd">audit &middot; /audit</div>
518
533
  </div>
519
534
  <div class="pipeline-step">
520
535
  <div class="step-circle active">2</div>
521
536
  <div class="step-name">Score</div>
522
- <div class="step-cmd">0-100</div>
537
+ <div class="step-cmd">0-100 Score</div>
523
538
  </div>
524
539
  <div class="pipeline-step">
525
540
  <div class="step-circle active">3</div>
526
541
  <div class="step-name">Harden</div>
527
- <div class="step-cmd">/harden</div>
542
+ <div class="step-cmd">harden &middot; /harden</div>
528
543
  </div>
529
544
  <div class="pipeline-step">
530
545
  <div class="step-circle active">4</div>
@@ -534,17 +549,17 @@ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
534
549
  <div class="pipeline-step">
535
550
  <div class="step-circle active">5</div>
536
551
  <div class="step-name">Apply</div>
537
- <div class="step-cmd">.bak Snapshot</div>
552
+ <div class="step-cmd">apply &middot; .bak</div>
538
553
  </div>
539
554
  <div class="pipeline-step">
540
555
  <div class="step-circle active">6</div>
541
556
  <div class="step-name">Recheck</div>
542
- <div class="step-cmd">Differential</div>
557
+ <div class="step-cmd">recheck &middot; verify</div>
543
558
  </div>
544
559
  <div class="pipeline-step">
545
560
  <div class="step-circle active">7</div>
546
561
  <div class="step-name">Report</div>
547
- <div class="step-cmd">SARIF v2.1.0</div>
562
+ <div class="step-cmd">report &middot; SARIF</div>
548
563
  </div>
549
564
  </div>
550
565
  </div>
@@ -578,7 +593,7 @@ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
578
593
  </div>
579
594
 
580
595
  <footer>
581
- TorusGuard v1.2.0 &middot; Autonomous Security Engine for AI-Built Applications &middot; 100% Local-First &middot; Zero Cloud Telemetry Leakage
596
+ TorusGuard v1.3.0 &middot; Autonomous Security Engine for AI-Built Applications &middot; 100% Local-First &middot; Zero Cloud Telemetry Leakage
582
597
  </footer>
583
598
  </div>
584
599
  </body>
@@ -621,7 +636,7 @@ def main():
621
636
  print(json.dumps(res, indent=2))
622
637
  else:
623
638
  print(f"[SUCCESS] Emitted TorusGuard visual dashboard: {res['report_path']}")
624
- print(f"Posture Score: {res['posture_score']}/100 &middot; Size: {res['file_size_bytes']} bytes")
639
+ print(f"Posture Score: {res['posture_score']}/100 · Size: {res['file_size_bytes']} bytes")
625
640
 
626
641
 
627
642
  if __name__ == "__main__":
@@ -29,6 +29,8 @@ def scan_workspace_files(base_dir):
29
29
  for root, dirs, files in os.walk(base_dir):
30
30
  if "runs" in dirs:
31
31
  dirs.remove("runs")
32
+ if "snapshots" in dirs:
33
+ dirs.remove("snapshots")
32
34
  if "memory" in dirs:
33
35
  dirs.remove("memory")
34
36
  if "__pycache__" in dirs:
@@ -1330,5 +1330,9 @@ def main():
1330
1330
  print(f" Fix Velocity Rate: {prof.get('fix_rate_percentage')}%")
1331
1331
 
1332
1332
 
1333
+ # Backward-compatible alias
1334
+ store_golden_recipe = record_golden_recipe
1335
+
1336
+
1333
1337
  if __name__ == "__main__":
1334
1338
  main()