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,643 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard Visual HTML Report Emitter (v1.2.0)
4
+ Compiles a self-contained, zero-external-CDN, interactive dark-mode dashboard.
5
+ Embeds SVG gauges, 7-stage pipeline radar, Golden Fix Recipes, and attack surface analytics.
6
+ Zero external runtime dependencies (100% Python 3.10+ standard library).
7
+ """
8
+
9
+ import sys
10
+ import json
11
+ import html
12
+ import datetime
13
+ import argparse
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ # Windows console UTF-8 support
18
+ if sys.stdout and hasattr(sys.stdout, "reconfigure"):
19
+ try:
20
+ getattr(sys.stdout, "reconfigure")(encoding="utf-8", errors="replace")
21
+ except (AttributeError, OSError):
22
+ pass
23
+ if sys.stderr and hasattr(sys.stderr, "reconfigure"):
24
+ try:
25
+ getattr(sys.stderr, "reconfigure")(encoding="utf-8", errors="replace")
26
+ except (AttributeError, OSError):
27
+ pass
28
+
29
+
30
+ def find_project_root(start_dir: str | Path | None = None) -> Path:
31
+ """Detect project root directory."""
32
+ current = Path(start_dir or Path.cwd()).resolve()
33
+ markers = [".git", "package.json", "pyproject.toml", ".torusguard"]
34
+ for m in markers:
35
+ if (current / m).exists():
36
+ return current
37
+ for parent in current.parents:
38
+ for m in markers:
39
+ if (parent / m).exists():
40
+ return parent
41
+ return current
42
+
43
+
44
+ def load_report_telemetry(root_dir: Path) -> dict[str, Any]:
45
+ """Gather all project telemetry, rules catalog, and memory state."""
46
+ tg_dir = root_dir / ".torusguard"
47
+ memory_dir = tg_dir / "memory"
48
+ config_file = tg_dir / "config" / "torusguard.json"
49
+
50
+ cfg: dict[str, Any] = {}
51
+ if config_file.exists():
52
+ try:
53
+ cfg = json.loads(config_file.read_text(encoding="utf-8"))
54
+ except (json.JSONDecodeError, OSError):
55
+ cfg = {}
56
+
57
+ profile_file = memory_dir / "profile.json"
58
+ patterns_file = memory_dir / "patterns.json"
59
+ context_file = memory_dir / "context.json"
60
+
61
+ profile: dict[str, Any] = {}
62
+ patterns: list[dict[str, Any]] = []
63
+ context: dict[str, Any] = {}
64
+
65
+ if profile_file.exists():
66
+ try:
67
+ profile = json.loads(profile_file.read_text(encoding="utf-8"))
68
+ except (json.JSONDecodeError, OSError):
69
+ profile = {}
70
+
71
+ if patterns_file.exists():
72
+ try:
73
+ patterns = json.loads(patterns_file.read_text(encoding="utf-8"))
74
+ except (json.JSONDecodeError, OSError):
75
+ patterns = []
76
+
77
+ if context_file.exists():
78
+ try:
79
+ context = json.loads(context_file.read_text(encoding="utf-8"))
80
+ except (json.JSONDecodeError, OSError):
81
+ context = {}
82
+
83
+ stack_info = cfg.get("detected_stack", {})
84
+ if not stack_info:
85
+ try:
86
+ import stack_detect # type: ignore
87
+ stack_info = stack_detect.detect_stack(root_dir)
88
+ except Exception:
89
+ stack_info = {"language": "Universal", "framework": "None", "data_layer": "None"}
90
+
91
+ ist_tz = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
92
+ now_ist = datetime.datetime.now(ist_tz)
93
+ return {
94
+ "project_name": root_dir.name or "Project",
95
+ "root_dir": str(root_dir),
96
+ "generated_at": now_ist.strftime("%Y-%m-%d %H:%M:%S IST"),
97
+ "config": cfg,
98
+ "profile": profile,
99
+ "patterns": patterns,
100
+ "context": context,
101
+ "detected_stack": stack_info
102
+ }
103
+
104
+
105
+ def compute_posture_score(telemetry: dict[str, Any]) -> int:
106
+ """Calculate an overall health score (0-100) based on verified fixes and regressions."""
107
+ profile = telemetry.get("profile", {})
108
+ patterns = telemetry.get("patterns", [])
109
+
110
+ base_score = 85
111
+ fix_rate = profile.get("fix_rate_percentage")
112
+ if fix_rate is not None:
113
+ base_score = int(fix_rate)
114
+
115
+ regressions = sum(1 for p in patterns if p.get("pattern_type") == "regression_watch")
116
+ recipes = sum(1 for p in patterns if p.get("pattern_type") == "golden_fix_recipe")
117
+
118
+ score = base_score - (regressions * 5) + (recipes * 3)
119
+ return min(max(score, 10), 100)
120
+
121
+
122
+ def generate_html_dashboard(telemetry: dict[str, Any]) -> str:
123
+ """Build a complete, standalone, single-file HTML dashboard."""
124
+ score = compute_posture_score(telemetry)
125
+ profile = telemetry.get("profile", {})
126
+ patterns = telemetry.get("patterns", [])
127
+ cfg = telemetry.get("config", {})
128
+ detected = cfg.get("detected_stack", {})
129
+ stack = profile.get("stack", [])
130
+ if not stack and detected.get("language") and detected.get("language") != "Unknown":
131
+ stack = [detected["language"]]
132
+ if detected.get("framework") and detected["framework"] != "None":
133
+ stack.append(detected["framework"])
134
+ stack_label = ", ".join(stack) if stack else "Universal Polyglot Application"
135
+
136
+ recipes = [p for p in patterns if p.get("pattern_type") == "golden_fix_recipe"]
137
+ regressions = [p for p in patterns if p.get("pattern_type") == "regression_watch"]
138
+ false_positives = [p for p in patterns if p.get("pattern_type") == "false_positive_class"]
139
+ recurring = [p for p in patterns if p.get("pattern_type") in ("recurring_fix", "common_vulnerability")]
140
+
141
+ # SVG Circle Math for Gauge (radius 40, circ = 251.2)
142
+ circumference = 251.2
143
+ dash_offset = circumference - (circumference * (score / 100.0))
144
+ score_color = "#3fb950" if score >= 80 else ("#d29922" if score >= 60 else "#f85149")
145
+
146
+ # Generate recipe cards HTML
147
+ recipe_cards_html = ""
148
+ if recipes:
149
+ for r in recipes[:6]:
150
+ rule = html.escape(r.get("rule_id", "TG-SEC"))
151
+ desc = html.escape(r.get("description", "Verified fix"))
152
+ rdata = r.get("recipe_data", {})
153
+ diff = html.escape(rdata.get("diff_snippet", ""))
154
+ verified = rdata.get("verified_count", 1)
155
+ metrics = rdata.get("ponytail_metrics", {})
156
+ adds = metrics.get("additions", 0)
157
+ dels = metrics.get("deletions", 0)
158
+
159
+ recipe_cards_html += f"""
160
+ <div class="card recipe-card">
161
+ <div class="card-header">
162
+ <span class="badge badge-success">{rule}</span>
163
+ <span class="meta-label">Verified {verified}x &middot; +{adds}/-{dels} Ponytail</span>
164
+ </div>
165
+ <p class="recipe-desc">{desc}</p>
166
+ <pre class="diff-block"><code>{diff}</code></pre>
167
+ </div>
168
+ """
169
+ else:
170
+ recipe_cards_html = """
171
+ <div class="empty-state">
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>
173
+ </div>
174
+ """
175
+
176
+ # Generate regression watch & suppression rows
177
+ regression_rows_html = ""
178
+ combined_watches: list[dict[str, str]] = []
179
+ for reg in regressions:
180
+ combined_watches.append({
181
+ "badge_class": "badge-danger",
182
+ "rule": reg.get("rule_id", "TG-REG"),
183
+ "desc": reg.get("description", "Active watch"),
184
+ "target": ", ".join(reg.get("affected_files", [])) or "Monitored files"
185
+ })
186
+ for fp in false_positives:
187
+ combined_watches.append({
188
+ "badge_class": "badge-warning",
189
+ "rule": fp.get("rule_id", "TG-FP"),
190
+ "desc": fp.get("description", "False positive suppression"),
191
+ "target": fp.get("reason") or "Suppressed rule pattern"
192
+ })
193
+
194
+ if combined_watches:
195
+ for item in combined_watches:
196
+ rule = html.escape(item["rule"])
197
+ desc = html.escape(item["desc"])
198
+ target = html.escape(item["target"])
199
+ bclass = item["badge_class"]
200
+ regression_rows_html += f"""
201
+ <tr class="reg-row">
202
+ <td><span class="badge {bclass}">{rule}</span></td>
203
+ <td>{desc}</td>
204
+ <td><code>{target}</code></td>
205
+ </tr>
206
+ """
207
+ else:
208
+ regression_rows_html = "<tr><td colspan='3' class='empty-cell'>Zero active security regressions or suppressions. Posture clean.</td></tr>"
209
+
210
+ stack_info = telemetry.get("detected_stack", {})
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
+
225
+ primary_fw = stack_info.get("framework", "None")
226
+ primary_orm = stack_info.get("data_layer", "None")
227
+ sub_stacks = stack_info.get("sub_stacks", [])
228
+
229
+ chips = [f'<span class="chip chip-primary">&#9679; {html.escape(primary_lang)}</span>']
230
+ if primary_fw and primary_fw != "None":
231
+ chips.append(f'<span class="chip">&#9881; {html.escape(primary_fw)}</span>')
232
+ if primary_orm and primary_orm != "None":
233
+ chips.append(f'<span class="chip">&#128451; {html.escape(primary_orm)}</span>')
234
+ for pkg in sub_stacks[:8]:
235
+ pkg_p = html.escape(pkg.get("path", ""))
236
+ pkg_l = html.escape(pkg.get("language", ""))
237
+ pkg_f = html.escape(pkg.get("framework", ""))
238
+ chips.append(f'<span class="chip" style="border-color: rgba(63, 185, 80, 0.4); color: var(--accent-green);">&#128230; {pkg_p} ({pkg_l} / {pkg_f})</span>')
239
+ stack_chips_html = "\n".join(chips)
240
+
241
+ return f"""<!DOCTYPE html>
242
+ <html lang="en">
243
+ <head>
244
+ <meta charset="UTF-8">
245
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
246
+ <title>TorusGuard Security Posture &middot; {html.escape(telemetry['project_name'])}</title>
247
+ <style>
248
+ :root {{
249
+ --bg-dark: #0d1117;
250
+ --bg-surface: #161b22;
251
+ --bg-card: #21262d;
252
+ --border-subtle: #30363d;
253
+ --text-main: #f0f6fc;
254
+ --text-muted: #8b949e;
255
+ --accent-blue: #58a6ff;
256
+ --accent-green: #3fb950;
257
+ --accent-yellow: #d29922;
258
+ --accent-red: #f85149;
259
+ --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji";
260
+ }}
261
+ * {{ box-sizing: border-box; margin: 0; padding: 0; }}
262
+ body {{
263
+ background: var(--bg-dark);
264
+ color: var(--text-main);
265
+ font-family: var(--font-stack);
266
+ line-height: 1.5;
267
+ padding: 24px;
268
+ }}
269
+ .container {{ max-width: 1200px; margin: 0 auto; }}
270
+ header {{
271
+ display: flex;
272
+ justify-content: space-between;
273
+ align-items: center;
274
+ padding-bottom: 20px;
275
+ border-bottom: 1px solid var(--border-subtle);
276
+ margin-bottom: 24px;
277
+ }}
278
+ .logo-group h1 {{
279
+ font-size: 24px;
280
+ font-weight: 700;
281
+ display: flex;
282
+ align-items: center;
283
+ gap: 8px;
284
+ }}
285
+ .shield-icon {{ font-size: 26px; }}
286
+ .project-meta {{ color: var(--text-muted); font-size: 14px; margin-top: 4px; }}
287
+ .status-badge {{
288
+ background: rgba(63, 185, 80, 0.15);
289
+ border: 1px solid var(--accent-green);
290
+ color: var(--accent-green);
291
+ padding: 6px 14px;
292
+ border-radius: 20px;
293
+ font-size: 13px;
294
+ font-weight: 600;
295
+ }}
296
+ .grid-top {{
297
+ display: grid;
298
+ grid-template-columns: 280px 1fr 1fr 1fr;
299
+ gap: 16px;
300
+ margin-bottom: 24px;
301
+ }}
302
+ @media (max-width: 900px) {{
303
+ .grid-top {{ grid-template-columns: 1fr 1fr; }}
304
+ }}
305
+ @media (max-width: 600px) {{
306
+ .grid-top {{ grid-template-columns: 1fr; }}
307
+ }}
308
+ .card {{
309
+ background: var(--bg-surface);
310
+ border: 1px solid var(--border-subtle);
311
+ border-radius: 8px;
312
+ padding: 20px;
313
+ }}
314
+ .gauge-card {{
315
+ display: flex;
316
+ flex-direction: column;
317
+ align-items: center;
318
+ justify-content: center;
319
+ text-align: center;
320
+ }}
321
+ .gauge-svg {{ width: 120px; height: 120px; transform: rotate(-90deg); }}
322
+ .gauge-bg {{ fill: none; stroke: var(--bg-card); stroke-width: 8; }}
323
+ .gauge-fill {{
324
+ fill: none;
325
+ stroke: {score_color};
326
+ stroke-width: 8;
327
+ stroke-linecap: round;
328
+ stroke-dasharray: 251.2;
329
+ stroke-dashoffset: {dash_offset};
330
+ transition: stroke-dashoffset 0.8s ease;
331
+ }}
332
+ .gauge-value {{
333
+ position: absolute;
334
+ font-size: 32px;
335
+ font-weight: 700;
336
+ color: var(--text-main);
337
+ }}
338
+ .stat-label {{ font-size: 13px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; }}
339
+ .stat-value {{ font-size: 28px; font-weight: 700; color: var(--text-main); }}
340
+ .stat-sub {{ font-size: 12px; color: var(--text-muted); margin-top: 6px; }}
341
+ .pipeline-section {{ margin-bottom: 24px; }}
342
+ .pipeline-tracker {{
343
+ display: flex;
344
+ justify-content: space-between;
345
+ position: relative;
346
+ margin-top: 16px;
347
+ }}
348
+ .pipeline-step {{
349
+ display: flex;
350
+ flex-direction: column;
351
+ align-items: center;
352
+ flex: 1;
353
+ text-align: center;
354
+ position: relative;
355
+ z-index: 2;
356
+ }}
357
+ .step-circle {{
358
+ width: 32px;
359
+ height: 32px;
360
+ border-radius: 50%;
361
+ background: var(--bg-card);
362
+ border: 2px solid var(--accent-blue);
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: center;
366
+ font-size: 13px;
367
+ font-weight: 700;
368
+ color: var(--accent-blue);
369
+ margin-bottom: 8px;
370
+ }}
371
+ .step-circle.active {{ background: var(--accent-blue); color: var(--bg-dark); }}
372
+ .step-name {{ font-size: 12px; font-weight: 600; color: var(--text-main); }}
373
+ .step-cmd {{ font-size: 11px; color: var(--text-muted); font-family: monospace; margin-top: 2px; }}
374
+ .pipeline-line {{
375
+ position: absolute;
376
+ top: 16px;
377
+ left: 6%;
378
+ right: 6%;
379
+ height: 2px;
380
+ background: var(--border-subtle);
381
+ z-index: 1;
382
+ }}
383
+ .polyglot-chips {{
384
+ display: flex;
385
+ flex-wrap: wrap;
386
+ gap: 8px;
387
+ margin-top: 10px;
388
+ }}
389
+ .chip {{
390
+ background: var(--bg-card);
391
+ border: 1px solid var(--border-subtle);
392
+ border-radius: 16px;
393
+ padding: 4px 12px;
394
+ font-size: 12px;
395
+ font-weight: 500;
396
+ color: var(--text-main);
397
+ display: inline-flex;
398
+ align-items: center;
399
+ gap: 6px;
400
+ }}
401
+ .chip-primary {{
402
+ border-color: var(--accent-blue);
403
+ color: var(--accent-blue);
404
+ background: rgba(88, 166, 255, 0.1);
405
+ }}
406
+ .grid-bottom {{
407
+ display: grid;
408
+ grid-template-columns: 1fr 1fr;
409
+ gap: 24px;
410
+ margin-top: 24px;
411
+ }}
412
+ @media (max-width: 800px) {{
413
+ .grid-bottom {{ grid-template-columns: 1fr; }}
414
+ }}
415
+ .section-title {{
416
+ font-size: 18px;
417
+ font-weight: 600;
418
+ margin-bottom: 16px;
419
+ display: flex;
420
+ align-items: center;
421
+ gap: 8px;
422
+ }}
423
+ .badge {{
424
+ display: inline-block;
425
+ padding: 3px 8px;
426
+ border-radius: 12px;
427
+ font-size: 11px;
428
+ font-weight: 600;
429
+ font-family: monospace;
430
+ }}
431
+ .badge-success {{ background: rgba(63, 185, 80, 0.15); color: var(--accent-green); border: 1px solid rgba(63, 185, 80, 0.3); }}
432
+ .badge-danger {{ background: rgba(248, 81, 73, 0.15); color: var(--accent-red); border: 1px solid rgba(248, 81, 73, 0.3); }}
433
+ .recipe-card {{
434
+ background: var(--bg-card);
435
+ border: 1px solid var(--border-subtle);
436
+ border-radius: 6px;
437
+ padding: 12px;
438
+ margin-bottom: 12px;
439
+ }}
440
+ .card-header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }}
441
+ .meta-label {{ font-size: 11px; color: var(--text-muted); }}
442
+ .recipe-desc {{ font-size: 13px; color: var(--text-main); margin-bottom: 8px; }}
443
+ .diff-block {{
444
+ background: var(--bg-dark);
445
+ border: 1px solid var(--border-subtle);
446
+ border-radius: 4px;
447
+ padding: 8px 12px;
448
+ font-family: monospace;
449
+ font-size: 12px;
450
+ overflow-x: auto;
451
+ color: #e6edf3;
452
+ }}
453
+ table {{ width: 100%; border-collapse: collapse; }}
454
+ th, td {{ padding: 10px 12px; text-align: left; font-size: 13px; }}
455
+ th {{ color: var(--text-muted); border-bottom: 1px solid var(--border-subtle); font-weight: 600; font-size: 12px; text-transform: uppercase; }}
456
+ td {{ border-bottom: 1px solid rgba(48, 54, 61, 0.5); }}
457
+ .empty-state {{ text-align: center; padding: 24px; color: var(--text-muted); font-size: 13px; }}
458
+ .empty-cell {{ text-align: center; color: var(--text-muted); padding: 16px; font-style: italic; }}
459
+ footer {{
460
+ margin-top: 40px;
461
+ text-align: center;
462
+ color: var(--text-muted);
463
+ font-size: 12px;
464
+ border-top: 1px solid var(--border-subtle);
465
+ padding-top: 20px;
466
+ }}
467
+ </style>
468
+ </head>
469
+ <body>
470
+ <div class="container">
471
+ <header>
472
+ <div class="logo-group">
473
+ <h1><span class="shield-icon">&#128737;</span> TorusGuard Security Posture</h1>
474
+ <div class="project-meta">Workspace: <strong>{html.escape(telemetry['project_name'])}</strong> &middot; Stack: <strong>{html.escape(stack_label)}</strong> &middot; {telemetry['generated_at']}</div>
475
+ </div>
476
+ <div class="status-badge">&check; Local Governance Active</div>
477
+ </header>
478
+
479
+ <!-- Top KPI Grid -->
480
+ <div class="grid-top">
481
+ <div class="card gauge-card">
482
+ <div style="position: relative; display: flex; align-items: center; justify-content: center;">
483
+ <svg class="gauge-svg" viewBox="0 0 100 100">
484
+ <circle class="gauge-bg" cx="50" cy="50" r="40" />
485
+ <circle class="gauge-fill" cx="50" cy="50" r="40" />
486
+ </svg>
487
+ <div class="gauge-value">{score}</div>
488
+ </div>
489
+ <div class="stat-label" style="margin-top: 12px;">Security Posture Score</div>
490
+ </div>
491
+
492
+ <div class="card">
493
+ <div class="stat-label">Verified Golden Recipes</div>
494
+ <div class="stat-value" style="color: var(--accent-green);">{len(recipes)}</div>
495
+ <div class="stat-sub">Distilled Ponytail fixes (<=35 add, <=25 del)</div>
496
+ </div>
497
+
498
+ <div class="card">
499
+ <div class="stat-label">Active Memory Patterns</div>
500
+ <div class="stat-value">{len(patterns)}</div>
501
+ <div class="stat-sub">{len(false_positives)} suppressed &middot; {len(recurring)} recurring</div>
502
+ </div>
503
+
504
+ <div class="card">
505
+ <div class="stat-label">Security Rules Catalog</div>
506
+ <div class="stat-value" style="color: var(--accent-blue);">71</div>
507
+ <div class="stat-sub">11 rule families &middot; Multi-agent verified</div>
508
+ </div>
509
+ </div>
510
+
511
+ <!-- Polyglot Architecture & Workspace Intelligence -->
512
+ <div class="card" style="margin-bottom: 24px;">
513
+ <div class="section-title">&#9670; Polyglot Architecture &amp; Workspace Intelligence</div>
514
+ <div style="font-size: 13px; color: var(--text-muted); margin-bottom: 12px;">
515
+ Primary Language: <strong style="color: var(--text-main);">{html.escape(primary_lang)}</strong>
516
+ &middot; Framework: <strong style="color: var(--text-main);">{html.escape(primary_fw)}</strong>
517
+ &middot; Data Layer: <strong style="color: var(--text-main);">{html.escape(primary_orm)}</strong>
518
+ </div>
519
+ <div class="polyglot-chips">
520
+ {stack_chips_html}
521
+ </div>
522
+ </div>
523
+
524
+ <!-- 7-Stage Canonical Pipeline Tracker -->
525
+ <div class="card pipeline-section">
526
+ <div class="section-title">&#9658; Canonical 7-Stage Closed-Loop Governance Flow</div>
527
+ <div class="pipeline-tracker">
528
+ <div class="pipeline-line"></div>
529
+ <div class="pipeline-step">
530
+ <div class="step-circle active">1</div>
531
+ <div class="step-name">Scan</div>
532
+ <div class="step-cmd">audit &middot; /audit</div>
533
+ </div>
534
+ <div class="pipeline-step">
535
+ <div class="step-circle active">2</div>
536
+ <div class="step-name">Score</div>
537
+ <div class="step-cmd">0-100 Score</div>
538
+ </div>
539
+ <div class="pipeline-step">
540
+ <div class="step-circle active">3</div>
541
+ <div class="step-name">Harden</div>
542
+ <div class="step-cmd">harden &middot; /harden</div>
543
+ </div>
544
+ <div class="pipeline-step">
545
+ <div class="step-circle active">4</div>
546
+ <div class="step-name">Authorize</div>
547
+ <div class="step-cmd">Human Gate</div>
548
+ </div>
549
+ <div class="pipeline-step">
550
+ <div class="step-circle active">5</div>
551
+ <div class="step-name">Apply</div>
552
+ <div class="step-cmd">apply &middot; .bak</div>
553
+ </div>
554
+ <div class="pipeline-step">
555
+ <div class="step-circle active">6</div>
556
+ <div class="step-name">Recheck</div>
557
+ <div class="step-cmd">recheck &middot; verify</div>
558
+ </div>
559
+ <div class="pipeline-step">
560
+ <div class="step-circle active">7</div>
561
+ <div class="step-name">Report</div>
562
+ <div class="step-cmd">report &middot; SARIF</div>
563
+ </div>
564
+ </div>
565
+ </div>
566
+
567
+ <!-- Bottom Details Grid -->
568
+ <div class="grid-bottom">
569
+ <!-- Golden Recipes Catalog -->
570
+ <div class="card">
571
+ <div class="section-title">&#10024; Active Golden Fix Recipes ({len(recipes)})</div>
572
+ <div class="recipes-list">
573
+ {recipe_cards_html}
574
+ </div>
575
+ </div>
576
+
577
+ <!-- Regression Watches & False Positives -->
578
+ <div class="card">
579
+ <div class="section-title">&#128065; Regression Watches & Suppressions</div>
580
+ <table>
581
+ <thead>
582
+ <tr>
583
+ <th>Rule ID</th>
584
+ <th>Description / Reason</th>
585
+ <th>Target Path</th>
586
+ </tr>
587
+ </thead>
588
+ <tbody>
589
+ {regression_rows_html}
590
+ </tbody>
591
+ </table>
592
+ </div>
593
+ </div>
594
+
595
+ <footer>
596
+ TorusGuard v1.3.0 &middot; Autonomous Security Engine for AI-Built Applications &middot; 100% Local-First &middot; Zero Cloud Telemetry Leakage
597
+ </footer>
598
+ </div>
599
+ </body>
600
+ </html>
601
+ """
602
+
603
+
604
+ def emit_html_report(
605
+ target_path: str | Path | None = None,
606
+ root_dir: str | Path | None = None
607
+ ) -> dict[str, Any]:
608
+ """Emit the standalone HTML report to disk."""
609
+ root = Path(root_dir or find_project_root()).resolve()
610
+ telemetry = load_report_telemetry(root)
611
+ html_content = generate_html_dashboard(telemetry)
612
+
613
+ out_file = Path(target_path) if target_path else (root / ".torusguard" / "runs" / "report-latest.html")
614
+ out_file.parent.mkdir(parents=True, exist_ok=True)
615
+ out_file.write_text(html_content, encoding="utf-8")
616
+
617
+ return {
618
+ "status": "success",
619
+ "report_path": str(out_file),
620
+ "file_size_bytes": len(html_content.encode("utf-8")),
621
+ "posture_score": compute_posture_score(telemetry)
622
+ }
623
+
624
+
625
+ def main():
626
+ parser = argparse.ArgumentParser(description="TorusGuard Visual HTML Report Emitter")
627
+ parser.add_argument("--out", help="Output HTML file path (default: .torusguard/runs/report-latest.html)")
628
+ parser.add_argument("--root", help="Project root override")
629
+ parser.add_argument("--json", action="store_true", help="Output raw JSON summary")
630
+
631
+ args = parser.parse_args()
632
+ root = Path(args.root).resolve() if args.root else None
633
+
634
+ res = emit_html_report(target_path=args.out, root_dir=root)
635
+ if args.json:
636
+ print(json.dumps(res, indent=2))
637
+ else:
638
+ print(f"[SUCCESS] Emitted TorusGuard visual dashboard: {res['report_path']}")
639
+ print(f"Posture Score: {res['posture_score']}/100 · Size: {res['file_size_bytes']} bytes")
640
+
641
+
642
+ if __name__ == "__main__":
643
+ main()
@@ -13,12 +13,12 @@ from pathlib import Path
13
13
 
14
14
 
15
15
  def compute_file_sha256(filepath):
16
- """Compute SHA-256 digest of a file in binary mode."""
17
- hasher = hashlib.sha256()
16
+ """Compute SHA-256 digest of a file, normalizing line endings for cross-platform determinism."""
18
17
  with open(filepath, "rb") as fh:
19
- while chunk := fh.read(65536):
20
- hasher.update(chunk)
21
- return hasher.hexdigest()
18
+ content = fh.read()
19
+ # Normalize CRLF to LF so manifests match identically on Windows, Linux, and macOS
20
+ content = content.replace(b"\r\n", b"\n")
21
+ return hashlib.sha256(content).hexdigest()
22
22
 
23
23
 
24
24
  def scan_workspace_files(base_dir):
@@ -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: