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,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
+ )
@@ -0,0 +1,351 @@
1
+ """
2
+ TorusGuard Report Formatter (v0.5.4)
3
+ Renders comprehensive, actionable, ticket-ready Markdown and structured JSON audit reports.
4
+ """
5
+
6
+ from typing import Dict, Any, List
7
+ from .models import (
8
+ AuditReport,
9
+ Finding,
10
+ SeverityLevel,
11
+ ConfidenceBand,
12
+ FindingStatus,
13
+ RemediationPriority,
14
+ )
15
+
16
+
17
+ class ReportFormatter:
18
+ """
19
+ Produces deterministic, human-readable, ticket-ready Markdown and structured JSON audit reports.
20
+ """
21
+
22
+ SEVERITY_BADGES = {
23
+ SeverityLevel.CRITICAL: "🔴 Critical",
24
+ SeverityLevel.HIGH: "🟠 High",
25
+ SeverityLevel.MEDIUM: "🟡 Medium",
26
+ SeverityLevel.LOW: "🔵 Low",
27
+ SeverityLevel.INFORMATIONAL: "ℹ️ Info",
28
+ }
29
+
30
+ CONFIDENCE_BADGES = {
31
+ ConfidenceBand.CONFIRMED: "🔒 Confirmed",
32
+ ConfidenceBand.HIGH_CONFIDENCE: "🟢 High Confidence",
33
+ ConfidenceBand.MEDIUM_CONFIDENCE: "🟡 Medium Confidence",
34
+ ConfidenceBand.LOW_CONFIDENCE: "🟠 Low Confidence",
35
+ ConfidenceBand.UNCONFIRMED: "⚪ Unconfirmed",
36
+ ConfidenceBand.NEEDS_REVIEW: "🔍 Needs Review",
37
+ ConfidenceBand.INFORMATIONAL: "ℹ️ Informational",
38
+ ConfidenceBand.NOT_APPLICABLE: "⚪ N/A",
39
+ }
40
+
41
+ STATUS_BADGES = {
42
+ FindingStatus.CONFIRMED: "🔴 Confirmed",
43
+ FindingStatus.HIGH_CONFIDENCE: "🟠 High Confidence",
44
+ FindingStatus.MEDIUM_CONFIDENCE: "🟡 Medium Confidence",
45
+ FindingStatus.LOW_CONFIDENCE: "⚪ Low Confidence",
46
+ FindingStatus.UNCONFIRMED: "⚪ Unconfirmed",
47
+ FindingStatus.NEEDS_REVIEW: "🔍 Needs Review",
48
+ FindingStatus.REMEDIATED: "🟡 Remediated (Pending Retest)",
49
+ FindingStatus.VERIFIED_FIXED: "🟢 Verified Fixed",
50
+ FindingStatus.SUPPRESSED: "⚪ Suppressed",
51
+ }
52
+
53
+ PRIORITY_BADGES = {
54
+ RemediationPriority.IMMEDIATE: "🚨 Immediate (P0)",
55
+ RemediationPriority.NEAR_TERM: "🟠 Near-Term (P1)",
56
+ RemediationPriority.BACKLOG: "🟡 Backlog (P2)",
57
+ }
58
+
59
+ @staticmethod
60
+ def render_markdown(report: AuditReport) -> str:
61
+ report.calculate_summary()
62
+ summary = report.summary_counts
63
+
64
+ # Overall posture indicator
65
+ if summary.get("critical", 0) > 0 or summary.get("high", 0) > 0:
66
+ posture_icon = "🔴 Action Required (Pre-Deployment Blockers Found)"
67
+ elif summary.get("medium", 0) > 0:
68
+ posture_icon = "🟡 Warnings Found (Hardening Recommended)"
69
+ elif summary.get("needs_review", 0) > 0:
70
+ posture_icon = "🔍 Needs Review (Architectural Verification Required)"
71
+ else:
72
+ posture_icon = "🟢 Ready / Verified Safe (All Checks Passing)"
73
+
74
+ lines = [
75
+ f"# TorusGuard Security Audit & Remediation Report",
76
+ f"",
77
+ f"> **Project:** `{report.project_name}` | **Repository Reference:** `{report.repository_ref}` ",
78
+ f"> **Engine Version:** TorusGuard `{report.torusguard_version}` (Actionable Remediation & Usability Release) ",
79
+ f"> **Generated At:** `{report.generated_at}` | **Report Owner:** `{report.report_owner}` ",
80
+ f"> **Overall Security Posture:** **{posture_icon}**",
81
+ f"",
82
+ f"---",
83
+ f"",
84
+ f"## 1. 📋 Executive Summary",
85
+ f"",
86
+ f"This security assessment evaluated **{report.project_name}** using TorusGuard's evidence-backed finding lifecycle and deterministic validation engine. The scan analyzed source files, configuration manifests, data queries, and API endpoint boundaries.",
87
+ f"",
88
+ f"- **Key Risk Themes:** " + (
89
+ "Critical authorization boundaries, input handling, and configuration exposures require remediation."
90
+ if summary.get("critical", 0) > 0 or summary.get("high", 0) > 0
91
+ else "No high-severity vulnerabilities detected; defensive baselines satisfied."
92
+ ),
93
+ f"- **Validation Status:** `{summary.get('confirmed', 0)}` Confirmed Findings | `{summary.get('needs_review', 0)}` Needs Review | `{summary.get('verified_fixed', 0)}` Verified Fixed",
94
+ f"- **Immediate Action Required:** " + (
95
+ f"Triage and remediate `{summary.get('immediate_priority', 0)}` P0 immediate priority finding(s) before production deployment."
96
+ if summary.get("immediate_priority", 0) > 0
97
+ else "Application meets primary security release criteria."
98
+ ),
99
+ f"",
100
+ f"### 📊 Security Metrics at a Glance",
101
+ f"",
102
+ f"| Metric | Count | Stakeholder Guidance |",
103
+ f"|---|:---:|---|",
104
+ f"| **Total Tracked Findings** | `{summary.get('total_findings', 0)}` | Total normalized finding objects |",
105
+ f"| **Immediate Priority (P0)** | `{summary.get('immediate_priority', 0)}` | 🚨 Pre-deployment blockers |",
106
+ f"| **Near-Term Priority (P1)** | `{summary.get('near_term_priority', 0)}` | 🟠 Current sprint patch cycle |",
107
+ f"| **Backlog / Hardening (P2)** | `{summary.get('backlog_priority', 0)}` | 🟡 Defense-in-depth backlog |",
108
+ f"| **Auditable Average Confidence** | `{summary.get('average_confidence_score', 0)}/100` | Mathematical evidence score |",
109
+ f"| **Verified Fixed Post-Retest** | `{summary.get('verified_fixed', 0)}` | 🟢 Retest confirmed resolution |",
110
+ f"",
111
+ f"---",
112
+ f"",
113
+ f"## 2. 🔍 Scope and Methodology",
114
+ f"",
115
+ f"- **Target Application:** `{report.project_name}`",
116
+ f"- **Detected Stack:** `{report.detected_stack.get('language', 'Unknown')}` / `{report.detected_stack.get('framework', 'Unknown')}` (Data layer: `{report.detected_stack.get('data_layer', 'None')}`)",
117
+ f"- **Tested Scope:** Source code AST structures, route handlers, serializer definitions, database queries, upload paths, and environment configurations.",
118
+ f"- **Out of Scope:** Live production network penetration testing, active DDoS simulation, third-party SaaS infrastructure compliance audits.",
119
+ f"- **Interpretation Guide:** Every finding is backed by immutable cryptographic evidence hashes. Findings marked `Needs Review` reflect out-of-band architecture requiring human architectural verification.",
120
+ f"",
121
+ f"---",
122
+ f"",
123
+ f"## 3. 📑 Key Findings Summary Table",
124
+ f"",
125
+ f"| Ref ID | Title | Category | Severity | Confidence | Status | Priority | Location |",
126
+ f"|---|---|---|:---:|:---:|:---:|:---:|---|",
127
+ ]
128
+
129
+ if not report.findings:
130
+ lines.append("| — | *No findings identified* | — | — | — | 🟢 Safe | — | — |")
131
+ else:
132
+ for f in report.findings:
133
+ sev_badge = ReportFormatter.SEVERITY_BADGES.get(f.severity.level, str(f.severity.level))
134
+ conf_badge = f"{f.confidence.score}/100"
135
+ stat_badge = ReportFormatter.STATUS_BADGES.get(f.status, str(f.status.value))
136
+ pri_badge = ReportFormatter.PRIORITY_BADGES.get(f.remediation_priority, str(f.remediation_priority))
137
+ loc_str = f"`{f.affected_component.target_path}`"
138
+ if f.affected_component.start_line:
139
+ loc_str += f":{f.affected_component.start_line}"
140
+ lines.append(
141
+ f"| `{f.rule_id}` | **{f.title}** | `{f.category.value}` | {sev_badge} | {conf_badge} | {stat_badge} | {pri_badge} | {loc_str} |"
142
+ )
143
+
144
+ lines.extend([
145
+ f"",
146
+ f"---",
147
+ f"",
148
+ f"## 4. 🛡️ Detailed Findings",
149
+ f"",
150
+ ])
151
+
152
+ if not report.findings:
153
+ lines.append("🎉 **No vulnerabilities detected!** Application complies with all TorusGuard security baselines.\n")
154
+ else:
155
+ for idx, f in enumerate(report.findings, 1):
156
+ sev_badge = ReportFormatter.SEVERITY_BADGES.get(f.severity.level, str(f.severity.level))
157
+ conf_badge = ReportFormatter.CONFIDENCE_BADGES.get(f.confidence.band, str(f.confidence.band))
158
+ status_badge = ReportFormatter.STATUS_BADGES.get(f.status, str(f.status.value))
159
+ pri_badge = ReportFormatter.PRIORITY_BADGES.get(f.remediation_priority, str(f.remediation_priority))
160
+
161
+ loc_str = f"{f.affected_component.target_path}"
162
+ if f.affected_component.start_line:
163
+ loc_str += f":{f.affected_component.start_line}"
164
+ if f.affected_component.symbol:
165
+ loc_str += f" (`{f.affected_component.symbol}`)"
166
+
167
+ factors = f.confidence.factors
168
+
169
+ lines.extend([
170
+ f"### {idx}. [{f.rule_id}] {f.title}",
171
+ f"",
172
+ f"> **Finding ID:** `{f.finding_id}` | **Remediation Priority:** **{pri_badge}** ",
173
+ f"> **Severity:** {sev_badge} | **Auditable Confidence:** {conf_badge} (`{f.confidence.score}/100`) | **Status:** {status_badge} ",
174
+ f"> **Location:** `{loc_str}` | **Lifecycle Stage:** `{f.lifecycle_stage.value}`",
175
+ f"",
176
+ f"#### 🏢 Business Impact & Executive Context",
177
+ f"{f.notes.business_impact}",
178
+ f"",
179
+ f"#### ⚙️ Technical Mechanics & Threat Context",
180
+ f"{f.notes.technical_description}",
181
+ f"",
182
+ f"#### 🔍 Auditable Confidence Score Breakdown (`{f.confidence.score}/100`)",
183
+ f"- **Evidence Quality:** `{factors.evidence_quality}/35` pts (Direct source AST/syntax match)",
184
+ f"- **Reproduction Success:** `{factors.reproduction_success}/25` pts (Deterministic reproduction path)",
185
+ f"- **Independent Confirmations:** `{factors.independent_confirmations}/15` pts (Corroborated across files)",
186
+ f"- **Environmental Clarity:** `{factors.environmental_clarity}/15` pts (No ambiguous out-of-band proxy/service)",
187
+ f"- **Manual Review Status:** `{factors.manual_review_status}/10` pts (Reviewer validation)",
188
+ f"- **Confidence Justification:** {f.confidence.rationale}",
189
+ f"",
190
+ f"#### 🔗 Provenance Chain",
191
+ f"- **Discovery Module:** `{f.provenance.discovery_module}`",
192
+ f"- **Triggering Input:** `{f.provenance.triggering_input}`",
193
+ f"- **Decision Path:**",
194
+ ])
195
+
196
+ for step in f.provenance.decision_path:
197
+ lines.append(f" 1. {step}")
198
+
199
+ lines.extend([
200
+ f"- **Verification Step:** {f.provenance.verification_step}",
201
+ f"",
202
+ f"#### 📦 Technical Evidence Package",
203
+ ])
204
+
205
+ for ev_idx, ev in enumerate(f.evidence, 1):
206
+ lines.extend([
207
+ f"**Evidence Item #{ev_idx} ({ev.type.value}):** `{ev.location}`",
208
+ f"- **SHA-256 Checksum:** `{ev.sha256_checksum}`",
209
+ f"- **Rationale:** {ev.rationale}",
210
+ f"```",
211
+ f"{ev.get_masked_snippet().strip()}",
212
+ f"```",
213
+ ])
214
+
215
+ lines.extend([
216
+ f"#### 🛠️ Prescriptive Remediation",
217
+ f"**Problem Statement:** {f.remediation.problem_statement}",
218
+ f"",
219
+ f"**Recommended Action:** {f.remediation.recommended_fix}",
220
+ f"",
221
+ f"**Framework Patch ({f.remediation.framework_pattern.framework}):**",
222
+ f"```diff",
223
+ f"- // UNSAFE ORIGINAL",
224
+ f"- {f.remediation.framework_pattern.unsafe_snippet.strip()}",
225
+ f"+ // SAFE REMEDIATION",
226
+ f"+ {f.remediation.framework_pattern.safe_snippet.strip()}",
227
+ f"```",
228
+ f"",
229
+ f"**Verification Method:** {f.remediation.verification_method}",
230
+ f"",
231
+ f"**Residual Risk Notes:** {f.remediation.residual_risk_notes}",
232
+ f"",
233
+ f"#### 🔁 Retest & Closure Status",
234
+ ])
235
+
236
+ if f.retest_result.retest_performed:
237
+ lines.extend([
238
+ f"- **Retest Executed:** ✅ Yes (`{f.retest_result.retest_timestamp}`)",
239
+ f"- **Closure Status:** **`{f.retest_result.closure_status.value}`**",
240
+ f"- **Retest Method:** `{f.retest_result.retest_method}`",
241
+ f"- **Post-Fix Evidence Hash:** `{f.retest_result.retest_evidence_hash}`",
242
+ f"- **Verifier Notes:** {f.retest_result.verifier_notes}",
243
+ ])
244
+ else:
245
+ lines.extend([
246
+ f"- **Retest Executed:** ⏳ Pending (Run `/torusguard recheck` after applying fix)",
247
+ f"- **Closure Status:** `{f.status.value}`",
248
+ ])
249
+
250
+ # Copy-pasteable Ticket/Issue payload
251
+ lines.extend([
252
+ f"",
253
+ f"<details>",
254
+ f"<summary>🎫 Copy-Paste Issue Tracker Payload (GitHub / Jira / Linear)</summary>",
255
+ f"",
256
+ f"```markdown",
257
+ f"### [Security] Fix {f.rule_id}: {f.title}",
258
+ f"",
259
+ f"**Priority:** {pri_badge} | **Severity:** {sev_badge} | **Location:** `{loc_str}`",
260
+ f"",
261
+ f"#### Problem",
262
+ f"{f.remediation.problem_statement}",
263
+ f"",
264
+ f"#### Business Impact",
265
+ f"{f.notes.business_impact}",
266
+ f"",
267
+ f"#### Proposed Fix",
268
+ f"{f.remediation.recommended_fix}",
269
+ f"",
270
+ f"#### Verification",
271
+ f"Run `/torusguard recheck` to verify resolution.",
272
+ f"```",
273
+ f"</details>",
274
+ f"",
275
+ f"---",
276
+ f"",
277
+ ])
278
+
279
+ # Section 5: Remediation Priorities
280
+ lines.extend([
281
+ f"## 5. 🎯 Remediation Priorities & Triage Roadmap",
282
+ f"",
283
+ f"### 🚨 Immediate Priority (P0 — Pre-Deployment Blockers)",
284
+ ])
285
+
286
+ immediate_findings = [f for f in report.findings if f.remediation_priority == RemediationPriority.IMMEDIATE]
287
+ if immediate_findings:
288
+ for f in immediate_findings:
289
+ lines.append(f"- [ ] **[{f.rule_id}] {f.title}** (`{f.affected_component.target_path}`) — {f.remediation.recommended_fix}")
290
+ else:
291
+ lines.append("- *No immediate P0 blockers.*")
292
+
293
+ lines.extend([
294
+ f"",
295
+ f"### 🟠 Near-Term Priority (P1 — Current Sprint Patch Cycle)",
296
+ ])
297
+
298
+ near_term_findings = [f for f in report.findings if f.remediation_priority == RemediationPriority.NEAR_TERM]
299
+ if near_term_findings:
300
+ for f in near_term_findings:
301
+ lines.append(f"- [ ] **[{f.rule_id}] {f.title}** (`{f.affected_component.target_path}`) — {f.remediation.recommended_fix}")
302
+ else:
303
+ lines.append("- *No near-term P1 items.*")
304
+
305
+ lines.extend([
306
+ f"",
307
+ f"### 🟡 Backlog / Hardening (P2 — Defense-in-Depth)",
308
+ ])
309
+
310
+ backlog_findings = [f for f in report.findings if f.remediation_priority == RemediationPriority.BACKLOG]
311
+ if backlog_findings:
312
+ for f in backlog_findings:
313
+ lines.append(f"- [ ] **[{f.rule_id}] {f.title}** (`{f.affected_component.target_path}`) — {f.remediation.recommended_fix}")
314
+ else:
315
+ lines.append("- *No backlog hardening items.*")
316
+
317
+ # Section 6: Retest & Verification
318
+ lines.extend([
319
+ f"",
320
+ f"---",
321
+ f"",
322
+ f"## 6. 🔁 Retest & Verification Workflow",
323
+ f"1. **Apply Code Patches:** Follow the Before/After framework diffs provided in Section 4.",
324
+ f"2. **Execute Retest:** Run `/torusguard recheck` to verify that findings transition to `Verified Fixed`.",
325
+ f"3. **Inspect Needs Review Items:** For items marked `Needs Review`, manually verify service-layer authorization and cloud IAM policies.",
326
+ f"",
327
+ f"---",
328
+ f"",
329
+ f"## 7. ⚖️ Limitations & Operational Boundaries",
330
+ f"- **Source-Only Analysis:** Findings are derived from static source code inspection, AST patterns, and configuration files.",
331
+ f"- **Manual Review Boundary:** When authorization or validation is delegated to out-of-band microservices, reverse proxies, or cloud IAM, TorusGuard assigns `Needs Review`.",
332
+ f"- **Sensitive Data Masking:** All secrets, tokens, and credentials in evidence snippets are automatically redacted.",
333
+ f"",
334
+ f"---",
335
+ f"",
336
+ f"## 8. 📚 Appendix & Reference Models",
337
+ f"",
338
+ f"### Confidence Scoring Model (0–100)",
339
+ f"- **90–100 (`Confirmed`):** Direct, indisputable proof of vulnerability with reachable sink.",
340
+ f"- **70–89 (`High Confidence`):** Strong static indicators present; localized verification recommended.",
341
+ f"- **50–69 (`Medium Confidence`):** Probable flaw; runtime confirmation recommended.",
342
+ f"- **< 50 (`Needs Review`):** Insufficient evidence or out-of-band architectural delegation.",
343
+ f"",
344
+ f"### Severity Rubric",
345
+ f"- **Critical (P0):** Remote Code Execution, unauthenticated SQL injection, hardcoded secrets, complete auth bypass.",
346
+ f"- **High (P1):** Object-level authorization bypass (IDOR), SSRF, XSS, tenant data leakage.",
347
+ f"- **Medium (P2):** Missing rate limits, insecure cookie attributes, missing CSRF tokens.",
348
+ f"- **Low / Info:** Verbose error banners, public source maps, hardening recommendations.",
349
+ ])
350
+
351
+ return "\n".join(lines)