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,189 @@
1
+ """
2
+ TorusGuard v6 SARIF v2.1.0 Exporter
3
+ Emits standard SARIF-compatible JSON reports for CI/CD, GitHub Security Code Scanning, and SIEM tool integration.
4
+ """
5
+
6
+ import json
7
+ from typing import List, Dict, Any, Optional, Tuple
8
+ from pathlib import Path
9
+
10
+
11
+ class SarifExporter:
12
+ """
13
+ Transforms TorusGuard findings and clusters into standard SARIF v2.1.0 format.
14
+ """
15
+
16
+ SARIF_VERSION = "2.1.0"
17
+ SARIF_SCHEMA = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
18
+
19
+ @staticmethod
20
+ def map_severity_to_level(severity: str) -> str:
21
+ """Maps TorusGuard SeverityLevel to standard SARIF result level."""
22
+ if severity in ["Critical", "High"]:
23
+ return "error"
24
+ elif severity in ["Low", "Informational"]:
25
+ return "note"
26
+ return "warning"
27
+
28
+ @staticmethod
29
+ def _build_rule_entry(rule_id: str, title: str, sarif_level: str, category: str = "security") -> Dict[str, Any]:
30
+ """Constructs a SARIF reportingDescriptor object for a rule."""
31
+ return {
32
+ "id": rule_id,
33
+ "name": rule_id.replace("-", "_"),
34
+ "shortDescription": {"text": title},
35
+ "fullDescription": {"text": f"TorusGuard Rule {rule_id}: {title}"},
36
+ "defaultConfiguration": {"level": sarif_level},
37
+ "help": {"text": f"Read documentation for {rule_id} at https://github.com/githubmofo/TorusGuard"},
38
+ "properties": {"category": category}
39
+ }
40
+
41
+ @classmethod
42
+ def _build_result_entry(
43
+ cls, f: Dict[str, Any], rule_index: int, sarif_level: str, analysis_category: str
44
+ ) -> Dict[str, Any]:
45
+ """Constructs a single SARIF result item with fingerprints and location."""
46
+ rule_id = f.get("rule_id", "TG-GENERIC")
47
+ finding_id = f.get("finding_id", "")
48
+ title = f.get("title", rule_id)
49
+ confidence_score = f.get("confidence_score", 80)
50
+ confidence_band = f.get("confidence_band", "High Confidence")
51
+ cluster_id = f.get("cluster_id", "")
52
+
53
+ target = f.get("target", {})
54
+ file_path = target.get("file_path", "unknown").replace("\\", "/")
55
+ start_line = max(1, target.get("line_start", 1))
56
+ end_line = max(start_line, target.get("line_end", start_line))
57
+ evidence_snippet = f.get("evidence", {}).get("code_snippet", "")
58
+ fingerprint = f.get("fingerprint_id", finding_id)
59
+
60
+ return {
61
+ "ruleId": rule_id,
62
+ "ruleIndex": rule_index,
63
+ "level": sarif_level,
64
+ "message": {
65
+ "text": f"[{rule_id}] {title} (Confidence: {confidence_score}/100 - {confidence_band})"
66
+ },
67
+ "locations": [
68
+ {
69
+ "physicalLocation": {
70
+ "artifactLocation": {
71
+ "uri": file_path,
72
+ "uriBaseId": "%SRCROOT%"
73
+ },
74
+ "region": {
75
+ "startLine": start_line,
76
+ "endLine": end_line,
77
+ "snippet": {"text": evidence_snippet}
78
+ }
79
+ }
80
+ }
81
+ ],
82
+ "partialFingerprints": {
83
+ "primaryLocationLineHash": fingerprint[:16] if fingerprint else "0" * 16,
84
+ "torusguard/v6/identity": fingerprint
85
+ },
86
+ "fingerprints": {
87
+ "torusguard/v6/stable_identity": fingerprint
88
+ },
89
+ "properties": {
90
+ "finding_id": finding_id,
91
+ "confidence_score": confidence_score,
92
+ "confidence_band": confidence_band,
93
+ "cluster_id": cluster_id,
94
+ "recheck_status": f.get("recheck_status", "Unrechecked"),
95
+ "runtime_exploitability": f.get("runtime_exploitability", "Not Tested"),
96
+ "tags": ["security", "governed-remediation", cluster_id, analysis_category]
97
+ }
98
+ }
99
+
100
+ @classmethod
101
+ def generate_sarif(
102
+ cls,
103
+ findings: List[Dict[str, Any]],
104
+ clusters: Optional[List[Dict[str, Any]]] = None,
105
+ tool_version: str = "0.7.0",
106
+ analysis_category: str = "torusguard/static",
107
+ ) -> Dict[str, Any]:
108
+ """
109
+ Builds a complete SARIF log dict supporting multi-analysis category partitioning.
110
+ """
111
+ rules_map: Dict[str, Dict[str, Any]] = {}
112
+ results: List[Dict[str, Any]] = []
113
+
114
+ for f in findings:
115
+ rule_id = f.get("rule_id", "TG-GENERIC")
116
+ title = f.get("title", rule_id)
117
+ severity = f.get("severity", "High")
118
+ sarif_level = cls.map_severity_to_level(severity)
119
+
120
+ if rule_id not in rules_map:
121
+ rules_map[rule_id] = cls._build_rule_entry(
122
+ rule_id, title, sarif_level, category=f.get("category", "security")
123
+ )
124
+
125
+ rule_index = list(rules_map.keys()).index(rule_id)
126
+ result_obj = cls._build_result_entry(f, rule_index, sarif_level, analysis_category)
127
+ results.append(result_obj)
128
+
129
+ sarif_payload = {
130
+ "$schema": cls.SARIF_SCHEMA,
131
+ "version": cls.SARIF_VERSION,
132
+ "runs": [
133
+ {
134
+ "tool": {
135
+ "driver": {
136
+ "name": "TorusGuard",
137
+ "semanticVersion": tool_version,
138
+ "informationUri": "https://github.com/githubmofo/TorusGuard",
139
+ "rules": list(rules_map.values()),
140
+ }
141
+ },
142
+ "automationDetails": {
143
+ "id": f"{analysis_category}/"
144
+ },
145
+ "results": results
146
+ }
147
+ ]
148
+ }
149
+
150
+ return sarif_payload
151
+
152
+ @classmethod
153
+ def validate_github_sarif(cls, sarif_dict: Dict[str, Any]) -> Tuple[bool, List[str]]:
154
+ """
155
+ Validates that a SARIF payload meets GitHub Code Scanning upload constraints:
156
+ - $schema is present
157
+ - version is 2.1.0
158
+ - runs contains at least one driver with rules
159
+ - results have valid ruleIds, locations with artifactLocation.uri, and partialFingerprints
160
+ """
161
+ errors = []
162
+ if sarif_dict.get("version") != "2.1.0":
163
+ errors.append("SARIF version must be 2.1.0")
164
+ if not sarif_dict.get("$schema"):
165
+ errors.append("SARIF missing $schema header")
166
+
167
+ runs = sarif_dict.get("runs", [])
168
+ if not runs:
169
+ errors.append("SARIF must contain at least one run")
170
+ return False, errors
171
+
172
+ driver = runs[0].get("tool", {}).get("driver", {})
173
+ if not driver.get("name"):
174
+ errors.append("Tool driver must specify name")
175
+
176
+ results = runs[0].get("results", [])
177
+ for idx, res in enumerate(results):
178
+ if not res.get("ruleId"):
179
+ errors.append(f"Result {idx} missing ruleId")
180
+ if not res.get("locations"):
181
+ errors.append(f"Result {idx} missing locations")
182
+ else:
183
+ uri = res["locations"][0].get("physicalLocation", {}).get("artifactLocation", {}).get("uri")
184
+ if not uri:
185
+ errors.append(f"Result {idx} missing artifactLocation uri")
186
+ if "partialFingerprints" not in res:
187
+ errors.append(f"Result {idx} missing partialFingerprints for GitHub deduplication")
188
+
189
+ return len(errors) == 0, errors
@@ -0,0 +1,184 @@
1
+ """
2
+ TorusGuard v6.2 Modern Stack Profiler & Version Family Detector
3
+ Analyzes project manifests, code syntax, and dependency configurations to detect
4
+ framework version families (Django 5.x/4.x, FastAPI/Pydantic v2, SQLAlchemy 2.0, Next.js 14+),
5
+ async paradigms, container/workflow setups, and modern configuration patterns.
6
+ """
7
+
8
+ from dataclasses import dataclass, field, asdict
9
+ from pathlib import Path
10
+ from typing import Dict, List, Any, Optional, Set
11
+ import re
12
+ import json
13
+
14
+
15
+ @dataclass
16
+ class StackProfile:
17
+ primary_language: str = "Python"
18
+ framework: str = "Unknown"
19
+ version_family: str = "Modern"
20
+ is_async: bool = False
21
+ orm_layer: str = "None"
22
+ orm_version_family: str = "2.0+"
23
+ config_loader: str = "Standard"
24
+ dependency_manager: str = "pip"
25
+ container_engine: Optional[str] = None
26
+ ci_platform: Optional[str] = None
27
+ frontend_framework: Optional[str] = None
28
+ detected_features: List[str] = field(default_factory=list)
29
+
30
+ def to_dict(self) -> Dict[str, Any]:
31
+ return asdict(self)
32
+
33
+
34
+ class StackProfiler:
35
+ """
36
+ Inspects repository files to determine comprehensive modern stack profile.
37
+ """
38
+
39
+ @classmethod
40
+ def profile_repository(cls, repo_path: Path) -> StackProfile:
41
+ profile = StackProfile()
42
+ detected_features = []
43
+
44
+ if not repo_path.exists():
45
+ return profile
46
+
47
+ # Collect files
48
+ all_files: List[Path] = []
49
+ try:
50
+ for p in repo_path.rglob("*"):
51
+ if p.is_file() and not any(part.startswith(".") and part != ".github" for part in p.parts):
52
+ all_files.append(p)
53
+ except Exception:
54
+ pass
55
+
56
+ file_names = {f.name for f in all_files}
57
+ file_paths_str = [str(f.relative_to(repo_path)).replace("\\", "/") for f in all_files]
58
+
59
+ # 1. Dependency Manager & Ecosystem Detection
60
+ if "uv.lock" in file_names:
61
+ profile.dependency_manager = "uv"
62
+ detected_features.append("uv (Astral) fast package manager")
63
+ elif "poetry.lock" in file_names:
64
+ profile.dependency_manager = "poetry"
65
+ detected_features.append("Poetry packaging")
66
+ elif "Pipfile.lock" in file_names:
67
+ profile.dependency_manager = "pipenv"
68
+ elif "pnpm-lock.yaml" in file_names:
69
+ profile.dependency_manager = "pnpm"
70
+ elif "package-lock.json" in file_names:
71
+ profile.dependency_manager = "npm"
72
+ elif "yarn.lock" in file_names:
73
+ profile.dependency_manager = "yarn"
74
+ elif "go.mod" in file_names:
75
+ profile.primary_language = "Go"
76
+ profile.dependency_manager = "go modules"
77
+ detected_features.append("Go Modules package ecosystem")
78
+ elif "Cargo.toml" in file_names:
79
+ profile.primary_language = "Rust"
80
+ profile.dependency_manager = "cargo"
81
+ detected_features.append("Cargo package manager (Rust)")
82
+ elif "pom.xml" in file_names:
83
+ profile.primary_language = "Java"
84
+ profile.dependency_manager = "maven"
85
+ detected_features.append("Maven project layout")
86
+ elif "build.gradle" in file_names or "build.gradle.kts" in file_names:
87
+ profile.primary_language = "Kotlin" if "build.gradle.kts" in file_names else "Java"
88
+ profile.dependency_manager = "gradle"
89
+ detected_features.append("Gradle build tool")
90
+ elif any(f.endswith(".csproj") for f in file_names):
91
+ profile.primary_language = "C#"
92
+ profile.dependency_manager = "dotnet / nuget"
93
+ detected_features.append(".NET / C# project ecosystem")
94
+ elif "composer.json" in file_names:
95
+ profile.primary_language = "PHP"
96
+ profile.dependency_manager = "composer"
97
+ detected_features.append("Composer package manager (PHP)")
98
+ elif "Gemfile" in file_names:
99
+ profile.primary_language = "Ruby"
100
+ profile.dependency_manager = "bundler"
101
+ detected_features.append("Bundler package manager (Ruby)")
102
+ elif "pyproject.toml" in file_names:
103
+ profile.dependency_manager = "pyproject.toml (PEP 621)"
104
+
105
+ # 2. Container & CI Platform
106
+ if any("Dockerfile" in name or "Containerfile" in name for name in file_names):
107
+ profile.container_engine = "Docker / OCI Container"
108
+ detected_features.append("Containerized deployment (Dockerfile)")
109
+ if any(p.startswith(".github/workflows/") for p in file_paths_str):
110
+ profile.ci_platform = "GitHub Actions"
111
+ detected_features.append("GitHub Actions CI/CD workflows")
112
+
113
+ # 3. Source Code Inspection
114
+ py_files = [f for f in all_files if f.suffix in [".py", ".pyi"]]
115
+ ts_files = [f for f in all_files if f.suffix in [".ts", ".tsx", ".js", ".jsx"]]
116
+
117
+ py_content_sample = ""
118
+ for pf in py_files[:20]:
119
+ try:
120
+ py_content_sample += pf.read_text(encoding="utf-8", errors="ignore") + "\n"
121
+ except Exception:
122
+ pass
123
+
124
+ ts_content_sample = ""
125
+ for tf in ts_files[:20]:
126
+ try:
127
+ ts_content_sample += tf.read_text(encoding="utf-8", errors="ignore") + "\n"
128
+ except Exception:
129
+ pass
130
+
131
+ # Check async paradigm
132
+ if "async def " in py_content_sample or "await " in py_content_sample:
133
+ profile.is_async = True
134
+ detected_features.append("Async / Await Coroutines")
135
+
136
+ # Framework & Version Family Detection
137
+ if "from fastapi" in py_content_sample or "import fastapi" in py_content_sample:
138
+ profile.framework = "FastAPI"
139
+ if "Annotated[" in py_content_sample or "model_validator" in py_content_sample or "pydantic_settings" in py_content_sample:
140
+ profile.version_family = "FastAPI 0.100+ (Pydantic v2)"
141
+ detected_features.append("Pydantic v2 Typed Validation & Annotated Dependencies")
142
+ else:
143
+ profile.version_family = "FastAPI 0.9x (Legacy)"
144
+
145
+ elif "django" in py_content_sample or "DJANGO_SETTINGS_MODULE" in py_content_sample:
146
+ profile.framework = "Django"
147
+ if "aget(" in py_content_sample or "asgiref" in py_content_sample or "async def " in py_content_sample:
148
+ profile.version_family = "Django 5.x (Async Native)"
149
+ detected_features.append("Django 5.x Async ORM & Views")
150
+ else:
151
+ profile.version_family = "Django 4.x / Standard"
152
+
153
+ elif "from flask" in py_content_sample or "import flask" in py_content_sample:
154
+ profile.framework = "Flask"
155
+ profile.version_family = "Flask 3.x"
156
+
157
+ # ORM Layer
158
+ if "sqlalchemy" in py_content_sample:
159
+ profile.orm_layer = "SQLAlchemy"
160
+ if "select(" in py_content_sample or "AsyncSession" in py_content_sample or "mapped_column" in py_content_sample:
161
+ profile.orm_version_family = "SQLAlchemy 2.0+ (Modern 2.0 Syntax)"
162
+ detected_features.append("SQLAlchemy 2.0 select() queries & async sessions")
163
+ else:
164
+ profile.orm_version_family = "SQLAlchemy 1.4 (Legacy Query API)"
165
+ elif profile.framework == "Django":
166
+ profile.orm_layer = "Django ORM"
167
+ profile.orm_version_family = "Django Model Manager"
168
+
169
+ # Frontend Framework
170
+ if '"next"' in ts_content_sample or "'next'" in ts_content_sample or "use server" in ts_content_sample:
171
+ profile.frontend_framework = "Next.js 14+ (App Router)"
172
+ detected_features.append("Next.js App Router & Server Actions")
173
+ elif "react" in ts_content_sample:
174
+ profile.frontend_framework = "React"
175
+
176
+ # Configuration Loading
177
+ if "pydantic_settings" in py_content_sample or "BaseSettings" in py_content_sample:
178
+ profile.config_loader = "pydantic-settings (Type-Safe Env)"
179
+ detected_features.append("pydantic-settings environment validation")
180
+ elif "os.getenv" in py_content_sample or "os.environ" in py_content_sample:
181
+ profile.config_loader = "os.environ"
182
+
183
+ profile.detected_features = detected_features
184
+ return profile
@@ -0,0 +1,91 @@
1
+ """
2
+ TorusGuard Symbol Table Engine
3
+ Per-file symbol resolution for tracking variable definitions, scopes, types,
4
+ and import aliases across language scopes.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Dict, List, Optional, Any
9
+
10
+
11
+ @dataclass
12
+ class VariableInfo:
13
+ name: str
14
+ defined_line: int
15
+ scope_name: str = "global"
16
+ assigned_expression: str = ""
17
+ is_tainted: bool = False
18
+ inferred_type: str = "unknown"
19
+ metadata: Dict[str, Any] = field(default_factory=dict)
20
+
21
+
22
+ class SymbolTable:
23
+ """Tracks variable definitions, scopes, and types within a single file."""
24
+
25
+ def __init__(self, file_path: str = ""):
26
+ self.file_path = file_path
27
+ self.variables: Dict[str, List[VariableInfo]] = {}
28
+ self.import_aliases: Dict[str, str] = {}
29
+ self.function_defs: Dict[str, int] = {}
30
+
31
+ def add_variable(
32
+ self,
33
+ name: str,
34
+ line: int,
35
+ scope: str = "global",
36
+ expression: str = "",
37
+ is_tainted: bool = False,
38
+ inferred_type: str = "unknown"
39
+ ) -> VariableInfo:
40
+ var_info = VariableInfo(
41
+ name=name,
42
+ defined_line=line,
43
+ scope_name=scope,
44
+ assigned_expression=expression,
45
+ is_tainted=is_tainted,
46
+ inferred_type=inferred_type
47
+ )
48
+ if name not in self.variables:
49
+ self.variables[name] = []
50
+ self.variables[name].append(var_info)
51
+ return var_info
52
+
53
+ def resolve_variable(self, name: str, at_line: int) -> Optional[VariableInfo]:
54
+ """
55
+ Given a variable name and current line, returns its most recent definition
56
+ preceding or at that line.
57
+ """
58
+ defs = self.variables.get(name, [])
59
+ candidate = None
60
+ for d in defs:
61
+ if d.defined_line <= at_line:
62
+ if candidate is None or d.defined_line > candidate.defined_line:
63
+ candidate = d
64
+ return candidate
65
+
66
+ def add_import_alias(self, alias: str, canonical_name: str) -> None:
67
+ """Register an import alias (e.g. `from flask import request as req`)."""
68
+ self.import_aliases[alias] = canonical_name
69
+
70
+ def get_import_alias(self, alias: str) -> Optional[str]:
71
+ """Resolve an alias to its canonical symbol or module."""
72
+ return self.import_aliases.get(alias)
73
+
74
+ def mark_tainted(self, name: str, at_line: int) -> None:
75
+ var_info = self.resolve_variable(name, at_line)
76
+ if var_info:
77
+ var_info.is_tainted = True
78
+ else:
79
+ self.add_variable(name=name, line=at_line, is_tainted=True)
80
+
81
+ def is_tainted(self, name: str, at_line: int) -> bool:
82
+ var_info = self.resolve_variable(name, at_line)
83
+ return var_info.is_tainted if var_info else False
84
+
85
+ def to_dict(self) -> Dict[str, Any]:
86
+ return {
87
+ "file_path": self.file_path,
88
+ "variables": {k: [v.defined_line for v in vs] for k, vs in self.variables.items()},
89
+ "import_aliases": self.import_aliases,
90
+ "functions": self.function_defs
91
+ }
@@ -0,0 +1,133 @@
1
+ """
2
+ TorusGuard Taint Engine — Core Taint Models & Propagation Dataclasses
3
+ Defines source, sink, sanitizer entities and dataflow propagation primitives.
4
+ """
5
+
6
+ from dataclasses import dataclass, field, asdict
7
+ from typing import List, Dict, Any, Optional, Set
8
+
9
+
10
+ @dataclass
11
+ class TaintSource:
12
+ pattern: str
13
+ framework: str
14
+ label: str
15
+ category: str
16
+ language: str
17
+ line_number: int = 0
18
+ file_path: str = ""
19
+ variable_name: str = ""
20
+
21
+ def to_dict(self) -> Dict[str, Any]:
22
+ return asdict(self)
23
+
24
+
25
+ @dataclass
26
+ class TaintSink:
27
+ pattern: str
28
+ category: str
29
+ rule_id: str
30
+ severity: str
31
+ language: str
32
+ line_number: int = 0
33
+ file_path: str = ""
34
+ argument_name: str = ""
35
+
36
+ def to_dict(self) -> Dict[str, Any]:
37
+ return asdict(self)
38
+
39
+
40
+ @dataclass
41
+ class TaintSanitizer:
42
+ pattern: str
43
+ cleans: List[str]
44
+ language: str
45
+ line_number: int = 0
46
+ file_path: str = ""
47
+
48
+ def to_dict(self) -> Dict[str, Any]:
49
+ return asdict(self)
50
+
51
+
52
+ @dataclass
53
+ class TaintNode:
54
+ variable_name: str
55
+ file_path: str
56
+ line_number: int
57
+ node_type: str # "source" | "transform" | "sink" | "sanitizer"
58
+ expression: str = ""
59
+ pattern: str = ""
60
+ category: str = ""
61
+ metadata: Dict[str, Any] = field(default_factory=dict)
62
+
63
+ def node_id(self) -> str:
64
+ return f"{self.file_path}:{self.line_number}:{self.variable_name}:{self.node_type}"
65
+
66
+ def to_dict(self) -> Dict[str, Any]:
67
+ return asdict(self)
68
+
69
+
70
+ @dataclass
71
+ class TaintEdge:
72
+ from_node: TaintNode
73
+ to_node: TaintNode
74
+ edge_type: str # "assignment" | "argument" | "return" | "attribute" | "call"
75
+ line_number: int = 0
76
+
77
+ def to_dict(self) -> Dict[str, Any]:
78
+ return {
79
+ "from_node": self.from_node.node_id(),
80
+ "to_node": self.to_node.node_id(),
81
+ "edge_type": self.edge_type,
82
+ "line_number": self.line_number,
83
+ }
84
+
85
+
86
+ @dataclass
87
+ class TaintPath:
88
+ source: TaintNode
89
+ sink: TaintNode
90
+ path: List[TaintNode]
91
+ is_sanitized: bool = False
92
+ sanitizers_found: List[TaintNode] = field(default_factory=list)
93
+ confidence_penalty: int = 0
94
+
95
+ @property
96
+ def depth(self) -> int:
97
+ return max(0, len(self.path) - 1)
98
+
99
+ def to_dict(self) -> Dict[str, Any]:
100
+ return {
101
+ "source": self.source.to_dict(),
102
+ "sink": self.sink.to_dict(),
103
+ "depth": self.depth,
104
+ "is_sanitized": self.is_sanitized,
105
+ "sanitizers_found": [s.to_dict() for s in self.sanitizers_found],
106
+ "path_nodes": [n.to_dict() for n in self.path],
107
+ }
108
+
109
+
110
+ class TaintPropagator:
111
+ """
112
+ Evaluates statement-level and expression-level taint transfer.
113
+ Determines if assignment, function call, or operation propagates taint
114
+ from RHS variables to LHS target.
115
+ """
116
+
117
+ @staticmethod
118
+ def propagates_taint(source_vars: Set[str], expression: str) -> bool:
119
+ """Checks if any currently tainted variable appears in the expression."""
120
+ if not source_vars or not expression:
121
+ return False
122
+ import re
123
+ tokens = set(re.findall(r"\b[A-Za-z_][A-Za-z0-9_]*\b", expression))
124
+ return bool(tokens.intersection(source_vars))
125
+
126
+ @staticmethod
127
+ def is_sanitizer_expression(expression: str, sanitizers: List[Dict[str, Any]], category: str) -> Optional[Dict[str, Any]]:
128
+ """Returns the matching sanitizer if the expression applies a known sanitizer for the category."""
129
+ for s in sanitizers:
130
+ if s["pattern"] in expression:
131
+ if not category or category in s.get("cleans", []):
132
+ return s
133
+ return None