torusguard 2.0.0-alpha → 2.1.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 (227) hide show
  1. package/.torusguard/.manifest.json +94 -30
  2. package/.torusguard/core/__init__.py +146 -0
  3. package/.torusguard/core/agent_roles.py +104 -0
  4. package/.torusguard/core/ast_walker.py +283 -0
  5. package/.torusguard/core/authorization.py +218 -0
  6. package/.torusguard/core/browser_verifier.py +128 -0
  7. package/.torusguard/core/bundle.py +141 -0
  8. package/.torusguard/core/call_graph.py +184 -0
  9. package/.torusguard/core/clustering.py +275 -0
  10. package/.torusguard/core/confidence.py +120 -0
  11. package/.torusguard/core/cross_file_taint.py +101 -0
  12. package/.torusguard/core/exploit_checker.py +317 -0
  13. package/.torusguard/core/formatter.py +351 -0
  14. package/.torusguard/core/governance.py +210 -0
  15. package/.torusguard/core/identity.py +104 -0
  16. package/.torusguard/core/import_resolver.py +91 -0
  17. package/.torusguard/core/incremental.py +102 -0
  18. package/.torusguard/core/lifecycle.py +137 -0
  19. package/.torusguard/core/models.py +425 -0
  20. package/.torusguard/core/parallel.py +56 -0
  21. package/.torusguard/core/parser.py +202 -0
  22. package/.torusguard/core/rechecker.py +107 -0
  23. package/.torusguard/core/replay_trace.py +178 -0
  24. package/.torusguard/core/rules_registry.py +131 -0
  25. package/.torusguard/core/run_folder.py +60 -0
  26. package/.torusguard/core/run_manager.py +163 -0
  27. package/.torusguard/core/runtime_evidence.py +175 -0
  28. package/.torusguard/core/runtime_validator.py +246 -0
  29. package/.torusguard/core/safety_gate.py +139 -0
  30. package/.torusguard/core/sarif.py +189 -0
  31. package/.torusguard/core/stack_profiler.py +184 -0
  32. package/.torusguard/core/symbol_table.py +91 -0
  33. package/.torusguard/core/taint.py +133 -0
  34. package/.torusguard/core/taint_graph.py +235 -0
  35. package/.torusguard/core/taint_rules.py +268 -0
  36. package/.torusguard/core/v070_reporter.py +102 -0
  37. package/.torusguard/core/v070_workflow.py +339 -0
  38. package/.torusguard/core/v6_reporter.py +180 -0
  39. package/.torusguard/core/v6_workflow.py +221 -0
  40. package/.torusguard/core/watcher.py +58 -0
  41. package/.torusguard/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  42. package/.torusguard/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  43. package/.torusguard/rules/container/TG-CONT-001-root-user-execution.md +50 -0
  44. package/.torusguard/rules/container/TG-CONT-002-docker-socket-mount.md +47 -0
  45. package/.torusguard/rules/container/TG-CONT-003-privileged-container-mode.md +53 -0
  46. package/.torusguard/rules/container/TG-CONT-004-build-arg-secret-exposure.md +43 -0
  47. package/.torusguard/rules/git/TG-GIT-001-historical-secret-in-git-commit.md +44 -0
  48. package/.torusguard/rules/git/TG-GIT-002-plaintext-credentials-in-git-config.md +41 -0
  49. package/.torusguard/rules/git/TG-GIT-003-sensitive-tracked-file-gitignore-breach.md +40 -0
  50. package/.torusguard/rules/rag/TG-RAG-001-untrusted-rag-context-injection.md +72 -0
  51. package/.torusguard/rules/rag/TG-RAG-002-autonomous-llm-tool-unsandboxed-call.md +51 -0
  52. package/.torusguard/rules/rag/TG-RAG-003-unpartitioned-vector-tenant-lookup.md +51 -0
  53. package/.torusguard/rules/redos/TG-REDOS-001-catastrophic-exponential-backtracking.md +46 -0
  54. package/.torusguard/rules/redos/TG-REDOS-002-unbounded-nested-quantifier.md +43 -0
  55. package/.torusguard/rules_catalog.json +96 -0
  56. package/.torusguard/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
  57. package/.torusguard/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
  58. package/.torusguard/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
  59. package/.torusguard/scripts/__pycache__/rules_sync.cpython-314.pyc +0 -0
  60. package/.torusguard/scripts/audit_runner.py +108 -10
  61. package/.torusguard/scripts/finding_scorer.py +43 -13
  62. package/.torusguard/scripts/manifest_builder.py +1 -1
  63. package/.torusguard/scripts/skill_profiler.py +26 -0
  64. package/.torusguard/skills/torusguard/SKILL.md +74 -25
  65. package/.torusguard/skills/torusguard/bootstrap.py +57 -24
  66. package/.torusguard/skills/torusguard-ai-guard/SKILL.md +95 -0
  67. package/.torusguard/skills/torusguard-apply/SKILL.md +60 -34
  68. package/.torusguard/skills/torusguard-audit/SKILL.md +131 -57
  69. package/.torusguard/skills/torusguard-authorize/SKILL.md +48 -6
  70. package/.torusguard/skills/torusguard-container/SKILL.md +94 -0
  71. package/.torusguard/skills/torusguard-exploit-check/SKILL.md +50 -6
  72. package/.torusguard/skills/torusguard-full/SKILL.md +62 -19
  73. package/.torusguard/skills/torusguard-git-mine/SKILL.md +92 -0
  74. package/.torusguard/skills/torusguard-harden/SKILL.md +81 -50
  75. package/.torusguard/skills/torusguard-init/SKILL.md +61 -14
  76. package/.torusguard/skills/torusguard-ocr-scan/SKILL.md +94 -0
  77. package/.torusguard/skills/torusguard-recheck/SKILL.md +71 -18
  78. package/.torusguard/skills/torusguard-redos/SKILL.md +91 -0
  79. package/.torusguard/skills/torusguard-report/SKILL.md +50 -9
  80. package/.torusguard/skills/torusguard-status/SKILL.md +63 -10
  81. package/.torusguard/skills/torusguard-verify/SKILL.md +52 -10
  82. package/.torusguard/skills/torusguard-web-validate/SKILL.md +53 -8
  83. package/.torusguard/workflows/ai-guard.md +31 -0
  84. package/.torusguard/workflows/apply.md +32 -55
  85. package/.torusguard/workflows/audit.md +35 -49
  86. package/.torusguard/workflows/authorize.md +27 -50
  87. package/.torusguard/workflows/container.md +29 -0
  88. package/.torusguard/workflows/exploit-check.md +28 -50
  89. package/.torusguard/workflows/git-mine.md +25 -0
  90. package/.torusguard/workflows/harden.md +29 -48
  91. package/.torusguard/workflows/init.md +27 -50
  92. package/.torusguard/workflows/memory.md +18 -23
  93. package/.torusguard/workflows/ocr-scan.md +25 -0
  94. package/.torusguard/workflows/recheck.md +28 -46
  95. package/.torusguard/workflows/redos.md +27 -0
  96. package/.torusguard/workflows/report.md +33 -52
  97. package/.torusguard/workflows/status.md +31 -52
  98. package/.torusguard/workflows/verify.md +29 -49
  99. package/.torusguard/workflows/web-validate.md +22 -45
  100. package/README.md +96 -60
  101. package/package.json +7 -2
  102. package/skills/torusguard/SKILL.md +75 -24
  103. package/skills/torusguard/__pycache__/bootstrap.cpython-314.pyc +0 -0
  104. package/skills/torusguard/bootstrap.py +60 -71
  105. package/skills/torusguard/payload/.manifest.json +94 -31
  106. package/skills/torusguard/payload/core/__init__.py +146 -0
  107. package/skills/torusguard/payload/core/agent_roles.py +104 -0
  108. package/skills/torusguard/payload/core/ast_walker.py +283 -0
  109. package/skills/torusguard/payload/core/authorization.py +218 -0
  110. package/skills/torusguard/payload/core/browser_verifier.py +128 -0
  111. package/skills/torusguard/payload/core/bundle.py +141 -0
  112. package/skills/torusguard/payload/core/call_graph.py +184 -0
  113. package/skills/torusguard/payload/core/clustering.py +275 -0
  114. package/skills/torusguard/payload/core/confidence.py +120 -0
  115. package/skills/torusguard/payload/core/cross_file_taint.py +101 -0
  116. package/skills/torusguard/payload/core/exploit_checker.py +317 -0
  117. package/skills/torusguard/payload/core/formatter.py +351 -0
  118. package/skills/torusguard/payload/core/governance.py +210 -0
  119. package/skills/torusguard/payload/core/identity.py +104 -0
  120. package/skills/torusguard/payload/core/import_resolver.py +91 -0
  121. package/skills/torusguard/payload/core/incremental.py +102 -0
  122. package/skills/torusguard/payload/core/lifecycle.py +137 -0
  123. package/skills/torusguard/payload/core/models.py +425 -0
  124. package/skills/torusguard/payload/core/parallel.py +56 -0
  125. package/skills/torusguard/payload/core/parser.py +202 -0
  126. package/skills/torusguard/payload/core/rechecker.py +107 -0
  127. package/skills/torusguard/payload/core/replay_trace.py +178 -0
  128. package/skills/torusguard/payload/core/rules_registry.py +131 -0
  129. package/skills/torusguard/payload/core/run_folder.py +60 -0
  130. package/skills/torusguard/payload/core/run_manager.py +163 -0
  131. package/skills/torusguard/payload/core/runtime_evidence.py +175 -0
  132. package/skills/torusguard/payload/core/runtime_validator.py +246 -0
  133. package/skills/torusguard/payload/core/safety_gate.py +139 -0
  134. package/skills/torusguard/payload/core/sarif.py +189 -0
  135. package/skills/torusguard/payload/core/stack_profiler.py +184 -0
  136. package/skills/torusguard/payload/core/symbol_table.py +91 -0
  137. package/skills/torusguard/payload/core/taint.py +133 -0
  138. package/skills/torusguard/payload/core/taint_graph.py +235 -0
  139. package/skills/torusguard/payload/core/taint_rules.py +268 -0
  140. package/skills/torusguard/payload/core/v070_reporter.py +102 -0
  141. package/skills/torusguard/payload/core/v070_workflow.py +339 -0
  142. package/skills/torusguard/payload/core/v6_reporter.py +180 -0
  143. package/skills/torusguard/payload/core/v6_workflow.py +221 -0
  144. package/skills/torusguard/payload/core/watcher.py +58 -0
  145. package/skills/torusguard/payload/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  146. package/skills/torusguard/payload/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  147. package/skills/torusguard/payload/rules/container/TG-CONT-001-root-user-execution.md +50 -0
  148. package/skills/torusguard/payload/rules/container/TG-CONT-002-docker-socket-mount.md +47 -0
  149. package/skills/torusguard/payload/rules/container/TG-CONT-003-privileged-container-mode.md +53 -0
  150. package/skills/torusguard/payload/rules/container/TG-CONT-004-build-arg-secret-exposure.md +43 -0
  151. package/skills/torusguard/payload/rules/git/TG-GIT-001-historical-secret-in-git-commit.md +44 -0
  152. package/skills/torusguard/payload/rules/git/TG-GIT-002-plaintext-credentials-in-git-config.md +41 -0
  153. package/skills/torusguard/payload/rules/git/TG-GIT-003-sensitive-tracked-file-gitignore-breach.md +40 -0
  154. package/skills/torusguard/payload/rules/rag/TG-RAG-001-untrusted-rag-context-injection.md +72 -0
  155. package/skills/torusguard/payload/rules/rag/TG-RAG-002-autonomous-llm-tool-unsandboxed-call.md +51 -0
  156. package/skills/torusguard/payload/rules/rag/TG-RAG-003-unpartitioned-vector-tenant-lookup.md +51 -0
  157. package/skills/torusguard/payload/rules/redos/TG-REDOS-001-catastrophic-exponential-backtracking.md +46 -0
  158. package/skills/torusguard/payload/rules/redos/TG-REDOS-002-unbounded-nested-quantifier.md +43 -0
  159. package/skills/torusguard/payload/rules_catalog.json +338 -518
  160. package/skills/torusguard/payload/scripts/__pycache__/term_ui.cpython-314.pyc +0 -0
  161. package/skills/torusguard/payload/scripts/audit_runner.py +108 -10
  162. package/skills/torusguard/payload/scripts/finding_scorer.py +43 -13
  163. package/skills/torusguard/payload/scripts/manifest_builder.py +1 -1
  164. package/skills/torusguard/payload/skills/torusguard/SKILL.md +74 -25
  165. package/skills/torusguard/payload/skills/torusguard/bootstrap.py +57 -24
  166. package/skills/torusguard/payload/skills/torusguard/references/csharp-security.md +41 -41
  167. package/skills/torusguard/payload/skills/torusguard/references/go-security.md +41 -41
  168. package/skills/torusguard/payload/skills/torusguard/references/java-security.md +40 -40
  169. package/skills/torusguard/payload/skills/torusguard/references/polyglot-security-matrix.md +25 -25
  170. package/skills/torusguard/payload/skills/torusguard/references/rust-security.md +40 -40
  171. package/skills/torusguard/payload/skills/torusguard-ai-guard/SKILL.md +95 -0
  172. package/skills/torusguard/payload/skills/torusguard-apply/SKILL.md +60 -34
  173. package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +131 -57
  174. package/skills/torusguard/payload/skills/torusguard-authorize/SKILL.md +48 -6
  175. package/skills/torusguard/payload/skills/torusguard-container/SKILL.md +94 -0
  176. package/skills/torusguard/payload/skills/torusguard-exploit-check/SKILL.md +50 -6
  177. package/skills/torusguard/payload/skills/torusguard-full/SKILL.md +62 -19
  178. package/skills/torusguard/payload/skills/torusguard-git-mine/SKILL.md +92 -0
  179. package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +81 -50
  180. package/skills/torusguard/payload/skills/torusguard-init/SKILL.md +61 -14
  181. package/skills/torusguard/payload/skills/torusguard-ocr-scan/SKILL.md +94 -0
  182. package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +71 -18
  183. package/skills/torusguard/payload/skills/torusguard-redos/SKILL.md +91 -0
  184. package/skills/torusguard/payload/skills/torusguard-report/SKILL.md +50 -9
  185. package/skills/torusguard/payload/skills/torusguard-status/SKILL.md +63 -10
  186. package/skills/torusguard/payload/skills/torusguard-verify/SKILL.md +52 -10
  187. package/skills/torusguard/payload/skills/torusguard-web-validate/SKILL.md +53 -8
  188. package/skills/torusguard/payload/workflows/ai-guard.md +31 -0
  189. package/skills/torusguard/payload/workflows/apply.md +31 -62
  190. package/skills/torusguard/payload/workflows/audit.md +35 -55
  191. package/skills/torusguard/payload/workflows/authorize.md +27 -50
  192. package/skills/torusguard/payload/workflows/container.md +29 -0
  193. package/skills/torusguard/payload/workflows/exploit-check.md +28 -50
  194. package/skills/torusguard/payload/workflows/git-mine.md +25 -0
  195. package/skills/torusguard/payload/workflows/harden.md +28 -52
  196. package/skills/torusguard/payload/workflows/init.md +27 -56
  197. package/skills/torusguard/payload/workflows/memory.md +18 -23
  198. package/skills/torusguard/payload/workflows/ocr-scan.md +25 -0
  199. package/skills/torusguard/payload/workflows/recheck.md +28 -46
  200. package/skills/torusguard/payload/workflows/redos.md +27 -0
  201. package/skills/torusguard/payload/workflows/report.md +39 -62
  202. package/skills/torusguard/payload/workflows/status.md +31 -55
  203. package/skills/torusguard/payload/workflows/torusguard-audit.md +35 -55
  204. package/skills/torusguard/payload/workflows/verify.md +29 -49
  205. package/skills/torusguard/payload/workflows/web-validate.md +22 -45
  206. package/skills/torusguard/references/csharp-security.md +41 -0
  207. package/skills/torusguard/references/go-security.md +41 -0
  208. package/skills/torusguard/references/java-security.md +40 -0
  209. package/skills/torusguard/references/polyglot-security-matrix.md +25 -0
  210. package/skills/torusguard/references/rust-security.md +40 -0
  211. package/skills/torusguard-ai-guard/SKILL.md +95 -0
  212. package/skills/torusguard-apply/SKILL.md +60 -34
  213. package/skills/torusguard-audit/SKILL.md +130 -57
  214. package/skills/torusguard-authorize/SKILL.md +48 -6
  215. package/skills/torusguard-container/SKILL.md +94 -0
  216. package/skills/torusguard-exploit-check/SKILL.md +50 -6
  217. package/skills/torusguard-full/SKILL.md +62 -19
  218. package/skills/torusguard-git-mine/SKILL.md +92 -0
  219. package/skills/torusguard-harden/SKILL.md +81 -50
  220. package/skills/torusguard-init/SKILL.md +61 -14
  221. package/skills/torusguard-ocr-scan/SKILL.md +94 -0
  222. package/skills/torusguard-recheck/SKILL.md +71 -18
  223. package/skills/torusguard-redos/SKILL.md +91 -0
  224. package/skills/torusguard-report/SKILL.md +50 -9
  225. package/skills/torusguard-status/SKILL.md +63 -10
  226. package/skills/torusguard-verify/SKILL.md +52 -10
  227. package/skills/torusguard-web-validate/SKILL.md +53 -8
