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,120 @@
1
+ """
2
+ TorusGuard Multi-Signal Evidence-Chain Confidence Calibration Engine
3
+ Calibrates finding confidence (0-100) using 7 weighted empirical signals:
4
+ rule severity, taint path confirmation, taint depth, sanitizer absence,
5
+ framework context match, evidence snippet quality, and test fixture suppression.
6
+ """
7
+
8
+ from dataclasses import dataclass, field, asdict
9
+ from typing import Dict, Any, Tuple, Optional
10
+
11
+
12
+ SEVERITY_BASE_SCORES = {
13
+ "Critical": 90,
14
+ "High": 75,
15
+ "Medium": 50,
16
+ "Low": 25,
17
+ "Informational": 10
18
+ }
19
+
20
+
21
+ @dataclass
22
+ class EvidenceSignals:
23
+ rule_severity: str = "High"
24
+ taint_path_confirmed: bool = False
25
+ taint_depth: Optional[int] = None
26
+ sanitizer_present: bool = False
27
+ framework_context_match: bool = True
28
+ has_multiline_evidence: bool = True
29
+ is_test_or_mock: bool = False
30
+ memory_boost: int = 0
31
+
32
+ def to_dict(self) -> Dict[str, Any]:
33
+ return asdict(self)
34
+
35
+
36
+ class ConfidenceCalibrator:
37
+ """Computes evidence-chain calibrated confidence scores."""
38
+
39
+ @staticmethod
40
+ def calculate_score(signals: EvidenceSignals) -> Tuple[int, str, Dict[str, Any]]:
41
+ # 1. rule_severity_base (weight 0.20)
42
+ sev_base = SEVERITY_BASE_SCORES.get(signals.rule_severity, 50)
43
+ w_sev = 0.20 * sev_base
44
+
45
+ # 2. taint_path_confirmed (weight 0.25)
46
+ taint_score = 100 if signals.taint_path_confirmed else 0
47
+ w_taint = 0.25 * taint_score
48
+
49
+ # 3. taint_depth (weight 0.10)
50
+ # direct=100, 1-hop=80, 2-hop=60, 3+=40; if no taint path, 0
51
+ if signals.taint_path_confirmed:
52
+ d = signals.taint_depth if signals.taint_depth is not None else 0
53
+ if d == 0:
54
+ depth_score = 100
55
+ elif d == 1:
56
+ depth_score = 80
57
+ elif d == 2:
58
+ depth_score = 60
59
+ else:
60
+ depth_score = 40
61
+ else:
62
+ depth_score = 0
63
+ w_depth = 0.10 * depth_score
64
+
65
+ # 4. sanitizer_absence (weight 0.15)
66
+ san_score = 0 if signals.sanitizer_present else 100
67
+ w_san = 0.15 * san_score
68
+
69
+ # 5. framework_context_match (weight 0.10)
70
+ ctx_score = 100 if signals.framework_context_match else 50
71
+ w_ctx = 0.10 * ctx_score
72
+
73
+ # 6. evidence_snippet_quality (weight 0.10)
74
+ snippet_score = 100 if signals.has_multiline_evidence else 50
75
+ w_snippet = 0.10 * snippet_score
76
+
77
+ # 7. test fixture penalty
78
+ test_penalty = -50 if signals.is_test_or_mock else 0
79
+
80
+ # Memory boost (-30 to +20)
81
+ mem_boost = signals.memory_boost
82
+
83
+ raw_total = (
84
+ w_sev +
85
+ w_taint +
86
+ w_depth +
87
+ w_san +
88
+ w_ctx +
89
+ w_snippet +
90
+ test_penalty +
91
+ mem_boost
92
+ )
93
+
94
+ final_score = int(round(min(max(raw_total, 0), 100)))
95
+
96
+ # Assign band
97
+ if final_score >= 90:
98
+ band = "Confirmed"
99
+ elif final_score >= 70:
100
+ band = "High Confidence"
101
+ elif final_score >= 50:
102
+ band = "Medium Confidence"
103
+ else:
104
+ band = "Needs Review"
105
+
106
+ breakdown = {
107
+ "rule_severity_score": sev_base,
108
+ "taint_path_confirmed": signals.taint_path_confirmed,
109
+ "taint_depth": signals.taint_depth,
110
+ "sanitizer_present": signals.sanitizer_present,
111
+ "framework_context_match": signals.framework_context_match,
112
+ "has_multiline_evidence": signals.has_multiline_evidence,
113
+ "is_test_or_mock": signals.is_test_or_mock,
114
+ "test_penalty": test_penalty,
115
+ "memory_boost": mem_boost,
116
+ "total_score": final_score,
117
+ "classification_band": band
118
+ }
119
+
120
+ return final_score, band, breakdown
@@ -0,0 +1,101 @@
1
+ """
2
+ TorusGuard Cross-File Interprocedural Taint Analyzer
3
+ Propagates taint across module and function boundaries using project call graphs.
4
+ Enforces strict 5-hop depth bound to prevent recursion and path explosion.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from dataclasses import dataclass, field
9
+ from typing import List, Dict, Set, Optional, Tuple, Any
10
+
11
+ from core.taint import TaintNode, TaintPath
12
+ from core.taint_graph import TaintGraph
13
+ from core.parser import PolyglotParser, ParseResult
14
+ from core.call_graph import CallGraph, CallSite, FunctionDef
15
+
16
+
17
+ class CrossFileTaintAnalyzer:
18
+ """Interprocedural taint analysis across module boundaries."""
19
+
20
+ MAX_INTERPROCEDURAL_DEPTH = 5
21
+
22
+ def __init__(self, project_root: Path):
23
+ self.project_root = project_root.resolve()
24
+ self.parser = PolyglotParser()
25
+ self.call_graph = CallGraph(self.project_root)
26
+
27
+ def analyze_project(self, file_paths: List[Path]) -> List[TaintPath]:
28
+ """
29
+ Builds call graph and walks intra-file and cross-file dataflow chains.
30
+ """
31
+ all_paths: List[TaintPath] = []
32
+ file_graphs: Dict[str, TaintGraph] = {}
33
+ file_contents: Dict[str, str] = {}
34
+ file_languages: Dict[str, str] = {}
35
+
36
+ # 1. Build CallGraph across target files
37
+ self.call_graph.build_from_files(file_paths, self.parser)
38
+
39
+ # 2. Intra-file analysis on each file
40
+ for p in file_paths:
41
+ try:
42
+ rel_p = str(p.resolve().relative_to(self.project_root)).replace("\\", "/")
43
+ content = p.read_text(encoding="utf-8", errors="replace")
44
+ lang = self.parser.detect_language(p)
45
+ file_contents[rel_p] = content
46
+ file_languages[rel_p] = lang
47
+
48
+ tg = TaintGraph()
49
+ paths = tg.analyze_file_code(rel_p, content, lang)
50
+ file_graphs[rel_p] = tg
51
+ all_paths.extend(paths)
52
+ except Exception:
53
+ continue
54
+
55
+ # 3. Interprocedural Propagation:
56
+ # Check if function returns tainted value, propagate to call site LHS
57
+ # Check if call passes tainted argument, propagate to callee parameters
58
+ for (caller_key, call_sites) in self.call_graph.callees.items():
59
+ caller_file, caller_func = caller_key
60
+ tg_caller = file_graphs.get(caller_file)
61
+ if not tg_caller:
62
+ continue
63
+
64
+ for cs in call_sites:
65
+ if not cs.callee_file or cs.callee_file not in file_graphs:
66
+ continue
67
+
68
+ tg_callee = file_graphs[cs.callee_file]
69
+
70
+ # Check argument propagation (caller argument -> callee parameter)
71
+ for arg_expr in cs.arguments:
72
+ # Is this argument connected to a tainted source in caller?
73
+ for src_id, src_node in tg_caller.nodes.items():
74
+ if src_node.node_type == "source":
75
+ # Check if src reaches the argument expression
76
+ arg_node = TaintNode(
77
+ variable_name=arg_expr,
78
+ file_path=caller_file,
79
+ line_number=cs.caller_line,
80
+ node_type="transform",
81
+ expression=arg_expr
82
+ )
83
+ # Look for sinks in the callee file
84
+ for sink_id, sink_node in tg_callee.nodes.items():
85
+ if sink_node.node_type == "sink":
86
+ # Create interprocedural path
87
+ cross_path = [src_node, arg_node, sink_node]
88
+ is_sanitized, sanitizers = tg_callee.is_path_sanitized(
89
+ cross_path,
90
+ sink_node.metadata.get("category", ""),
91
+ file_languages.get(cs.callee_file, "python")
92
+ )
93
+ all_paths.append(TaintPath(
94
+ source=src_node,
95
+ sink=sink_node,
96
+ path=cross_path,
97
+ is_sanitized=is_sanitized,
98
+ sanitizers_found=sanitizers
99
+ ))
100
+
101
+ return all_paths
@@ -0,0 +1,317 @@
1
+ """
2
+ TorusGuard v0.7.0 Bounded Exploitability Confirmation Engine
3
+ Confirms practical reachability and exploitability for approved issue classes
4
+ using safe, non-destructive, single-step verification probes.
5
+ """
6
+
7
+ from enum import Enum
8
+ from pathlib import Path
9
+ from typing import Dict, Any, List, Optional
10
+ from dataclasses import dataclass, asdict
11
+
12
+ from core.runtime_validator import WebValidator
13
+
14
+
15
+ class ExploitabilityStatus(str, Enum):
16
+ RUNTIME_CONFIRMED = "Runtime Confirmed"
17
+ RUNTIME_LIKELY = "Runtime Likely"
18
+ NEEDS_MANUAL_REVIEW = "Needs Manual Review"
19
+ NOT_REPRODUCIBLE_IN_SCOPE = "Not Reproducible in Scope"
20
+ BLOCKED_BY_CONTROLS = "Blocked by Environment / Controls"
21
+
22
+
23
+ @dataclass
24
+ class ExploitCheckResult:
25
+ finding_id: str
26
+ issue_class: str
27
+ status: str
28
+ confidence_score: int
29
+ probe_url: str
30
+ http_status_observed: int
31
+ proof_summary: str
32
+ reproducible: bool
33
+ remediation_advice: str
34
+
35
+ def to_dict(self) -> Dict[str, Any]:
36
+ return asdict(self)
37
+
38
+
39
+ class ExploitChecker:
40
+ """
41
+ Executes bounded, safe probes to confirm practical exploitability of static findings.
42
+ """
43
+
44
+ @classmethod
45
+ def check_auth_bypass(
46
+ cls,
47
+ validator: WebValidator,
48
+ finding_id: str,
49
+ cluster_id: str,
50
+ endpoint_url: str,
51
+ expected_sensitive_marker: Optional[str] = None,
52
+ pre_approved: bool = True
53
+ ) -> ExploitCheckResult:
54
+ """
55
+ Validates whether an endpoint flagged for missing authentication
56
+ returns sensitive data to unauthenticated requests.
57
+ """
58
+ status, headers, body, decision = validator.execute_probe(
59
+ finding_id=finding_id,
60
+ cluster_id=cluster_id,
61
+ method="GET",
62
+ target_url=endpoint_url,
63
+ headers={}, # Explicitly unauthenticated
64
+ pre_approved=pre_approved,
65
+ route_context="auth_bypass_probe"
66
+ )
67
+
68
+ if not decision.allowed_to_proceed:
69
+ return ExploitCheckResult(
70
+ finding_id=finding_id,
71
+ issue_class="auth_bypass",
72
+ status=ExploitabilityStatus.BLOCKED_BY_CONTROLS.value,
73
+ confidence_score=50,
74
+ probe_url=endpoint_url,
75
+ http_status_observed=status,
76
+ proof_summary="Probe blocked by TorusGuard Safety Gate.",
77
+ reproducible=False,
78
+ remediation_advice="Review endpoint permissions manually."
79
+ )
80
+
81
+ if status in [401, 403, 302]:
82
+ # Endpoint has active authentication barrier
83
+ return ExploitCheckResult(
84
+ finding_id=finding_id,
85
+ issue_class="auth_bypass",
86
+ status=ExploitabilityStatus.NOT_REPRODUCIBLE_IN_SCOPE.value,
87
+ confidence_score=20,
88
+ probe_url=endpoint_url,
89
+ http_status_observed=status,
90
+ proof_summary=f"Endpoint returned HTTP {status}, enforcing auth barrier at runtime.",
91
+ reproducible=True,
92
+ remediation_advice="Static finding may be protected by middleware or gateway barrier."
93
+ )
94
+
95
+ if status == 200:
96
+ if expected_sensitive_marker and expected_sensitive_marker not in body:
97
+ return ExploitCheckResult(
98
+ finding_id=finding_id,
99
+ issue_class="auth_bypass",
100
+ status=ExploitabilityStatus.RUNTIME_LIKELY.value,
101
+ confidence_score=75,
102
+ probe_url=endpoint_url,
103
+ http_status_observed=status,
104
+ proof_summary="Endpoint returned HTTP 200 without auth, but expected payload marker was absent.",
105
+ reproducible=True,
106
+ remediation_advice="Enforce login_required or authentication dependency."
107
+ )
108
+
109
+ # Confirmed unauthenticated access
110
+ return ExploitCheckResult(
111
+ finding_id=finding_id,
112
+ issue_class="auth_bypass",
113
+ status=ExploitabilityStatus.RUNTIME_CONFIRMED.value,
114
+ confidence_score=98,
115
+ probe_url=endpoint_url,
116
+ http_status_observed=status,
117
+ proof_summary=f"Unauthenticated GET to {endpoint_url} returned HTTP 200 with sensitive payload.",
118
+ reproducible=True,
119
+ remediation_advice="Immediate remediation required: Add authentication barrier (e.g. Depends(get_current_user))."
120
+ )
121
+
122
+ return ExploitCheckResult(
123
+ finding_id=finding_id,
124
+ issue_class="auth_bypass",
125
+ status=ExploitabilityStatus.NEEDS_MANUAL_REVIEW.value,
126
+ confidence_score=60,
127
+ probe_url=endpoint_url,
128
+ http_status_observed=status,
129
+ proof_summary=f"Ambiguous response HTTP {status} received.",
130
+ reproducible=True,
131
+ remediation_advice="Inspect route handler and server error logs."
132
+ )
133
+
134
+ @classmethod
135
+ def check_tenant_isolation(
136
+ cls,
137
+ validator: WebValidator,
138
+ finding_id: str,
139
+ cluster_id: str,
140
+ tenant_a_resource_url: str,
141
+ tenant_b_auth_headers: Dict[str, str],
142
+ tenant_a_data_marker: str,
143
+ pre_approved: bool = True
144
+ ) -> ExploitCheckResult:
145
+ """
146
+ Validates IDOR / Multi-tenant isolation: accesses Tenant A's resource
147
+ using Tenant B's credentials.
148
+ """
149
+ status, headers, body, decision = validator.execute_probe(
150
+ finding_id=finding_id,
151
+ cluster_id=cluster_id,
152
+ method="GET",
153
+ target_url=tenant_a_resource_url,
154
+ headers=tenant_b_auth_headers,
155
+ pre_approved=pre_approved,
156
+ route_context="tenant_isolation_probe"
157
+ )
158
+
159
+ if not decision.allowed_to_proceed:
160
+ return ExploitCheckResult(
161
+ finding_id=finding_id,
162
+ issue_class="tenant_isolation",
163
+ status=ExploitabilityStatus.BLOCKED_BY_CONTROLS.value,
164
+ confidence_score=50,
165
+ probe_url=tenant_a_resource_url,
166
+ http_status_observed=status,
167
+ proof_summary="Probe blocked by Safety Gate.",
168
+ reproducible=False,
169
+ remediation_advice="Review tenant boundary manually."
170
+ )
171
+
172
+ if status in [403, 404]:
173
+ return ExploitCheckResult(
174
+ finding_id=finding_id,
175
+ issue_class="tenant_isolation",
176
+ status=ExploitabilityStatus.NOT_REPRODUCIBLE_IN_SCOPE.value,
177
+ confidence_score=15,
178
+ probe_url=tenant_a_resource_url,
179
+ http_status_observed=status,
180
+ proof_summary=f"Tenant B credentials received HTTP {status} accessing Tenant A resource. Tenant scoping holds.",
181
+ reproducible=True,
182
+ remediation_advice="Static rule may have flagged query without detecting surrounding tenant middleware."
183
+ )
184
+
185
+ if status == 200 and tenant_a_data_marker in body:
186
+ return ExploitCheckResult(
187
+ finding_id=finding_id,
188
+ issue_class="tenant_isolation",
189
+ status=ExploitabilityStatus.RUNTIME_CONFIRMED.value,
190
+ confidence_score=99,
191
+ probe_url=tenant_a_resource_url,
192
+ http_status_observed=status,
193
+ proof_summary="Tenant B credentials successfully retrieved Tenant A's private data (Cross-Tenant IDOR confirmed).",
194
+ reproducible=True,
195
+ remediation_advice="Add tenant_id filter predicate to database query."
196
+ )
197
+
198
+ return ExploitCheckResult(
199
+ finding_id=finding_id,
200
+ issue_class="tenant_isolation",
201
+ status=ExploitabilityStatus.NEEDS_MANUAL_REVIEW.value,
202
+ confidence_score=60,
203
+ probe_url=tenant_a_resource_url,
204
+ http_status_observed=status,
205
+ proof_summary=f"HTTP {status} returned; marker presence inconclusive.",
206
+ reproducible=True,
207
+ remediation_advice="Manually verify multi-tenant query filter."
208
+ )
209
+
210
+ @classmethod
211
+ def check_header_trust(
212
+ cls,
213
+ validator: WebValidator,
214
+ finding_id: str,
215
+ cluster_id: str,
216
+ endpoint_url: str,
217
+ spoofed_headers: Dict[str, str],
218
+ expected_reflection_marker: str,
219
+ pre_approved: bool = True
220
+ ) -> ExploitCheckResult:
221
+ """
222
+ Validates whether the backend implicitly trusts spoofed client headers (e.g. X-Tenant-ID, X-Role).
223
+ """
224
+ status, headers, body, decision = validator.execute_probe(
225
+ finding_id=finding_id,
226
+ cluster_id=cluster_id,
227
+ method="GET",
228
+ target_url=endpoint_url,
229
+ headers=spoofed_headers,
230
+ pre_approved=pre_approved,
231
+ route_context="header_trust_probe"
232
+ )
233
+
234
+ if not decision.allowed_to_proceed:
235
+ return ExploitCheckResult(
236
+ finding_id=finding_id,
237
+ issue_class="header_trust",
238
+ status=ExploitabilityStatus.BLOCKED_BY_CONTROLS.value,
239
+ confidence_score=50,
240
+ probe_url=endpoint_url,
241
+ http_status_observed=status,
242
+ proof_summary="Probe blocked by Safety Gate.",
243
+ reproducible=False,
244
+ remediation_advice="Review header trust manually."
245
+ )
246
+
247
+ if status == 200 and expected_reflection_marker in body:
248
+ return ExploitCheckResult(
249
+ finding_id=finding_id,
250
+ issue_class="header_trust",
251
+ status=ExploitabilityStatus.RUNTIME_CONFIRMED.value,
252
+ confidence_score=95,
253
+ probe_url=endpoint_url,
254
+ http_status_observed=status,
255
+ proof_summary=f"Backend adopted spoofed header values: '{expected_reflection_marker}' reflected in state/response.",
256
+ reproducible=True,
257
+ remediation_advice="Derive user and tenant identity from verified server-side session/JWT, not request headers."
258
+ )
259
+
260
+ return ExploitCheckResult(
261
+ finding_id=finding_id,
262
+ issue_class="header_trust",
263
+ status=ExploitabilityStatus.NOT_REPRODUCIBLE_IN_SCOPE.value,
264
+ confidence_score=20,
265
+ probe_url=endpoint_url,
266
+ http_status_observed=status,
267
+ proof_summary="Header spoofing rejected or ignored by backend.",
268
+ reproducible=True,
269
+ remediation_advice="Ensure gateway strips untrusted client headers."
270
+ )
271
+
272
+ @classmethod
273
+ def check_debug_exposure(
274
+ cls,
275
+ validator: WebValidator,
276
+ finding_id: str,
277
+ cluster_id: str,
278
+ debug_url: str,
279
+ debug_marker: str,
280
+ pre_approved: bool = True
281
+ ) -> ExploitCheckResult:
282
+ """
283
+ Validates whether internal debug/profiler endpoints are exposed to public callers.
284
+ """
285
+ status, headers, body, decision = validator.execute_probe(
286
+ finding_id=finding_id,
287
+ cluster_id=cluster_id,
288
+ method="GET",
289
+ target_url=debug_url,
290
+ pre_approved=pre_approved,
291
+ route_context="debug_exposure_probe"
292
+ )
293
+
294
+ if status == 200 and debug_marker in body:
295
+ return ExploitCheckResult(
296
+ finding_id=finding_id,
297
+ issue_class="debug_exposure",
298
+ status=ExploitabilityStatus.RUNTIME_CONFIRMED.value,
299
+ confidence_score=97,
300
+ probe_url=debug_url,
301
+ http_status_observed=status,
302
+ proof_summary=f"Debug endpoint {debug_url} is active and leaks internal environment markers.",
303
+ reproducible=True,
304
+ remediation_advice="Set DEBUG=False and disable development profiler routes in production."
305
+ )
306
+
307
+ return ExploitCheckResult(
308
+ finding_id=finding_id,
309
+ issue_class="debug_exposure",
310
+ status=ExploitabilityStatus.NOT_REPRODUCIBLE_IN_SCOPE.value,
311
+ confidence_score=10,
312
+ probe_url=debug_url,
313
+ http_status_observed=status,
314
+ proof_summary="Debug route disabled or restricted (HTTP 404/403).",
315
+ reproducible=True,
316
+ remediation_advice="Maintain strict environment variable isolation."
317
+ )