torusguard 1.3.4 → 1.3.5

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 (239) hide show
  1. package/.torusguard/.manifest.json +66 -63
  2. package/.torusguard/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +30 -14
  3. package/.torusguard/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +33 -16
  4. package/.torusguard/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +31 -15
  5. package/.torusguard/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +26 -16
  6. package/.torusguard/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +24 -16
  7. package/.torusguard/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +32 -18
  8. package/.torusguard/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +24 -17
  9. package/.torusguard/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +31 -18
  10. package/.torusguard/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +28 -17
  11. package/.torusguard/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +33 -17
  12. package/.torusguard/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +31 -16
  13. package/.torusguard/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +25 -16
  14. package/.torusguard/rules/secrets/TG-SEC-005-secret-in-git-history.md +24 -19
  15. package/.torusguard/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +36 -19
  16. package/.torusguard/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +21 -16
  17. package/.torusguard/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +45 -18
  18. package/.torusguard/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +20 -16
  19. package/.torusguard/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +28 -19
  20. package/.torusguard/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +25 -18
  21. package/.torusguard/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +29 -19
  22. package/.torusguard/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +23 -18
  23. package/.torusguard/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +23 -18
  24. package/.torusguard/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +36 -16
  25. package/.torusguard/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +26 -17
  26. package/.torusguard/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +30 -16
  27. package/.torusguard/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +25 -16
  28. package/.torusguard/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +28 -17
  29. package/.torusguard/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +31 -17
  30. package/.torusguard/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +31 -17
  31. package/.torusguard/rules_catalog.json +520 -0
  32. package/.torusguard/runs/report-latest.html +93 -7
  33. package/.torusguard/runs/run-20260912-214110-audit/findings.json +1256 -0
  34. package/.torusguard/runs/run-20260912-214110-audit/findings.md +534 -0
  35. package/.torusguard/runs/run-20260912-214110-audit/manifest.json +12 -0
  36. package/.torusguard/runs/run-20260912-214110-audit/summary.md +5 -0
  37. package/.torusguard/runs/run-20260912-214813-audit/findings.json +992 -0
  38. package/.torusguard/runs/run-20260912-214813-audit/findings.md +422 -0
  39. package/.torusguard/runs/run-20260912-214813-audit/manifest.json +12 -0
  40. package/.torusguard/runs/run-20260912-214813-audit/summary.md +5 -0
  41. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-auth-003-49-7c96f3/metadata.json +17 -0
  42. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-auth-003-49-7c96f3/minimal_patch_plan.md +21 -0
  43. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-auth-003-49-7c96f3/patch.diff +9 -0
  44. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +17 -0
  45. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
  46. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
  47. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-platform-001-12-cf8156/metadata.json +17 -0
  48. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-platform-001-12-cf8156/minimal_patch_plan.md +21 -0
  49. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-platform-001-12-cf8156/patch.diff +9 -0
  50. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-rate-001-26-affcfa/metadata.json +17 -0
  51. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-rate-001-26-affcfa/minimal_patch_plan.md +21 -0
  52. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-rate-001-26-affcfa/patch.diff +9 -0
  53. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-sec-001-9-e22e50/metadata.json +17 -0
  54. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-sec-001-9-e22e50/minimal_patch_plan.md +21 -0
  55. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-sec-001-9-e22e50/patch.diff +9 -0
  56. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-ssrf-004-9-9803ad/metadata.json +17 -0
  57. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-ssrf-004-9-9803ad/minimal_patch_plan.md +21 -0
  58. package/.torusguard/runs/run-20260912-215555-audit/bundles/bnd-tg-ssrf-004-9-9803ad/patch.diff +9 -0
  59. package/.torusguard/runs/run-20260912-215555-audit/findings.json +530 -0
  60. package/.torusguard/runs/run-20260912-215555-audit/findings.md +232 -0
  61. package/.torusguard/runs/run-20260912-215555-audit/manifest.json +13 -0
  62. package/.torusguard/runs/run-20260912-215555-audit/remediation.md +102 -0
  63. package/.torusguard/runs/run-20260912-215555-audit/summary.md +5 -0
  64. package/.torusguard/runs/run-20260912-215731-audit/apply_plan.md +6 -0
  65. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-client-001-7-587129/metadata.json +17 -0
  66. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-client-001-7-587129/minimal_patch_plan.md +21 -0
  67. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-client-001-7-587129/patch.diff +9 -0
  68. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-db-002-9-5f7850/metadata.json +17 -0
  69. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-db-002-9-5f7850/minimal_patch_plan.md +21 -0
  70. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-db-002-9-5f7850/patch.diff +9 -0
  71. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-001-27-2486f1/metadata.json +17 -0
  72. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-001-27-2486f1/minimal_patch_plan.md +22 -0
  73. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-001-27-2486f1/patch.diff +10 -0
  74. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +17 -0
  75. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
  76. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
  77. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-001-12-cf8156/metadata.json +17 -0
  78. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-001-12-cf8156/minimal_patch_plan.md +21 -0
  79. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-001-12-cf8156/patch.diff +9 -0
  80. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-17-876973/metadata.json +17 -0
  81. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-17-876973/minimal_patch_plan.md +23 -0
  82. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-17-876973/patch.diff +11 -0
  83. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-5-40c794/metadata.json +17 -0
  84. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-5-40c794/minimal_patch_plan.md +23 -0
  85. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-platform-002-5-40c794/patch.diff +11 -0
  86. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-001-26-affcfa/metadata.json +17 -0
  87. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-001-26-affcfa/minimal_patch_plan.md +21 -0
  88. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-001-26-affcfa/patch.diff +9 -0
  89. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-002-54-0b38d2/metadata.json +17 -0
  90. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-002-54-0b38d2/minimal_patch_plan.md +21 -0
  91. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-rate-002-54-0b38d2/patch.diff +9 -0
  92. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-18-a3c1e8/metadata.json +17 -0
  93. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-18-a3c1e8/minimal_patch_plan.md +21 -0
  94. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-18-a3c1e8/patch.diff +9 -0
  95. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-9-e22e50/metadata.json +17 -0
  96. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-9-e22e50/minimal_patch_plan.md +21 -0
  97. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-001-9-e22e50/patch.diff +9 -0
  98. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-28-a1a0a1/metadata.json +17 -0
  99. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-28-a1a0a1/minimal_patch_plan.md +21 -0
  100. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-28-a1a0a1/patch.diff +9 -0
  101. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-30-00569e/metadata.json +17 -0
  102. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-30-00569e/minimal_patch_plan.md +21 -0
  103. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-sec-004-30-00569e/patch.diff +9 -0
  104. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-ssrf-004-9-9803ad/metadata.json +17 -0
  105. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-ssrf-004-9-9803ad/minimal_patch_plan.md +21 -0
  106. package/.torusguard/runs/run-20260912-215731-audit/bundles/bnd-tg-ssrf-004-9-9803ad/patch.diff +9 -0
  107. package/.torusguard/runs/run-20260912-215731-audit/diff_summary.md +186 -0
  108. package/.torusguard/runs/run-20260912-215731-audit/findings.json +497 -0
  109. package/.torusguard/runs/run-20260912-215731-audit/findings.md +218 -0
  110. package/.torusguard/runs/run-20260912-215731-audit/manifest.json +14 -0
  111. package/.torusguard/runs/run-20260912-215731-audit/recheck.md +76 -0
  112. package/.torusguard/runs/run-20260912-215731-audit/remediation.md +234 -0
  113. package/.torusguard/runs/run-20260912-215731-audit/summary.md +5 -0
  114. package/.torusguard/runs/run-20260912-220134-audit/findings.json +68 -0
  115. package/.torusguard/runs/run-20260912-220134-audit/findings.md +36 -0
  116. package/.torusguard/runs/run-20260912-220134-audit/manifest.json +12 -0
  117. package/.torusguard/runs/run-20260912-220134-audit/summary.md +5 -0
  118. package/.torusguard/runs/run-20260912-220200-audit/findings.json +1 -0
  119. package/.torusguard/runs/run-20260912-220200-audit/findings.md +8 -0
  120. package/.torusguard/runs/run-20260912-220200-audit/manifest.json +12 -0
  121. package/.torusguard/runs/run-20260912-220200-audit/recheck.md +6 -0
  122. package/.torusguard/runs/run-20260912-220200-audit/summary.md +5 -0
  123. package/.torusguard/runs/run-20260912-220227-audit/findings.json +1 -0
  124. package/.torusguard/runs/run-20260912-220227-audit/findings.md +8 -0
  125. package/.torusguard/runs/run-20260912-220227-audit/manifest.json +12 -0
  126. package/.torusguard/runs/run-20260912-220227-audit/recheck.md +6 -0
  127. package/.torusguard/runs/run-20260912-220227-audit/summary.md +5 -0
  128. package/.torusguard/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
  129. package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
  130. package/.torusguard/scripts/__pycache__/report_sync.cpython-311.pyc +0 -0
  131. package/.torusguard/scripts/__pycache__/term_ui.cpython-311.pyc +0 -0
  132. package/.torusguard/scripts/apply_runner.py +383 -377
  133. package/.torusguard/scripts/audit_runner.py +1306 -653
  134. package/.torusguard/scripts/finding_scorer.py +14 -2
  135. package/.torusguard/scripts/harden_runner.py +628 -455
  136. package/.torusguard/scripts/recheck_runner.py +248 -238
  137. package/.torusguard/scripts/recipes_runner.py +137 -137
  138. package/.torusguard/scripts/report_sync.py +423 -0
  139. package/.torusguard/scripts/sarif_exporter.py +116 -116
  140. package/.torusguard/scripts/term_ui.py +162 -162
  141. package/.torusguard/snapshots/run-20260912-215731-audit/demo/playground/vulnerable_nextjs/actions.ts.bak +24 -0
  142. package/.torusguard/snapshots/run-20260912-215731-audit/examples/hardened-react-express/server/index.js.bak +121 -0
  143. package/.torusguard/snapshots/run-20260912-215731-audit/examples/python/fastapi-vuln/main.py.bak +20 -0
  144. package/.torusguard/snapshots/run-20260912-215731-audit/examples/vulnerable-react-express/client/src/App.jsx.bak +63 -0
  145. package/.torusguard/snapshots/run-20260912-215731-audit/examples/vulnerable-react-express/client/vite.config.js.bak +12 -0
  146. package/.torusguard/snapshots/run-20260912-215731-audit/examples/vulnerable-react-express/server/index.js.bak +82 -0
  147. package/.torusguard/workflows/apply.md +11 -4
  148. package/.torusguard/workflows/audit.md +7 -1
  149. package/.torusguard/workflows/authorize.md +2 -1
  150. package/.torusguard/workflows/exploit-check.md +2 -1
  151. package/.torusguard/workflows/harden.md +7 -1
  152. package/.torusguard/workflows/init.md +6 -1
  153. package/.torusguard/workflows/memory.md +2 -1
  154. package/.torusguard/workflows/recheck.md +7 -1
  155. package/.torusguard/workflows/report.md +7 -1
  156. package/.torusguard/workflows/status.md +6 -1
  157. package/.torusguard/workflows/verify.md +6 -1
  158. package/.torusguard/workflows/web-validate.md +2 -1
  159. package/README.md +470 -345
  160. package/bin/torusguard.js +550 -545
  161. package/package.json +1 -1
  162. package/skills/torusguard/SKILL.md +73 -72
  163. package/skills/torusguard/payload/.manifest.json +66 -63
  164. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-001-client-controlled-sensitive-business-value.md +30 -14
  165. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-002-replayable-one-time-operation.md +33 -16
  166. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-003-missing-workflow-state-validation.md +31 -15
  167. package/skills/torusguard/payload/rules/business-logic/TG-BIZ-004-unrestricted-sensitive-business-flow.md +26 -16
  168. package/skills/torusguard/payload/rules/cache/TG-CACHE-001-sensitive-response-publicly-cacheable.md +24 -16
  169. package/skills/torusguard/payload/rules/cache/TG-CACHE-002-missing-user-cache-isolation.md +32 -18
  170. package/skills/torusguard/payload/rules/cache/TG-CACHE-003-sensitive-data-in-url.md +24 -17
  171. package/skills/torusguard/payload/rules/csrf/TG-CSRF-001-missing-csrf-protection.md +31 -18
  172. package/skills/torusguard/payload/rules/csrf/TG-CSRF-002-unsafe-credentialed-cross-origin-request.md +28 -17
  173. package/skills/torusguard/payload/rules/graphql/TG-GQL-002-resolver-missing-authorization.md +33 -17
  174. package/skills/torusguard/payload/rules/graphql/TG-GQL-003-unbounded-graphql-pagination-or-batching.md +31 -16
  175. package/skills/torusguard/payload/rules/graphql/TG-GQL-004-unnecessary-production-introspection.md +25 -16
  176. package/skills/torusguard/payload/rules/secrets/TG-SEC-005-secret-in-git-history.md +24 -19
  177. package/skills/torusguard/payload/rules/secrets/TG-SEC-006-secret-in-build-artifact.md +36 -19
  178. package/skills/torusguard/payload/rules/secrets/TG-SEC-007-secret-in-test-or-example-fixture.md +21 -16
  179. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-002-missing-internal-network-protection.md +45 -18
  180. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-003-unsafe-redirect-following.md +20 -16
  181. package/skills/torusguard/payload/rules/ssrf/TG-SSRF-004-unbounded-outbound-request.md +28 -19
  182. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-001-missing-or-ignored-dependency-lockfile.md +25 -18
  183. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-003-unsafe-ci-cd-secret-exposure.md +29 -19
  184. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-004-unpinned-ci-action.md +23 -18
  185. package/skills/torusguard/payload/rules/supply-chain/TG-SUPPLY-005-unsafe-dependency-install-script.md +23 -18
  186. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-001-missing-webhook-signature-verification.md +36 -16
  187. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-002-replayable-webhook.md +26 -17
  188. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-003-missing-webhook-idempotency.md +30 -16
  189. package/skills/torusguard/payload/rules/webhook/TG-WEBHOOK-004-unvalidated-webhook-event-type.md +25 -16
  190. package/skills/torusguard/payload/rules/websocket/TG-WS-002-missing-channel-level-authorization.md +28 -17
  191. package/skills/torusguard/payload/rules/websocket/TG-WS-003-unvalidated-websocket-message.md +31 -17
  192. package/skills/torusguard/payload/rules/websocket/TG-WS-004-unbounded-websocket-connections.md +31 -17
  193. package/skills/torusguard/payload/rules_catalog.json +520 -0
  194. package/skills/torusguard/payload/scripts/apply_runner.py +383 -377
  195. package/skills/torusguard/payload/scripts/audit_runner.py +1306 -653
  196. package/skills/torusguard/payload/scripts/finding_scorer.py +14 -2
  197. package/skills/torusguard/payload/scripts/harden_runner.py +628 -455
  198. package/skills/torusguard/payload/scripts/recheck_runner.py +248 -238
  199. package/skills/torusguard/payload/scripts/recipes_runner.py +137 -137
  200. package/skills/torusguard/payload/scripts/report_sync.py +423 -0
  201. package/skills/torusguard/payload/scripts/sarif_exporter.py +116 -116
  202. package/skills/torusguard/payload/scripts/term_ui.py +162 -162
  203. package/skills/torusguard/payload/skills/torusguard/SKILL.md +75 -72
  204. package/skills/torusguard/payload/skills/torusguard-apply/SKILL.md +84 -80
  205. package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +87 -83
  206. package/skills/torusguard/payload/skills/torusguard-authorize/SKILL.md +7 -2
  207. package/skills/torusguard/payload/skills/torusguard-exploit-check/SKILL.md +7 -2
  208. package/skills/torusguard/payload/skills/torusguard-full/SKILL.md +7 -2
  209. package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +98 -94
  210. package/skills/torusguard/payload/skills/torusguard-init/SKILL.md +68 -64
  211. package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +81 -77
  212. package/skills/torusguard/payload/skills/torusguard-report/SKILL.md +76 -72
  213. package/skills/torusguard/payload/skills/torusguard-status/SKILL.md +62 -58
  214. package/skills/torusguard/payload/skills/torusguard-verify/SKILL.md +7 -2
  215. package/skills/torusguard/payload/skills/torusguard-web-validate/SKILL.md +7 -2
  216. package/skills/torusguard/payload/workflows/apply.md +11 -4
  217. package/skills/torusguard/payload/workflows/audit.md +7 -1
  218. package/skills/torusguard/payload/workflows/authorize.md +2 -1
  219. package/skills/torusguard/payload/workflows/exploit-check.md +2 -1
  220. package/skills/torusguard/payload/workflows/harden.md +7 -1
  221. package/skills/torusguard/payload/workflows/init.md +6 -1
  222. package/skills/torusguard/payload/workflows/memory.md +2 -1
  223. package/skills/torusguard/payload/workflows/recheck.md +7 -1
  224. package/skills/torusguard/payload/workflows/report.md +7 -1
  225. package/skills/torusguard/payload/workflows/status.md +6 -1
  226. package/skills/torusguard/payload/workflows/verify.md +6 -1
  227. package/skills/torusguard/payload/workflows/web-validate.md +2 -1
  228. package/skills/torusguard-apply/SKILL.md +84 -80
  229. package/skills/torusguard-audit/SKILL.md +87 -83
  230. package/skills/torusguard-authorize/SKILL.md +7 -2
  231. package/skills/torusguard-exploit-check/SKILL.md +7 -2
  232. package/skills/torusguard-full/SKILL.md +7 -2
  233. package/skills/torusguard-harden/SKILL.md +98 -94
  234. package/skills/torusguard-init/SKILL.md +68 -64
  235. package/skills/torusguard-recheck/SKILL.md +81 -77
  236. package/skills/torusguard-report/SKILL.md +76 -72
  237. package/skills/torusguard-status/SKILL.md +62 -58
  238. package/skills/torusguard-verify/SKILL.md +7 -2
  239. package/skills/torusguard-web-validate/SKILL.md +7 -2