@@ -0,0 +1,107 @@
1
+ """
2
+ TorusGuard v6 Targeted Recheck Engine
3
+ Executes scoped differential re-audits verifying only impacted files and adjacent trust boundaries.
4
+ Evaluates formal status transitions:
5
+ - Confirmed Fixed
6
+ - Partially Fixed
7
+ - Needs Manual Review
8
+ - Regressed
9
+ - Not Reproducible / Limited Confidence
10
+ """
11
+
12
+ from dataclasses import dataclass, field, asdict
13
+ from enum import Enum
14
+ from pathlib import Path
15
+ from typing import Dict, Any, List, Optional
16
+ import hashlib
17
+ import re
18
+
19
+
20
+ class RecheckOutcome(str, Enum):
21
+ CONFIRMED_FIXED = "Confirmed Fixed"
22
+ PARTIALLY_FIXED = "Partially Fixed"
23
+ NEEDS_MANUAL_REVIEW = "Needs Manual Review"
24
+ REGRESSED = "Regressed"
25
+ NOT_REPRODUCIBLE = "Not Reproducible / Limited Confidence"
26
+
27
+
28
+ @dataclass
29
+ class TargetedRecheckResult:
30
+ finding_id: str
31
+ rule_id: str
32
+ target_file: str
33
+ outcome: RecheckOutcome
34
+ explanation: str
35
+ original_evidence_hash: str
36
+ post_fix_evidence_hash: str
37
+ adjacent_boundaries_checked: List[str] = field(default_factory=list)
38
+ regressions_detected: List[str] = field(default_factory=list)
39
+
40
+ def to_dict(self) -> Dict[str, Any]:
41
+ d = asdict(self)
42
+ d["outcome"] = self.outcome.value
43
+ return d
44
+
45
+
46
+ class TargetedRechecker:
47
+ """
48
+ Evaluates modified files and adjacent boundaries to determine recheck outcome.
49
+ """
50
+
51
+ @staticmethod
52
+ def verify_finding(
53
+ finding_id: str,
54
+ rule_id: str,
55
+ target_file: str,
56
+ original_code_snippet: str,
57
+ post_fix_code_snippet: str,
58
+ is_safe_pattern_present: bool = True,
59
+ is_unsafe_pattern_present: bool = False,
60
+ introduced_new_flaws: Optional[List[str]] = None,
61
+ requires_manual_context: bool = False,
62
+ ) -> TargetedRecheckResult:
63
+ """
64
+ Computes recheck status based on AST evaluation and evidence hashes.
65
+ """
66
+ orig_hash = hashlib.sha256(original_code_snippet.encode("utf-8")).hexdigest()
67
+ post_hash = hashlib.sha256(post_fix_code_snippet.encode("utf-8")).hexdigest()
68
+
69
+ # Identify adjacent trust boundaries (e.g. imports, route decorators, parent caller)
70
+ adjacent_boundaries = [
71
+ f"{target_file}:imports",
72
+ f"{target_file}:route_guards",
73
+ f"{target_file}:database_session",
74
+ ]
75
+
76
+ regressions = introduced_new_flaws or []
77
+
78
+ if regressions:
79
+ outcome = RecheckOutcome.REGRESSED
80
+ explanation = f"Patch introduced secondary regression: {', '.join(regressions)}."
81
+ elif requires_manual_context:
82
+ outcome = RecheckOutcome.NEEDS_MANUAL_REVIEW
83
+ explanation = "Patch applied, but verification requires out-of-band infrastructure or runtime authentication checks."
84
+ elif is_safe_pattern_present and not is_unsafe_pattern_present:
85
+ outcome = RecheckOutcome.CONFIRMED_FIXED
86
+ explanation = f"Vulnerability pattern `{rule_id}` resolved. Hardened pattern validated with 0 detected regressions."
87
+ elif is_safe_pattern_present and is_unsafe_pattern_present:
88
+ outcome = RecheckOutcome.PARTIALLY_FIXED
89
+ explanation = "Hardened pattern partially added, but unsafe execution pathway remains accessible."
90
+ elif orig_hash == post_hash:
91
+ outcome = RecheckOutcome.NOT_REPRODUCIBLE
92
+ explanation = "Target code snippet unchanged; fix not detected in active buffer."
93
+ else:
94
+ outcome = RecheckOutcome.PARTIALLY_FIXED
95
+ explanation = "Code was modified, but rule violation remains detectable."
96
+
97
+ return TargetedRecheckResult(
98
+ finding_id=finding_id,
99
+ rule_id=rule_id,
100
+ target_file=target_file,
101
+ outcome=outcome,
102
+ explanation=explanation,
103
+ original_evidence_hash=orig_hash,
104
+ post_fix_evidence_hash=post_hash,
105
+ adjacent_boundaries_checked=adjacent_boundaries,
106
+ regressions_detected=regressions,
107
+ )
@@ -0,0 +1,178 @@
1
+ """
2
+ TorusGuard v0.7.0 Replayable Validation Traces Engine
3
+ Records and deterministically replays runtime verification sequences to prove
4
+ that vulnerabilities or remediations are reliably reproducible.
5
+ """
6
+
7
+ import json
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Dict, Any, List, Optional, Tuple
11
+ from dataclasses import dataclass, asdict
12
+
13
+ from core.runtime_evidence import RedactionEngine
14
+ from core.runtime_validator import WebValidator
15
+
16
+
17
+ @dataclass
18
+ class ReplayStep:
19
+ step_number: int
20
+ action_type: str # "http_request", "status_assert", "header_assert"
21
+ target: str
22
+ method: str = "GET"
23
+ headers: Optional[Dict[str, str]] = None
24
+ expected_status: Optional[int] = None
25
+ expected_pattern: Optional[str] = None
26
+
27
+ def to_dict(self) -> Dict[str, Any]:
28
+ return asdict(self)
29
+
30
+
31
+ @dataclass
32
+ class ReplayTrace:
33
+ trace_id: str
34
+ finding_id: str
35
+ created_at: str
36
+ target_base_url: str
37
+ description: str
38
+ steps: List[ReplayStep]
39
+
40
+ def to_dict(self) -> Dict[str, Any]:
41
+ d = asdict(self)
42
+ d["steps"] = [s.to_dict() for s in self.steps]
43
+ return d
44
+
45
+
46
+ class ReplayManager:
47
+ """
48
+ Manages recording and re-executing deterministic validation sequences.
49
+ """
50
+
51
+ def __init__(self, finding_id: str, target_base_url: str, description: str = ""):
52
+ self.trace_id = f"TRC-{finding_id}"
53
+ self.finding_id = finding_id
54
+ self.target_base_url = target_base_url
55
+ self.description = description
56
+ self.steps: List[ReplayStep] = []
57
+
58
+ def add_step(
59
+ self,
60
+ action_type: str,
61
+ target: str,
62
+ method: str = "GET",
63
+ headers: Optional[Dict[str, str]] = None,
64
+ expected_status: Optional[int] = None,
65
+ expected_pattern: Optional[str] = None
66
+ ):
67
+ sanitized_headers = RedactionEngine.redact_headers(headers)
68
+ step = ReplayStep(
69
+ step_number=len(self.steps) + 1,
70
+ action_type=action_type,
71
+ target=target,
72
+ method=method.upper(),
73
+ headers=sanitized_headers,
74
+ expected_status=expected_status,
75
+ expected_pattern=expected_pattern
76
+ )
77
+ self.steps.append(step)
78
+
79
+ def to_trace(self) -> ReplayTrace:
80
+ return ReplayTrace(
81
+ trace_id=self.trace_id,
82
+ finding_id=self.finding_id,
83
+ created_at=datetime.now(timezone.utc).isoformat(),
84
+ target_base_url=self.target_base_url,
85
+ description=self.description,
86
+ steps=self.steps
87
+ )
88
+
89
+ def write_artifacts(self, run_dir: Path) -> Tuple[Path, Path]:
90
+ run_dir.mkdir(parents=True, exist_ok=True)
91
+ json_file = run_dir / "replay.json"
92
+ md_file = run_dir / "replay.md"
93
+
94
+ trace = self.to_trace()
95
+
96
+ # 1. Write replay.json
97
+ with open(json_file, "w", encoding="utf-8") as f:
98
+ json.dump(trace.to_dict(), f, indent=2)
99
+
100
+ # 2. Write replay.md
101
+ lines = [
102
+ f"# TorusGuard v0.7.0 Replayable Verification Trace: `{self.finding_id}`",
103
+ f"\n**Trace ID:** `{self.trace_id}`",
104
+ f"**Target Base URL:** `{self.target_base_url}`",
105
+ f"**Total Steps:** `{len(self.steps)}`",
106
+ f"**Description:** {self.description or 'Deterministic runtime verification trace.'}",
107
+ "\n---",
108
+ "\n## 🔁 Execution Steps\n",
109
+ "| Step | Action | Method | Target Path / URI | Expected Status | Assertion Pattern |",
110
+ "|:---:|---|:---:|---|:---:|---|",
111
+ ]
112
+ for s in self.steps:
113
+ lines.append(
114
+ f"| {s.step_number} | `{s.action_type}` | `{s.method}` | `{s.target}` | `{s.expected_status or '-'}` | `{s.expected_pattern or '-'}` |"
115
+ )
116
+
117
+ lines.extend([
118
+ "\n---",
119
+ "\n## 🚀 Replay Instructions",
120
+ "To execute this deterministic verification trace via the TorusGuard CLI:",
121
+ "```bash",
122
+ f"python -m core.replay_runner --trace runs/<run-id>/replay.json",
123
+ "```"
124
+ ])
125
+
126
+ with open(md_file, "w", encoding="utf-8") as f:
127
+ f.write("\n".join(lines) + "\n")
128
+
129
+ return json_file, md_file
130
+
131
+ @classmethod
132
+ def execute_replay(cls, trace: ReplayTrace, validator: WebValidator) -> Dict[str, Any]:
133
+ """
134
+ Re-executes the trace steps against the target application to verify reproducibility.
135
+ """
136
+ results = []
137
+ all_passed = True
138
+
139
+ for step in trace.steps:
140
+ target_url = step.target
141
+ if not target_url.startswith("http"):
142
+ target_url = f"{trace.target_base_url.rstrip('/')}/{step.target.lstrip('/')}"
143
+
144
+ status, headers, body, decision = validator.execute_probe(
145
+ finding_id=trace.finding_id,
146
+ cluster_id="cluster-replay",
147
+ method=step.method,
148
+ target_url=target_url,
149
+ headers=step.headers,
150
+ expected_status=step.expected_status
151
+ )
152
+
153
+ step_passed = True
154
+ reason = "Success"
155
+
156
+ if step.expected_status and status != step.expected_status:
157
+ step_passed = False
158
+ reason = f"Status mismatch: expected {step.expected_status}, got {status}"
159
+ all_passed = False
160
+
161
+ if step.expected_pattern and step.expected_pattern not in body:
162
+ step_passed = False
163
+ reason = f"Pattern mismatch: '{step.expected_pattern}' not found in response"
164
+ all_passed = False
165
+
166
+ results.append({
167
+ "step": step.step_number,
168
+ "passed": step_passed,
169
+ "status_code": status,
170
+ "reason": reason
171
+ })
172
+
173
+ return {
174
+ "trace_id": trace.trace_id,
175
+ "reproducible": all_passed,
176
+ "total_steps": len(trace.steps),
177
+ "step_results": results
178
+ }
@@ -0,0 +1,131 @@
1
+ """
2
+ TorusGuard Rules Registry
3
+ Centralized repository loading and validating canonical security rules
4
+ across all 22 architectural rule families.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from dataclasses import dataclass, field, asdict
9
+ from typing import Dict, List, Optional, Any
10
+ import re
11
+
12
+
13
+ CANONICAL_FAMILIES = [
14
+ "TG-SEC", "TG-AUTH", "TG-DB", "TG-INPUT", "TG-RATE", "TG-AGENT",
15
+ "TG-SSRF", "TG-WEBHOOK", "TG-WS", "TG-CSRF", "TG-GQL", "TG-SUPPLY",
16
+ "TG-BIZ", "TG-CACHE", "TG-CLIENT", "TG-PLATFORM", "TG-DIFF",
17
+ "TG-EDGE", "TG-CONT", "TG-GIT", "TG-REDOS", "TG-RAG"
18
+ ]
19
+
20
+
21
+ @dataclass
22
+ class CanonicalRule:
23
+ rule_id: str
24
+ family: str
25
+ title: str
26
+ severity: str
27
+ category: str
28
+ description: str
29
+ remediation_guidance: str = ""
30
+ file_path: str = ""
31
+ detection_patterns: List[str] = field(default_factory=list)
32
+
33
+ def to_dict(self) -> Dict[str, Any]:
34
+ return asdict(self)
35
+
36
+
37
+ class RulesRegistry:
38
+ """Centralized loader and validator for TorusGuard rules."""
39
+
40
+ def __init__(self, rules_root: Optional[Path] = None):
41
+ self.rules_root = rules_root or self._locate_rules_dir()
42
+ self.rules: Dict[str, CanonicalRule] = {}
43
+ self.load()
44
+
45
+ def _locate_rules_dir(self) -> Path:
46
+ base = Path(__file__).resolve().parent.parent / ".torusguard" / "rules"
47
+ if base.exists():
48
+ return base
49
+ return Path.cwd() / ".torusguard" / "rules"
50
+
51
+ def load(self) -> None:
52
+ if not self.rules_root.exists():
53
+ return
54
+
55
+ for p in self.rules_root.glob("**/*.md"):
56
+ if p.name.startswith("README") or p.name.startswith("TORUSGUARD"):
57
+ continue
58
+
59
+ content = p.read_text(encoding="utf-8", errors="replace")
60
+ rule = self._parse_rule_markdown(p, content)
61
+ if rule:
62
+ self.rules[rule.rule_id] = rule
63
+
64
+ def _parse_rule_markdown(self, path: Path, content: str) -> Optional[CanonicalRule]:
65
+ filename = path.stem
66
+ # Extract rule_id from filename or first heading
67
+ rule_id_match = re.search(r"(TG-[A-Z]+-\d{3})", filename)
68
+ if not rule_id_match:
69
+ rule_id_match = re.search(r"#\s*(TG-[A-Z]+-\d{3})", content)
70
+ if not rule_id_match:
71
+ return None
72
+
73
+ rule_id = rule_id_match.group(1)
74
+ family = rule_id.rsplit("-", 1)[0]
75
+
76
+ # Extract title
77
+ title = filename.replace(rule_id, "").strip(" -_").replace("-", " ").title()
78
+ title_match = re.search(r"#\s*(?:TG-[A-Z]+-\d{3}[\s:—-]+)?(.+)", content)
79
+ if title_match:
80
+ cand_title = title_match.group(1).strip()
81
+ if cand_title and not cand_title.startswith("TG-"):
82
+ title = cand_title
83
+
84
+ # Severity
85
+ severity = "High"
86
+ sev_match = re.search(r"(?i)severity[:\s*|`]+(Critical|High|Medium|Low)", content)
87
+ if sev_match:
88
+ severity = sev_match.group(1).capitalize()
89
+
90
+ # Description
91
+ desc = ""
92
+ desc_match = re.search(r"(?i)(?:###?\s*Description|##\s*Overview|Summary)[:\s]*\n([^\n#]+)", content)
93
+ if desc_match:
94
+ desc = desc_match.group(1).strip()
95
+ else:
96
+ desc = title
97
+
98
+ # Remediation
99
+ remed = ""
100
+ rem_match = re.search(r"(?i)(?:###?\s*Remediation|###?\s*Mitigation|Fix)[:\s]*\n([^\n#]+)", content)
101
+ if rem_match:
102
+ remed = rem_match.group(1).strip()
103
+
104
+ return CanonicalRule(
105
+ rule_id=rule_id,
106
+ family=family,
107
+ title=title,
108
+ severity=severity,
109
+ category=family.replace("TG-", "").lower(),
110
+ description=desc,
111
+ remediation_guidance=remed,
112
+ file_path=str(path)
113
+ )
114
+
115
+ def get_rule(self, rule_id: str) -> Optional[CanonicalRule]:
116
+ return self.rules.get(rule_id)
117
+
118
+ def get_rules_by_family(self, family: str) -> List[CanonicalRule]:
119
+ fam_norm = family.upper()
120
+ if not fam_norm.startswith("TG-"):
121
+ fam_norm = f"TG-{fam_norm}"
122
+ return [r for r in self.rules.values() if r.family == fam_norm]
123
+
124
+ def get_rules_by_severity(self, severity: str) -> List[CanonicalRule]:
125
+ return [r for r in self.rules.values() if r.severity.lower() == severity.lower()]
126
+
127
+ def all_rules(self) -> List[CanonicalRule]:
128
+ return sorted(self.rules.values(), key=lambda r: r.rule_id)
129
+
130
+ def count(self) -> int:
131
+ return len(self.rules)
@@ -0,0 +1,60 @@
1
+ import json
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ class RunFolder:
7
+ """
8
+ Manages the per-run folder state and artifacts for a TorusGuard execution.
9
+ Creates a unique directory (e.g., .torusguard/runs/run-YYYYMMDD-HHMMSS) containing
10
+ findings.md, remediation.md, recheck.md, metadata.json, patches/, and logs/.
11
+ """
12
+ def __init__(self, output_root: str = ".torusguard/runs", run_name: Optional[str] = None):
13
+ self.output_root = Path(output_root).resolve()
14
+
15
+ if run_name:
16
+ self.run_id = run_name
17
+ else:
18
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
19
+ self.run_id = f"run-{timestamp}"
20
+
21
+ self.run_path = self.output_root / self.run_id
22
+
23
+ # Subdirectories
24
+ self.patches_dir = self.run_path / "patches"
25
+ self.logs_dir = self.run_path / "logs"
26
+
27
+ # Files
28
+ self.findings_file = self.run_path / "findings.md"
29
+ self.remediation_file = self.run_path / "remediation.md"
30
+ self.recheck_file = self.run_path / "recheck.md"
31
+ self.metadata_file = self.run_path / "metadata.json"
32
+
33
+ self._initialize_folders()
34
+ self._write_metadata()
35
+
36
+ def _initialize_folders(self):
37
+ self.run_path.mkdir(parents=True, exist_ok=True)
38
+ self.patches_dir.mkdir(parents=True, exist_ok=True)
39
+ self.logs_dir.mkdir(parents=True, exist_ok=True)
40
+
41
+ def _write_metadata(self):
42
+ metadata = {
43
+ "run_id": self.run_id,
44
+ "created_at": datetime.now().isoformat(),
45
+ "status": "initialized"
46
+ }
47
+ with open(self.metadata_file, "w", encoding="utf-8") as f:
48
+ json.dump(metadata, f, indent=2)
49
+
50
+ def get_path(self, item: str) -> Path:
51
+ """Helper to get a path within the run structure."""
52
+ paths = {
53
+ "findings.md": self.findings_file,
54
+ "remediation.md": self.remediation_file,
55
+ "recheck.md": self.recheck_file,
56
+ "metadata.json": self.metadata_file,
57
+ "patches": self.patches_dir,
58
+ "logs": self.logs_dir
59
+ }
60
+ return paths.get(item, self.run_path / item)
@@ -0,0 +1,163 @@
1
+ """
2
+ TorusGuard v6 Run Folder & Artifact Registry Manager
3
+ Creates and manages isolated run directories under runs/<run-id>/ containing:
4
+ - manifest.json
5
+ - summary.md
6
+ - findings.md
7
+ - remediation.md
8
+ - apply-plan.md
9
+ - recheck.md
10
+ - evidence.json
11
+ - diff-summary.md
12
+ - changed-files.txt
13
+ - sarif.json (optional)
14
+ - logs/
15
+ """
16
+
17
+ import json
18
+ import subprocess
19
+ from datetime import datetime
20
+ from pathlib import Path
21
+ from typing import Optional, Dict, Any, List
22
+
23
+
24
+ class RunManager:
25
+ """
26
+ Manages the lifecycle of an isolated TorusGuard run directory.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ base_dir: Optional[Path] = None,
32
+ target_name: str = "workspace",
33
+ command: str = "audit",
34
+ run_id: Optional[str] = None,
35
+ ):
36
+ self.base_dir = base_dir or Path(".torusguard/runs")
37
+ self.target_name = target_name
38
+ self.command = command
39
+
40
+ timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S")
41
+ self.run_id = run_id or f"run-{timestamp}-{target_name}"
42
+ self.run_path = self.base_dir / self.run_id
43
+
44
+ # Subdirectories
45
+ self.logs_dir = self.run_path / "logs"
46
+ self.patches_dir = self.run_path / "patches"
47
+ self.bundles_dir = self.run_path / "bundles"
48
+
49
+ # Standard Artifact Paths
50
+ self.manifest_file = self.run_path / "manifest.json"
51
+ self.summary_file = self.run_path / "summary.md"
52
+ self.findings_file = self.run_path / "findings.md"
53
+ self.remediation_file = self.run_path / "remediation.md"
54
+ self.apply_plan_file = self.run_path / "apply-plan.md"
55
+ self.recheck_file = self.run_path / "recheck.md"
56
+ self.evidence_file = self.run_path / "evidence.json"
57
+ self.diff_summary_file = self.run_path / "diff-summary.md"
58
+ self.changed_files_file = self.run_path / "changed-files.txt"
59
+ self.sarif_file = self.run_path / "sarif.json"
60
+
61
+ self._init_directories()
62
+
63
+ def _init_directories(self):
64
+ self.run_path.mkdir(parents=True, exist_ok=True)
65
+ self.logs_dir.mkdir(parents=True, exist_ok=True)
66
+ self.patches_dir.mkdir(parents=True, exist_ok=True)
67
+ self.bundles_dir.mkdir(parents=True, exist_ok=True)
68
+
69
+ def get_git_commit(self) -> Optional[str]:
70
+ try:
71
+ res = subprocess.run(
72
+ ["git", "rev-parse", "HEAD"],
73
+ capture_output=True,
74
+ text=True,
75
+ timeout=5,
76
+ )
77
+ if res.returncode == 0:
78
+ return res.stdout.strip()
79
+ except Exception:
80
+ pass
81
+ return None
82
+
83
+ def write_manifest(
84
+ self,
85
+ status_counts: Optional[Dict[str, int]] = None,
86
+ extra_meta: Optional[Dict[str, Any]] = None,
87
+ ):
88
+ """
89
+ Writes the standard manifest.json.
90
+ """
91
+ counts = status_counts or {
92
+ "total_findings": 0,
93
+ "confirmed": 0,
94
+ "high_confidence": 0,
95
+ "needs_review": 0,
96
+ "remediated": 0,
97
+ "verified_fixed": 0,
98
+ "regressed": 0,
99
+ }
100
+
101
+ manifest_data = {
102
+ "version": "v0.6.3",
103
+ "run_id": self.run_id,
104
+ "target_name": self.target_name,
105
+ "command": self.command,
106
+ "timestamp": datetime.utcnow().isoformat() + "Z",
107
+ "git_commit": self.get_git_commit(),
108
+ "status_counts": counts,
109
+ "artifacts": {
110
+ "manifest": "manifest.json",
111
+ "summary": "summary.md",
112
+ "findings": "findings.md",
113
+ "remediation": "remediation.md",
114
+ "apply_plan": "apply-plan.md",
115
+ "recheck": "recheck.md",
116
+ "evidence": "evidence.json",
117
+ "diff_summary": "diff-summary.md",
118
+ "changed_files": "changed-files.txt",
119
+ "sarif": "sarif.json",
120
+ },
121
+ }
122
+
123
+ if extra_meta:
124
+ manifest_data.update(extra_meta)
125
+
126
+ with open(self.manifest_file, "w", encoding="utf-8") as f:
127
+ json.dump(manifest_data, f, indent=2)
128
+
129
+ def write_evidence(self, evidence_data: List[Dict[str, Any]]):
130
+ with open(self.evidence_file, "w", encoding="utf-8") as f:
131
+ json.dump(evidence_data, f, indent=2)
132
+
133
+ def write_changed_files(self, files: List[str]):
134
+ with open(self.changed_files_file, "w", encoding="utf-8") as f:
135
+ f.write("\n".join(files) + "\n")
136
+
137
+ def write_summary(self, content: str):
138
+ with open(self.summary_file, "w", encoding="utf-8") as f:
139
+ f.write(content)
140
+
141
+ def write_findings(self, content: str):
142
+ with open(self.findings_file, "w", encoding="utf-8") as f:
143
+ f.write(content)
144
+
145
+ def write_remediation(self, content: str):
146
+ with open(self.remediation_file, "w", encoding="utf-8") as f:
147
+ f.write(content)
148
+
149
+ def write_apply_plan(self, content: str):
150
+ with open(self.apply_plan_file, "w", encoding="utf-8") as f:
151
+ f.write(content)
152
+
153
+ def write_diff_summary(self, content: str):
154
+ with open(self.diff_summary_file, "w", encoding="utf-8") as f:
155
+ f.write(content)
156
+
157
+ def write_recheck(self, content: str):
158
+ with open(self.recheck_file, "w", encoding="utf-8") as f:
159
+ f.write(content)
160
+
161
+ def write_sarif(self, sarif_dict: Dict[str, Any]):
162
+ with open(self.sarif_file, "w", encoding="utf-8") as f:
163
+ json.dump(sarif_dict, f, indent=2)