@@ -1,162 +1,162 @@
1
- #!/usr/bin/env python3
2
- """
3
- TorusGuard Unified Terminal UI Engine (v1.3.3)
4
- Provides mathematically exact 75-column card formatting, ANSI-stripping,
5
- Unicode/emoji visual width calculation, and visual truncation with ellipsis.
6
- Pure Python 3.10+ standard library (zero external dependencies).
7
- """
8
-
9
- import sys
10
- import os
11
- import re
12
- import unicodedata
13
- from typing import Optional
14
-
15
- # Ensure UTF-8 stdout/stderr on Windows consoles
16
- if sys.stdout and hasattr(sys.stdout, "reconfigure"):
17
- try:
18
- getattr(sys.stdout, "reconfigure")(encoding="utf-8", errors="replace")
19
- except Exception:
20
- pass
21
- if sys.stderr and hasattr(sys.stderr, "reconfigure"):
22
- try:
23
- getattr(sys.stderr, "reconfigure")(encoding="utf-8", errors="replace")
24
- except Exception:
25
- pass
26
-
27
- # ─── ANSI Colors & Styles ─────────────────────────────────────────────────────
28
- BOLD = "\033[1m"
29
- DIM = "\033[2m"
30
- RESET = "\033[0m"
31
- CYAN = "\033[36m"
32
- GREEN = "\033[32m"
33
- YELLOW = "\033[33m"
34
- WHITE = "\033[97m"
35
- GRAY = "\033[90m"
36
- RED = "\033[31m"
37
- MAGENTA = "\033[35m"
38
- BG_GREEN = "\033[42m"
39
- BG_CYAN = "\033[46m"
40
-
41
- ANSI_REGEX = re.compile(r'\x1b\[[0-9;]*m|\033\[[0-9;]*m')
42
-
43
- def strip_ansi(text: str) -> str:
44
- """Remove all ANSI color and style escape sequences."""
45
- return ANSI_REGEX.sub('', text)
46
-
47
- def get_visual_width(text: str) -> int:
48
- """
49
- Calculate the printable display width of a string in terminal columns.
50
- Accounts for ANSI escapes, Unicode variation selectors, emojis, and fullwidth chars.
51
- """
52
- clean = strip_ansi(text)
53
- width = 0
54
- for ch in clean:
55
- cp = ord(ch)
56
- # Variation selectors (VS1-16, VS17-256) occupy 0 terminal columns
57
- if (0xFE00 <= cp <= 0xFE0F) or (0xE0100 <= cp <= 0xE01EF):
58
- continue
59
- # Zero-width spaces, joiners, soft hyphens
60
- if cp in (0x200B, 0x200C, 0x200D, 0x00AD):
61
- continue
62
- ea = unicodedata.east_asian_width(ch)
63
- if ea in ('W', 'F'):
64
- width += 2
65
- elif cp >= 0x1F300: # Emojis and miscellaneous pictographs
66
- width += 2
67
- elif cp in (0x26A0, 0x2714, 0x2716, 0x2139): # ⚠, ✔, ✖, ℹ
68
- width += 1
69
- else:
70
- width += 1
71
- return width
72
-
73
- def truncate_visual(text: str, max_w: int = 67) -> str:
74
- """Truncate text visually to max_w columns without breaking ANSI escape codes."""
75
- if get_visual_width(text) <= max_w:
76
- return text
77
- out = []
78
- curr_w = 0
79
- in_ansi = False
80
- ansi_buf = ''
81
- for ch in text:
82
- if ch in ('\033', '\x1b'):
83
- in_ansi = True
84
- ansi_buf = ch
85
- continue
86
- if in_ansi:
87
- ansi_buf += ch
88
- if ch == 'm':
89
- in_ansi = False
90
- out.append(ansi_buf)
91
- continue
92
- cp = ord(ch)
93
- if (0xFE00 <= cp <= 0xFE0F) or (0xE0100 <= cp <= 0xE01EF) or cp in (0x200B, 0x200C, 0x200D, 0x00AD):
94
- out.append(ch)
95
- continue
96
- ea = unicodedata.east_asian_width(ch)
97
- cw = 2 if (ea in ('W', 'F') or cp >= 0x1F300) else 1
98
- if curr_w + cw > max_w - 3:
99
- out.append(RESET + '...')
100
- curr_w += 3
101
- break
102
- out.append(ch)
103
- curr_w += cw
104
- out.append(RESET)
105
- return ''.join(out)
106
-
107
- def format_box_line(content: str, width: int = 67, border: str = "│", border_color: str = CYAN) -> str:
108
- """
109
- Pad content so the card line is strictly 75 visual columns total width.
110
- Layout: ' ' (2) + border (1) + ' ' (2) + content/pad (width=67) + ' ' (2) + border (1) = 75 cols.
111
- """
112
- trunc = truncate_visual(content, width)
113
- vis = get_visual_width(trunc)
114
- pad = " " * max(0, width - vis)
115
- return f" {border_color}{border}{RESET} {trunc}{pad} {border_color}{border}{RESET}"
116
-
117
- def card_border_top(title: str = "", border_color: str = CYAN, double: bool = False) -> str:
118
- """Generate 75-column top border (single ┌ or double ╔) with optional title."""
119
- left = "╔" if double else "┌"
120
- right = "╗" if double else "┐"
121
- h = "═" if double else "─"
122
- if title:
123
- vis = get_visual_width(title)
124
- rem = max(0, 68 - vis)
125
- return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
126
- return f" {border_color}{left}{h * 71}{right}{RESET}"
127
-
128
- def card_border_bottom(border_color: str = CYAN, double: bool = False) -> str:
129
- """Generate 75-column bottom border (single └ or double ╚)."""
130
- left = "╚" if double else "└"
131
- right = "╝" if double else "┘"
132
- h = "═" if double else "─"
133
- return f" {border_color}{left}{h * 71}{right}{RESET}"
134
-
135
- def card_divider(title: str = "", border_color: str = CYAN, double: bool = False) -> str:
136
- """Generate 75-column divider (single ├ or double ╠) with optional title."""
137
- left = "╠" if double else "├"
138
- right = "╣" if double else "┤"
139
- h = "═" if double else "─"
140
- if title:
141
- vis = get_visual_width(title)
142
- rem = max(0, 68 - vis)
143
- return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
144
- return f" {border_color}{left}{h * 71}{right}{RESET}"
145
-
146
- def card_header(title: str, subtitle: str = "", version: str = "v1.3.4", border_color: str = CYAN) -> str:
147
- """Generate standardized 75-column curved header box."""
148
- top = f" {border_color}╭{'─' * 71}╮{RESET}"
149
- bottom = f" {border_color}╰{'─' * 71}╯{RESET}"
150
- empty = f" {border_color}│{' ' * 71}│{RESET}"
151
-
152
- # Calculate padding between title and version
153
- title_vis = get_visual_width(title)
154
- ver_vis = get_visual_width(version)
155
- space_count = max(1, 67 - title_vis - ver_vis)
156
- title_str = f"{BOLD}{WHITE}{title}{RESET}{' ' * space_count}{GRAY}{version}{RESET}"
157
-
158
- lines = [top, empty, format_box_line(title_str, width=67, border_color=border_color)]
159
- if subtitle:
160
- lines.append(format_box_line(f"{DIM}{subtitle}{RESET}", width=67, border_color=border_color))
161
- lines.extend([empty, bottom])
162
- return "\n".join(lines)
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard Unified Terminal UI Engine (v1.3.5)
4
+ Provides mathematically exact 75-column card formatting, ANSI-stripping,
5
+ Unicode/emoji visual width calculation, and visual truncation with ellipsis.
6
+ Pure Python 3.10+ standard library (zero external dependencies).
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ import re
12
+ import unicodedata
13
+ from typing import Optional
14
+
15
+ # Ensure UTF-8 stdout/stderr on Windows consoles
16
+ if sys.stdout and hasattr(sys.stdout, "reconfigure"):
17
+ try:
18
+ getattr(sys.stdout, "reconfigure")(encoding="utf-8", errors="replace")
19
+ except Exception:
20
+ pass
21
+ if sys.stderr and hasattr(sys.stderr, "reconfigure"):
22
+ try:
23
+ getattr(sys.stderr, "reconfigure")(encoding="utf-8", errors="replace")
24
+ except Exception:
25
+ pass
26
+
27
+ # ─── ANSI Colors & Styles ─────────────────────────────────────────────────────
28
+ BOLD = "\033[1m"
29
+ DIM = "\033[2m"
30
+ RESET = "\033[0m"
31
+ CYAN = "\033[36m"
32
+ GREEN = "\033[32m"
33
+ YELLOW = "\033[33m"
34
+ WHITE = "\033[97m"
35
+ GRAY = "\033[90m"
36
+ RED = "\033[31m"
37
+ MAGENTA = "\033[35m"
38
+ BG_GREEN = "\033[42m"
39
+ BG_CYAN = "\033[46m"
40
+
41
+ ANSI_REGEX = re.compile(r'\x1b\[[0-9;]*m|\033\[[0-9;]*m')
42
+
43
+ def strip_ansi(text: str) -> str:
44
+ """Remove all ANSI color and style escape sequences."""
45
+ return ANSI_REGEX.sub('', text)
46
+
47
+ def get_visual_width(text: str) -> int:
48
+ """
49
+ Calculate the printable display width of a string in terminal columns.
50
+ Accounts for ANSI escapes, Unicode variation selectors, emojis, and fullwidth chars.
51
+ """
52
+ clean = strip_ansi(text)
53
+ width = 0
54
+ for ch in clean:
55
+ cp = ord(ch)
56
+ # Variation selectors (VS1-16, VS17-256) occupy 0 terminal columns
57
+ if (0xFE00 <= cp <= 0xFE0F) or (0xE0100 <= cp <= 0xE01EF):
58
+ continue
59
+ # Zero-width spaces, joiners, soft hyphens
60
+ if cp in (0x200B, 0x200C, 0x200D, 0x00AD):
61
+ continue
62
+ ea = unicodedata.east_asian_width(ch)
63
+ if ea in ('W', 'F'):
64
+ width += 2
65
+ elif cp >= 0x1F300: # Emojis and miscellaneous pictographs
66
+ width += 2
67
+ elif cp in (0x26A0, 0x2714, 0x2716, 0x2139): # ⚠, ✔, ✖, ℹ
68
+ width += 1
69
+ else:
70
+ width += 1
71
+ return width
72
+
73
+ def truncate_visual(text: str, max_w: int = 67) -> str:
74
+ """Truncate text visually to max_w columns without breaking ANSI escape codes."""
75
+ if get_visual_width(text) <= max_w:
76
+ return text
77
+ out = []
78
+ curr_w = 0
79
+ in_ansi = False
80
+ ansi_buf = ''
81
+ for ch in text:
82
+ if ch in ('\033', '\x1b'):
83
+ in_ansi = True
84
+ ansi_buf = ch
85
+ continue
86
+ if in_ansi:
87
+ ansi_buf += ch
88
+ if ch == 'm':
89
+ in_ansi = False
90
+ out.append(ansi_buf)
91
+ continue
92
+ cp = ord(ch)
93
+ if (0xFE00 <= cp <= 0xFE0F) or (0xE0100 <= cp <= 0xE01EF) or cp in (0x200B, 0x200C, 0x200D, 0x00AD):
94
+ out.append(ch)
95
+ continue
96
+ ea = unicodedata.east_asian_width(ch)
97
+ cw = 2 if (ea in ('W', 'F') or cp >= 0x1F300) else 1
98
+ if curr_w + cw > max_w - 3:
99
+ out.append(RESET + '...')
100
+ curr_w += 3
101
+ break
102
+ out.append(ch)
103
+ curr_w += cw
104
+ out.append(RESET)
105
+ return ''.join(out)
106
+
107
+ def format_box_line(content: str, width: int = 67, border: str = "│", border_color: str = CYAN) -> str:
108
+ """
109
+ Pad content so the card line is strictly 75 visual columns total width.
110
+ Layout: ' ' (2) + border (1) + ' ' (2) + content/pad (width=67) + ' ' (2) + border (1) = 75 cols.
111
+ """
112
+ trunc = truncate_visual(content, width)
113
+ vis = get_visual_width(trunc)
114
+ pad = " " * max(0, width - vis)
115
+ return f" {border_color}{border}{RESET} {trunc}{pad} {border_color}{border}{RESET}"
116
+
117
+ def card_border_top(title: str = "", border_color: str = CYAN, double: bool = False) -> str:
118
+ """Generate 75-column top border (single ┌ or double ╔) with optional title."""
119
+ left = "╔" if double else "┌"
120
+ right = "╗" if double else "┐"
121
+ h = "═" if double else "─"
122
+ if title:
123
+ vis = get_visual_width(title)
124
+ rem = max(0, 68 - vis)
125
+ return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
126
+ return f" {border_color}{left}{h * 71}{right}{RESET}"
127
+
128
+ def card_border_bottom(border_color: str = CYAN, double: bool = False) -> str:
129
+ """Generate 75-column bottom border (single └ or double ╚)."""
130
+ left = "╚" if double else "└"
131
+ right = "╝" if double else "┘"
132
+ h = "═" if double else "─"
133
+ return f" {border_color}{left}{h * 71}{right}{RESET}"
134
+
135
+ def card_divider(title: str = "", border_color: str = CYAN, double: bool = False) -> str:
136
+ """Generate 75-column divider (single ├ or double ╠) with optional title."""
137
+ left = "╠" if double else "├"
138
+ right = "╣" if double else "┤"
139
+ h = "═" if double else "─"
140
+ if title:
141
+ vis = get_visual_width(title)
142
+ rem = max(0, 68 - vis)
143
+ return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
144
+ return f" {border_color}{left}{h * 71}{right}{RESET}"
145
+
146
+ def card_header(title: str, subtitle: str = "", version: str = "v1.3.4", border_color: str = CYAN) -> str:
147
+ """Generate standardized 75-column curved header box."""
148
+ top = f" {border_color}╭{'─' * 71}╮{RESET}"
149
+ bottom = f" {border_color}╰{'─' * 71}╯{RESET}"
150
+ empty = f" {border_color}│{' ' * 71}│{RESET}"
151
+
152
+ # Calculate padding between title and version
153
+ title_vis = get_visual_width(title)
154
+ ver_vis = get_visual_width(version)
155
+ space_count = max(1, 67 - title_vis - ver_vis)
156
+ title_str = f"{BOLD}{WHITE}{title}{RESET}{' ' * space_count}{GRAY}{version}{RESET}"
157
+
158
+ lines = [top, empty, format_box_line(title_str, width=67, border_color=border_color)]
159
+ if subtitle:
160
+ lines.append(format_box_line(f"{DIM}{subtitle}{RESET}", width=67, border_color=border_color))
161
+ lines.extend([empty, bottom])
162
+ return "\n".join(lines)
@@ -0,0 +1,24 @@
1
+ /**
2
+ * TorusGuard Playground: Vulnerable Next.js Server Action Fixture
3
+ * Contains 2 intentional security findings for demonstration purposes:
4
+ * - TG-CLIENT-001 (Leaked Service Role Key)
5
+ * - TG-AUTH-003 (Unauthenticated Server Action Mutation)
6
+ */
7
+
8
+ // Finding 1: TG-CLIENT-001 - Backend admin key exposed in client-accessible code
9
+ export const SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.demo_secret_token_1234567890";
10
+
11
+ interface UserRecord {
12
+ id: string;
13
+ role: string;
14
+ }
15
+
16
+ // Finding 2: TG-AUTH-003 - Server Action performing state change without session check
17
+ export async function updateUserRole(userId: string, newRole: string): Promise<{ success: boolean }> {
18
+ "use server";
19
+
20
+ // VULNERABLE: Lacks auth session check e.g. const session = await auth(); if (!session?.user?.isAdmin) throw ...
21
+ console.log(`[VULNERABLE] Updating user ${userId} to role ${newRole} without authentication check`);
22
+
23
+ return { success: true };
24
+ }
@@ -0,0 +1,121 @@
1
+ require('dotenv').config();
2
+ const express = require('express');
3
+ const helmet = require('helmet');
4
+ const cors = require('cors');
5
+ const cookieParser = require('cookie-parser');
6
+ const rateLimit = require('express-rate-limit');
7
+ const bcrypt = require('bcrypt');
8
+ const jwt = require('jsonwebtoken');
9
+ const crypto = require('crypto');
10
+ const { z } = require('zod');
11
+
12
+ if (!process.env.JWT_SECRET) {
13
+ console.error('Missing JWT_SECRET');
14
+ process.exit(1);
15
+ }
16
+
17
+ const app = express();
18
+ const PORT = 3001;
19
+ const isProd = process.env.NODE_ENV === 'production';
20
+
21
+ app.use(helmet());
22
+ app.use(cors({ origin: process.env.CLIENT_ORIGIN, credentials: true }));
23
+ app.use(express.json({ limit: '100kb' }));
24
+ app.use(cookieParser());
25
+
26
+ const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5 });
27
+ const resetLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 3 });
28
+ const publicWriteLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 10 });
29
+
30
+ const users = [];
31
+ const resetTokens = new Map();
32
+
33
+ async function seed() {
34
+ if (users.length) return;
35
+ const hash = await bcrypt.hash('DemoPass123!', 12);
36
+ users.push({ id: '1', email: 'alice@demo.local', passwordHash: hash, name: 'Alice', role: 'user' });
37
+ users.push({ id: '2', email: 'bob@demo.local', passwordHash: hash, name: 'Bob', role: 'user' });
38
+ }
39
+
40
+ const loginSchema = z.object({
41
+ email: z.string().email().max(255),
42
+ password: z.string().min(8).max(128),
43
+ });
44
+
45
+ function auth(req, res, next) {
46
+ const token = req.cookies.token;
47
+ if (!token) return res.status(401).json({ error: 'Unauthorized' });
48
+ try {
49
+ req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });
50
+ next();
51
+ } catch {
52
+ return res.status(401).json({ error: 'Unauthorized' });
53
+ }
54
+ }
55
+
56
+ app.post('/api/login', loginLimiter, async (req, res) => {
57
+ const parsed = loginSchema.safeParse(req.body);
58
+ if (!parsed.success) return res.status(400).json({ error: 'Invalid input' });
59
+ const { email, password } = parsed.data;
60
+ const user = users.find((u) => u.email === email);
61
+ if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
62
+ return res.status(401).json({ error: 'Invalid email or password' });
63
+ }
64
+ const token = jwt.sign({ id: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '1h' });
65
+ res.cookie('token', token, {
66
+ httpOnly: true,
67
+ secure: isProd,
68
+ sameSite: 'lax',
69
+ maxAge: 3600000,
70
+ });
71
+ res.json({ message: 'Login successful' });
72
+ });
73
+
74
+ app.get('/api/users/me', auth, (req, res) => {
75
+ const user = users.find((u) => u.id === req.user.id);
76
+ if (!user) return res.status(404).json({ error: 'Not found' });
77
+ res.json({ id: user.id, email: user.email, name: user.name });
78
+ });
79
+
80
+ app.get('/api/search', auth, (req, res) => {
81
+ const q = String(req.query.q || '').slice(0, 100);
82
+ const results = users
83
+ .filter((u) => u.email.includes(q))
84
+ .slice(0, 20)
85
+ .map(({ id, email, name }) => ({ id, email, name }));
86
+ res.json({ results });
87
+ });
88
+
89
+ const contactSchema = z.object({ message: z.string().min(1).max(2000) });
90
+ app.post('/api/contact', publicWriteLimiter, (req, res) => {
91
+ const parsed = contactSchema.safeParse(req.body);
92
+ if (!parsed.success) return res.status(400).json({ error: 'Invalid input' });
93
+ res.json({ ok: true });
94
+ });
95
+
96
+ app.post('/api/ai', auth, publicWriteLimiter, (req, res) => {
97
+ const prompt = String(req.body.prompt || '').slice(0, 4000);
98
+ res.json({ reply: 'demo response', length: prompt.length });
99
+ });
100
+
101
+ app.post('/api/reset', resetLimiter, (req, res) => {
102
+ const email = String(req.body.email || '');
103
+ const user = users.find((u) => u.email === email);
104
+ if (user) {
105
+ const token = crypto.randomBytes(32).toString('hex');
106
+ resetTokens.set(token, { userId: user.id, expires: Date.now() + 900000 });
107
+ }
108
+ res.json({ message: 'If an account exists, a reset link was sent.' });
109
+ });
110
+
111
+ app.get('/api/admin/stats', auth, (req, res) => {
112
+ if (req.user.role !== 'admin') return res.status(403).json({ error: 'Forbidden' });
113
+ res.json({ users: users.length });
114
+ });
115
+
116
+ app.use((err, req, res, next) => {
117
+ console.error('Error', { message: err.message });
118
+ res.status(500).json({ error: isProd ? 'Internal server error' : err.message });
119
+ });
120
+
121
+ seed().then(() => app.listen(PORT, () => console.log(`Hardened demo http://localhost:${PORT}`)));
@@ -0,0 +1,20 @@
1
+ from fastapi import FastAPI
2
+ import requests
3
+
4
+ app = FastAPI()
5
+
6
+ # ❌ TG-SSRF-001: Unvalidated outbound URL fetching
7
+ @app.get("/fetch")
8
+ def fetch_url(url: str):
9
+ response = requests.get(url)
10
+ return {"status": response.status_code, "content": response.text}
11
+
12
+ # ❌ TG-WEBHOOK-001: Unverified webhook payload
13
+ @app.post("/webhook")
14
+ def receive_webhook(payload: dict):
15
+ return {"status": "success", "data": payload}
16
+
17
+ # ❌ TG-AUTH-006: Unfiltered dict mass assignment
18
+ @app.post("/update_profile")
19
+ def update_profile(user_id: int, updates: dict):
20
+ return {"status": "updated", "user_id": user_id, "updates": updates}
@@ -0,0 +1,63 @@
1
+ import { useState } from 'react';
2
+ import { clientConfig } from './config';
3
+
4
+ // TG-DB-001: SQL query construction in frontend (never do this)
5
+ function buildSearchQuery(email) {
6
+ return `SELECT id, email FROM users WHERE email LIKE '%${email}%'`;
7
+ }
8
+
9
+ // TG-DB-003: Documented anti-pattern — Prisma must never run in browser
10
+ // import { PrismaClient } from '@prisma/client'; // FORBIDDEN in client
11
+
12
+ export default function App() {
13
+ const [email, setEmail] = useState('');
14
+ const [password, setPassword] = useState('');
15
+ const [bio, setBio] = useState('<img src=x onerror=alert(1)>');
16
+ const [userId, setUserId] = useState('1');
17
+ const [isAdmin] = useState(localStorage.getItem('role') === 'admin'); // TG-AUTH-002
18
+ const [message, setMessage] = useState('');
19
+
20
+ async function login(e) {
21
+ e.preventDefault();
22
+ const res = await fetch('/api/login', {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/json' },
25
+ credentials: 'include',
26
+ body: JSON.stringify({ email, password }),
27
+ });
28
+ const data = await res.json();
29
+ // TG-CLIENT-002: Sensitive console logging
30
+ console.log('Login token:', data.token, 'config:', clientConfig);
31
+ setMessage(JSON.stringify(data));
32
+ }
33
+
34
+ async function fetchUser() {
35
+ const res = await fetch(`/api/users/${userId}`);
36
+ setMessage(await res.text());
37
+ }
38
+
39
+ return (
40
+ <div style={{ maxWidth: 560, margin: '2rem auto', fontFamily: 'sans-serif' }}>
41
+ <h1>Vulnerable Demo</h1>
42
+ <p style={{ color: 'crimson' }}>Intentionally insecure — localhost lab only</p>
43
+
44
+ <form onSubmit={login}>
45
+ <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="email" />
46
+ <input value={password} onChange={(e) => setPassword(e.target.value)} placeholder="password" type="password" />
47
+ <button type="submit">Login</button>
48
+ </form>
49
+
50
+ <p>Frontend SQL preview: {buildSearchQuery(email)}</p>
51
+
52
+ {/* TG-INPUT-003: Unsafe HTML rendering */}
53
+ <div dangerouslySetInnerHTML={{ __html: bio }} />
54
+
55
+ <button onClick={fetchUser}>Fetch user (IDOR demo)</button>
56
+
57
+ {/* TG-AUTH-002: Client-only admin gate */}
58
+ {isAdmin && <section><h2>Admin Panel</h2><p>Visible if localStorage role=admin — not real security</p></section>}
59
+
60
+ {message && <pre>{message}</pre>}
61
+ </div>
62
+ );
63
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ // TG-CLIENT-001: Public production source maps enabled
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ build: { sourcemap: true },
8
+ server: {
9
+ port: 5173,
10
+ proxy: { '/api': 'http://localhost:3001' },
11
+ },
12
+ });
@@ -0,0 +1,82 @@
1
+ const express = require('express');
2
+ const cors = require('cors');
3
+ const jwt = require('jsonwebtoken');
4
+
5
+ const app = express();
6
+ const PORT = 3001;
7
+
8
+ // TG-SEC-001: Hardcoded JWT secret
9
+ const JWT_SECRET = 'FAKE_DEMO_JWT_SECRET_NOT_FOR_PRODUCTION';
10
+
11
+ // TG-PLATFORM-001: Wildcard CORS with credentials
12
+ app.use(cors({ origin: '*', credentials: true }));
13
+
14
+ // TG-PLATFORM-004: No explicit JSON body size limit
15
+ app.use(express.json());
16
+
17
+ const users = [
18
+ { id: '1', email: 'alice@demo.local', password: 'password123', name: 'Alice' },
19
+ { id: '2', email: 'bob@demo.local', password: 'secret456', name: 'Bob' },
20
+ ];
21
+
22
+ // TG-RATE-001: No rate limit on login
23
+ // TG-AUTH-001: Plaintext password comparison
24
+ // TG-SEC-004: Logs sensitive data
25
+ // TG-AUTH-004: Insecure cookie (no httpOnly/Secure/SameSite)
26
+ app.post('/api/login', (req, res) => {
27
+ const { email, password } = req.body;
28
+ console.log('Login attempt:', { email, password }); // TG-SEC-004
29
+ const user = users.find((u) => u.email === email && u.password === password);
30
+ if (!user) return res.status(401).json({ error: 'User not found' }); // enumeration
31
+ const token = jwt.sign({ id: user.id }, JWT_SECRET);
32
+ res.cookie('token', token);
33
+ res.json({ message: 'ok', token, user });
34
+ });
35
+
36
+ // TG-AUTH-003: IDOR — no ownership check
37
+ app.get('/api/users/:id', (req, res) => {
38
+ const user = users.find((u) => u.id === req.params.id);
39
+ if (!user) return res.status(404).json({ error: 'Not found' });
40
+ res.json(user);
41
+ });
42
+
43
+ // TG-INPUT-002: SQL concatenation (simulated query string)
44
+ // TG-RATE-003: Unbounded results
45
+ app.get('/api/search', (req, res) => {
46
+ const q = req.query.q || '';
47
+ const fakeSql = `SELECT * FROM users WHERE email LIKE '%${q}%'`;
48
+ const results = users.filter((u) => u.email.includes(q));
49
+ res.json({ query: fakeSql, results });
50
+ });
51
+
52
+ // TG-INPUT-001: No validation
53
+ // TG-RATE-002: Unlimited contact endpoint
54
+ app.post('/api/contact', (req, res) => {
55
+ res.json({ received: req.body });
56
+ });
57
+
58
+ // TG-RATE-002: Unlimited AI endpoint
59
+ app.post('/api/ai', (req, res) => {
60
+ res.json({ reply: 'demo', prompt: req.body.prompt });
61
+ });
62
+
63
+ // TG-INPUT-004: Unrestricted upload (stub)
64
+ app.post('/api/upload', (req, res) => {
65
+ res.json({ saved: req.body.filename });
66
+ });
67
+
68
+ // TG-AUTH-005: Unsafe password reset
69
+ app.post('/api/reset', (req, res) => {
70
+ const { email } = req.body;
71
+ const user = users.find((u) => u.email === email);
72
+ if (!user) return res.status(404).json({ error: 'Email not registered' }); // enumeration
73
+ const token = `reset-${user.id}-12345`; // predictable
74
+ res.json({ resetToken: token });
75
+ });
76
+
77
+ app.use((err, req, res, next) => {
78
+ // TG-PLATFORM-003: Stack trace exposed
79
+ res.status(500).json({ error: err.message, stack: err.stack });
80
+ });
81
+
82
+ app.listen(PORT, () => console.log(`Vulnerable demo server http://localhost:${PORT}`));