torusguard 2.1.0 → 2.1.2

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 (152) hide show
  1. package/.torusguard/.manifest.json +55 -5
  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/custom_rules/README.md +24 -0
  42. package/.torusguard/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  43. package/.torusguard/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  44. package/.torusguard/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
  45. package/.torusguard/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
  46. package/.torusguard/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
  47. package/.torusguard/scripts/__pycache__/rules_sync.cpython-314.pyc +0 -0
  48. package/.torusguard/scripts/audit_runner.py +108 -10
  49. package/.torusguard/scripts/deliberation_tournament.py +136 -0
  50. package/.torusguard/scripts/finding_scorer.py +43 -13
  51. package/.torusguard/scripts/reachability_analyzer.py +165 -0
  52. package/.torusguard/scripts/skill_profiler.py +26 -0
  53. package/.torusguard/scripts/stride_generator.py +198 -0
  54. package/.torusguard/skills/torusguard/SKILL.md +6 -2
  55. package/.torusguard/skills/torusguard-audit/SKILL.md +109 -84
  56. package/.torusguard/skills/torusguard-review/SKILL.md +27 -0
  57. package/.torusguard/skills/torusguard-threatmodel/SKILL.md +27 -0
  58. package/.torusguard/workflows/audit.md +21 -17
  59. package/.torusguard/workflows/review.md +11 -0
  60. package/.torusguard/workflows/threatmodel.md +9 -0
  61. package/README.md +149 -48
  62. package/package.json +10 -2
  63. package/skills/torusguard/SKILL.md +6 -2
  64. package/skills/torusguard/__pycache__/bootstrap.cpython-314.pyc +0 -0
  65. package/skills/torusguard/bootstrap.py +3 -3
  66. package/skills/torusguard/payload/.manifest.json +56 -7
  67. package/skills/torusguard/payload/core/__init__.py +146 -0
  68. package/skills/torusguard/payload/core/agent_roles.py +104 -0
  69. package/skills/torusguard/payload/core/ast_walker.py +283 -0
  70. package/skills/torusguard/payload/core/authorization.py +218 -0
  71. package/skills/torusguard/payload/core/browser_verifier.py +128 -0
  72. package/skills/torusguard/payload/core/bundle.py +141 -0
  73. package/skills/torusguard/payload/core/call_graph.py +184 -0
  74. package/skills/torusguard/payload/core/clustering.py +275 -0
  75. package/skills/torusguard/payload/core/confidence.py +120 -0
  76. package/skills/torusguard/payload/core/cross_file_taint.py +101 -0
  77. package/skills/torusguard/payload/core/exploit_checker.py +317 -0
  78. package/skills/torusguard/payload/core/formatter.py +351 -0
  79. package/skills/torusguard/payload/core/governance.py +210 -0
  80. package/skills/torusguard/payload/core/identity.py +104 -0
  81. package/skills/torusguard/payload/core/import_resolver.py +91 -0
  82. package/skills/torusguard/payload/core/incremental.py +102 -0
  83. package/skills/torusguard/payload/core/lifecycle.py +137 -0
  84. package/skills/torusguard/payload/core/models.py +425 -0
  85. package/skills/torusguard/payload/core/parallel.py +56 -0
  86. package/skills/torusguard/payload/core/parser.py +202 -0
  87. package/skills/torusguard/payload/core/rechecker.py +107 -0
  88. package/skills/torusguard/payload/core/replay_trace.py +178 -0
  89. package/skills/torusguard/payload/core/rules_registry.py +131 -0
  90. package/skills/torusguard/payload/core/run_folder.py +60 -0
  91. package/skills/torusguard/payload/core/run_manager.py +163 -0
  92. package/skills/torusguard/payload/core/runtime_evidence.py +175 -0
  93. package/skills/torusguard/payload/core/runtime_validator.py +246 -0
  94. package/skills/torusguard/payload/core/safety_gate.py +139 -0
  95. package/skills/torusguard/payload/core/sarif.py +189 -0
  96. package/skills/torusguard/payload/core/stack_profiler.py +184 -0
  97. package/skills/torusguard/payload/core/symbol_table.py +91 -0
  98. package/skills/torusguard/payload/core/taint.py +133 -0
  99. package/skills/torusguard/payload/core/taint_graph.py +235 -0
  100. package/skills/torusguard/payload/core/taint_rules.py +268 -0
  101. package/skills/torusguard/payload/core/v070_reporter.py +102 -0
  102. package/skills/torusguard/payload/core/v070_workflow.py +339 -0
  103. package/skills/torusguard/payload/core/v6_reporter.py +180 -0
  104. package/skills/torusguard/payload/core/v6_workflow.py +221 -0
  105. package/skills/torusguard/payload/core/watcher.py +58 -0
  106. package/skills/torusguard/payload/custom_rules/README.md +15 -0
  107. package/skills/torusguard/payload/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  108. package/skills/torusguard/payload/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  109. package/skills/torusguard/payload/rules/container/TG-CONT-001-root-user-execution.md +50 -50
  110. package/skills/torusguard/payload/rules/container/TG-CONT-002-docker-socket-mount.md +47 -47
  111. package/skills/torusguard/payload/rules/container/TG-CONT-003-privileged-container-mode.md +53 -53
  112. package/skills/torusguard/payload/rules/container/TG-CONT-004-build-arg-secret-exposure.md +43 -43
  113. package/skills/torusguard/payload/rules/git/TG-GIT-001-historical-secret-in-git-commit.md +44 -44
  114. package/skills/torusguard/payload/rules/git/TG-GIT-002-plaintext-credentials-in-git-config.md +41 -41
  115. package/skills/torusguard/payload/rules/git/TG-GIT-003-sensitive-tracked-file-gitignore-breach.md +40 -40
  116. package/skills/torusguard/payload/rules/rag/TG-RAG-001-untrusted-rag-context-injection.md +72 -72
  117. package/skills/torusguard/payload/rules/rag/TG-RAG-002-autonomous-llm-tool-unsandboxed-call.md +51 -51
  118. package/skills/torusguard/payload/rules/rag/TG-RAG-003-unpartitioned-vector-tenant-lookup.md +51 -51
  119. package/skills/torusguard/payload/rules/redos/TG-REDOS-001-catastrophic-exponential-backtracking.md +46 -46
  120. package/skills/torusguard/payload/rules/redos/TG-REDOS-002-unbounded-nested-quantifier.md +43 -43
  121. package/skills/torusguard/payload/scripts/audit_runner.py +108 -10
  122. package/skills/torusguard/payload/scripts/deliberation_tournament.py +127 -0
  123. package/skills/torusguard/payload/scripts/finding_scorer.py +43 -13
  124. package/skills/torusguard/payload/scripts/reachability_analyzer.py +164 -0
  125. package/skills/torusguard/payload/scripts/stride_generator.py +196 -0
  126. package/skills/torusguard/payload/skills/torusguard/SKILL.md +6 -2
  127. package/skills/torusguard/payload/skills/torusguard/bootstrap.py +3 -3
  128. package/skills/torusguard/payload/skills/torusguard-ai-guard/SKILL.md +95 -95
  129. package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +109 -84
  130. package/skills/torusguard/payload/skills/torusguard-container/SKILL.md +94 -94
  131. package/skills/torusguard/payload/skills/torusguard-git-mine/SKILL.md +92 -92
  132. package/skills/torusguard/payload/skills/torusguard-ocr-scan/SKILL.md +94 -94
  133. package/skills/torusguard/payload/skills/torusguard-redos/SKILL.md +91 -91
  134. package/skills/torusguard/payload/skills/torusguard-review/SKILL.md +27 -0
  135. package/skills/torusguard/payload/skills/torusguard-threatmodel/SKILL.md +27 -0
  136. package/skills/torusguard/payload/workflows/ai-guard.md +31 -31
  137. package/skills/torusguard/payload/workflows/audit.md +21 -17
  138. package/skills/torusguard/payload/workflows/container.md +29 -29
  139. package/skills/torusguard/payload/workflows/git-mine.md +25 -25
  140. package/skills/torusguard/payload/workflows/ocr-scan.md +25 -25
  141. package/skills/torusguard/payload/workflows/redos.md +27 -27
  142. package/skills/torusguard/payload/workflows/review.md +11 -0
  143. package/skills/torusguard/payload/workflows/threatmodel.md +9 -0
  144. package/skills/torusguard/payload/workflows/torusguard-audit.md +35 -55
  145. package/skills/torusguard/references/csharp-security.md +41 -41
  146. package/skills/torusguard/references/go-security.md +41 -41
  147. package/skills/torusguard/references/java-security.md +40 -40
  148. package/skills/torusguard/references/polyglot-security-matrix.md +25 -25
  149. package/skills/torusguard/references/rust-security.md +40 -40
  150. package/skills/torusguard-audit/SKILL.md +107 -83
  151. package/skills/torusguard-review/SKILL.md +27 -0
  152. package/skills/torusguard-threatmodel/SKILL.md +27 -0
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard Tri-Perspective Multi-Agent Deliberation Tournament Engine
4
+ Inspired by Alibaba Group's OpenCodeReview (OCR) hybrid architecture.
5
+ Coordinates consensus deliberation across:
6
+ 1. Security Hunter (Adversarial Attacker Perspective)
7
+ 2. Devil's Advocate (Defense & Sanitizer Verifier Perspective)
8
+ 3. Ponytail Remediator (Minimal Surgical Fix Formulator)
9
+ """
10
+
11
+ import sys
12
+ import json
13
+ import re
14
+ from pathlib import Path
15
+ from typing import Dict, List, Any
16
+
17
+
18
+ class DeliberationTournament:
19
+ def __init__(self, workspace_root: Path):
20
+ self.workspace_root = workspace_root
21
+
22
+ def evaluate_finding(self, finding: Dict[str, Any]) -> Dict[str, Any]:
23
+ rule_id = finding.get("rule_id", "TG-GENERIC")
24
+ file_path = finding.get("file", "")
25
+ line_num = finding.get("line", 1)
26
+ line_content = finding.get("line_content", "")
27
+ context_str = finding.get("context", "")
28
+
29
+ hunter_argument = f"Flagged potential violation of {rule_id} on {file_path}:{line_num}. Pattern match: '{line_content}'."
30
+
31
+ sanitizer_signals = [
32
+ "parseInt", "Number(", "sanitize", "escape", "validator", "z.string()",
33
+ "pydantic", "isinstance", "where: { tenantId", "requireAuth", "verifyToken",
34
+ "DOMPurify", "encodeURIComponent", "whitelist"
35
+ ]
36
+
37
+ disproven = False
38
+ rebuttal = "No enclosing sanitizer or invariant guard identified in AST context window."
39
+ for sig in sanitizer_signals:
40
+ if sig.lower() in context_str.lower() or sig.lower() in line_content.lower():
41
+ disproven = True
42
+ rebuttal = f"Devil's Advocate identified active defensive guard '{sig}' in context window, neutralizing exploitability."
43
+ break
44
+
45
+ if disproven:
46
+ verdict = "DISPROVEN_FALSE_POSITIVE"
47
+ consensus_score = 0.25
48
+ status = "Filtered"
49
+ else:
50
+ verdict = "CONFIRMED_VULNERABLE"
51
+ consensus_score = 0.92
52
+ status = "Approved"
53
+
54
+ remediation_strategy = finding.get("suggested_fix", "Apply parameterized bounds and enforce tenant partition.")
55
+ ponytail_compliance = {
56
+ "max_additions": 35,
57
+ "max_deletions": 25,
58
+ "bounded": True,
59
+ "surgical_strategy": remediation_strategy
60
+ }
61
+
62
+ return {
63
+ "finding_id": finding.get("id", f"{rule_id}-{line_num}"),
64
+ "rule_id": rule_id,
65
+ "file": file_path,
66
+ "line": line_num,
67
+ "verdict": verdict,
68
+ "consensus_score": consensus_score,
69
+ "status": status,
70
+ "deliberation": {
71
+ "hunter": hunter_argument,
72
+ "devils_advocate": rebuttal,
73
+ "remediator": ponytail_compliance
74
+ }
75
+ }
76
+
77
+ def deliberate_batch(self, findings: List[Dict[str, Any]]) -> Dict[str, Any]:
78
+ evaluated = []
79
+ confirmed_count = 0
80
+ filtered_count = 0
81
+
82
+ for f in findings:
83
+ result = self.evaluate_finding(f)
84
+ evaluated.append(result)
85
+ if result["verdict"] == "CONFIRMED_VULNERABLE":
86
+ confirmed_count += 1
87
+ else:
88
+ filtered_count += 1
89
+
90
+ return {
91
+ "total_candidates": len(findings),
92
+ "confirmed_count": confirmed_count,
93
+ "filtered_false_positives": filtered_count,
94
+ "precision_gain": f"{round((filtered_count / max(len(findings), 1)) * 100, 1)}%",
95
+ "results": evaluated
96
+ }
97
+
98
+
99
+ def main():
100
+ if hasattr(sys.stdout, "reconfigure"):
101
+ sys.stdout.reconfigure(encoding="utf-8")
102
+
103
+ input_data = []
104
+ if len(sys.argv) > 1 and sys.argv[1] != "-":
105
+ p = Path(sys.argv[1])
106
+ if p.is_file():
107
+ try:
108
+ input_data = json.loads(p.read_text(encoding="utf-8"))
109
+ except Exception:
110
+ pass
111
+ else:
112
+ if not sys.stdin.isatty():
113
+ try:
114
+ input_data = json.loads(sys.stdin.read())
115
+ except Exception:
116
+ pass
117
+
118
+ if not isinstance(input_data, list):
119
+ input_data = []
120
+
121
+ tournament = DeliberationTournament(Path("."))
122
+ report = tournament.deliberate_batch(input_data)
123
+ print(json.dumps(report, indent=2))
124
+
125
+
126
+ if __name__ == "__main__":
127
+ main()
@@ -11,9 +11,14 @@ import argparse
11
11
  from pathlib import Path
12
12
  from typing import Dict, Any, Tuple, Optional
13
13
 
14
+ # Ensure .torusguard directory is in sys.path for core imports
15
+ _TG_DIR = Path(__file__).resolve().parent.parent
16
+ if str(_TG_DIR) not in sys.path:
17
+ sys.path.insert(0, str(_TG_DIR))
18
+
14
19
 
15
20
  def compute_memory_boost(
16
- rule_id: str,
21
+ rule_id: Optional[str] = None,
17
22
  file_path: Optional[str] = None,
18
23
  root_dir: Optional[Path] = None
19
24
  ) -> int:
@@ -112,21 +117,35 @@ def compute_confidence_score(
112
117
  memory_boost: int = 0,
113
118
  rule_id: Optional[str] = None,
114
119
  file_path: Optional[str] = None,
115
- root_dir: Optional[Path] = None
120
+ root_dir: Optional[Path] = None,
121
+ taint_path_confirmed: bool = False,
122
+ taint_depth: Optional[int] = None,
123
+ sanitizer_present: bool = False,
124
+ rule_severity: str = "High",
125
+ use_evidence_chain: bool = False,
126
+ **kwargs
116
127
  ) -> Tuple[int, str, Dict[str, Any]]:
117
128
  """
118
129
  Computes total score and assigns confidence band.
119
- Max points:
120
- - evidence_quality: 35
121
- - reproduction_success: 25
122
- - independent_confirmations: 15
123
- - environmental_clarity: 15
124
- - manual_review_status: 10
125
- - memory_boost: -30 to +20 (modifier from persistent memory)
126
- - test_deduction: -30 if file is located in a test/mock path
127
- - doc_deduction: -25 if file is located in documentation
128
- Total is clamped to [0, 100].
130
+ Supports classical factor evaluation and multi-signal evidence-chain calibration.
129
131
  """
132
+ if use_evidence_chain:
133
+ try:
134
+ from core.confidence import ConfidenceCalibrator, EvidenceSignals
135
+ signals = EvidenceSignals(
136
+ rule_severity=rule_severity,
137
+ taint_path_confirmed=taint_path_confirmed,
138
+ taint_depth=taint_depth,
139
+ sanitizer_present=sanitizer_present,
140
+ framework_context_match=True,
141
+ has_multiline_evidence=(evidence_quality >= 30),
142
+ is_test_or_mock=is_test_path(file_path),
143
+ memory_boost=memory_boost or (compute_memory_boost(rule_id, file_path=file_path, root_dir=root_dir) if rule_id else 0)
144
+ )
145
+ return ConfidenceCalibrator.calculate_score(signals)
146
+ except Exception:
147
+ pass
148
+
130
149
  eq = min(max(evidence_quality, 0), 35)
131
150
  rs = min(max(reproduction_success, 0), 25)
132
151
  ic = min(max(independent_confirmations, 0), 15)
@@ -138,6 +157,13 @@ def compute_confidence_score(
138
157
  if rule_id and eff_mem_boost == 0:
139
158
  eff_mem_boost = compute_memory_boost(rule_id, file_path=file_path, root_dir=root_dir)
140
159
 
160
+ # Taint path adjustments
161
+ taint_mod = 0
162
+ if taint_path_confirmed:
163
+ taint_mod += 15
164
+ if sanitizer_present:
165
+ taint_mod -= 35
166
+
141
167
  # Test and Doc path noise suppression
142
168
  is_test = is_test_path(file_path)
143
169
  test_deduction = -30 if is_test else 0
@@ -145,7 +171,7 @@ def compute_confidence_score(
145
171
  is_doc = is_doc_path(file_path)
146
172
  doc_deduction = -25 if is_doc else 0
147
173
 
148
- raw_total = eq + rs + ic + ec + mr + eff_mem_boost + test_deduction + doc_deduction
174
+ raw_total = eq + rs + ic + ec + mr + eff_mem_boost + taint_mod + test_deduction + doc_deduction
149
175
  total = min(max(raw_total, 0), 100)
150
176
 
151
177
  if total >= 90:
@@ -164,6 +190,9 @@ def compute_confidence_score(
164
190
  "environmental_clarity": ec,
165
191
  "manual_review_status": mr,
166
192
  "memory_boost": eff_mem_boost,
193
+ "taint_path_confirmed": taint_path_confirmed,
194
+ "taint_depth": taint_depth,
195
+ "sanitizer_present": sanitizer_present,
167
196
  "test_exemption": is_test,
168
197
  "test_deduction": test_deduction,
169
198
  "total_score": total,
@@ -172,6 +201,7 @@ def compute_confidence_score(
172
201
  return total, band, factors
173
202
 
174
203
 
204
+
175
205
  def main():
176
206
  parser = argparse.ArgumentParser(description="TorusGuard Confidence Scorer")
177
207
  parser.add_argument("--dir", type=str, help="Target project root directory to scan and score")
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard Dependency Reachability & Vulnerability Exploitability eXchange (VEX) Engine
4
+ Analyzes AST import specifiers and function call sites against declared dependencies to
5
+ distinguish between actively reachable vulnerabilities and harmless dead transitive code.
6
+ """
7
+
8
+ import os
9
+ import re
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Dict, List, Set, Any
13
+
14
+
15
+ def extract_package_names_node(root_dir: Path) -> List[str]:
16
+ pkg_file = root_dir / "package.json"
17
+ if not pkg_file.is_file():
18
+ return []
19
+ try:
20
+ data = json.loads(pkg_file.read_text(encoding="utf-8"))
21
+ deps = list(data.get("dependencies", {}).keys()) + list(data.get("devDependencies", {}).keys())
22
+ return deps
23
+ except Exception:
24
+ return []
25
+
26
+
27
+ def extract_package_names_python(root_dir: Path) -> List[str]:
28
+ req_file = root_dir / "requirements.txt"
29
+ if not req_file.is_file():
30
+ return []
31
+ deps = []
32
+ try:
33
+ for line in req_file.read_text(encoding="utf-8").splitlines():
34
+ line = line.strip()
35
+ if line and not line.startswith("#"):
36
+ pkg = re.split(r"[=<>~;]", line)[0].strip()
37
+ if pkg:
38
+ deps.append(pkg)
39
+ except Exception:
40
+ pass
41
+ return deps
42
+
43
+
44
+ def extract_package_names_go(root_dir: Path) -> List[str]:
45
+ mod_file = root_dir / "go.mod"
46
+ if not mod_file.is_file():
47
+ return []
48
+ deps = []
49
+ try:
50
+ for line in mod_file.read_text(encoding="utf-8").splitlines():
51
+ line = line.strip()
52
+ if line.startswith("require "):
53
+ parts = line.split()
54
+ if len(parts) >= 2:
55
+ deps.append(parts[1])
56
+ elif not line.startswith("module") and not line.startswith("go ") and "/" in line:
57
+ parts = line.split()
58
+ if len(parts) >= 1:
59
+ deps.append(parts[0])
60
+ except Exception:
61
+ pass
62
+ return deps
63
+
64
+
65
+ def scan_source_imports(root_dir: Path) -> Set[str]:
66
+ """
67
+ Scans polyglot codebase for import/require/use statements.
68
+ """
69
+ imported_packages = set()
70
+ skip_dirs = {"node_modules", ".git", ".torusguard", "dist", "build", ".next", "__pycache__"}
71
+
72
+ # Patterns for imports
73
+ js_ts_pattern = re.compile(r"""(?:import\s+.*?\s+from\s+['"]|require\s*\(\s*['"])([@a-zA-Z0-9_\-\./]+)['"]""")
74
+ py_pattern = re.compile(r"""(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)""")
75
+ go_pattern = re.compile(r"""["']([a-zA-Z0-9_\-\./]+)["']""")
76
+
77
+ for root, dirs, files in os.walk(root_dir):
78
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
79
+ for f in files:
80
+ ext = os.path.splitext(f)[1].lower()
81
+ if ext not in {".js", ".jsx", ".ts", ".tsx", ".py", ".go"}:
82
+ continue
83
+ file_path = Path(root) / f
84
+ try:
85
+ content = file_path.read_text(encoding="utf-8", errors="ignore")
86
+ if ext in {".js", ".jsx", ".ts", ".tsx"}:
87
+ for match in js_ts_pattern.findall(content):
88
+ # Extract top-level package or scoped package (@scope/pkg)
89
+ parts = match.split("/")
90
+ if match.startswith("@") and len(parts) >= 2:
91
+ imported_packages.add(f"{parts[0]}/{parts[1]}")
92
+ elif parts:
93
+ imported_packages.add(parts[0])
94
+ elif ext == ".py":
95
+ for m1, m2 in py_pattern.findall(content):
96
+ pkg = m1 or m2
97
+ if pkg:
98
+ imported_packages.add(pkg)
99
+ elif ext == ".go":
100
+ for match in go_pattern.findall(content):
101
+ if "/" in match:
102
+ imported_packages.add(match)
103
+ except Exception:
104
+ continue
105
+
106
+ return imported_packages
107
+
108
+
109
+ def analyze_reachability(root_dir: Path) -> Dict[str, Any]:
110
+ declared = set()
111
+ declared.update(extract_package_names_node(root_dir))
112
+ declared.update(extract_package_names_python(root_dir))
113
+ declared.update(extract_package_names_go(root_dir))
114
+
115
+ imported = scan_source_imports(root_dir)
116
+
117
+ reachable = []
118
+ unreachable = []
119
+
120
+ for dep in sorted(declared):
121
+ is_used = False
122
+ dep_clean = dep.lower().replace("-", "_")
123
+ for imp in imported:
124
+ imp_clean = imp.lower().replace("-", "_")
125
+ if dep_clean == imp_clean or dep in imp or imp in dep:
126
+ is_used = True
127
+ break
128
+
129
+ if is_used:
130
+ reachable.append({
131
+ "package": dep,
132
+ "status": "REACHABLE",
133
+ "impact": "Code path actively imported in application source",
134
+ "vex_statement": "Affected / Call-path active"
135
+ })
136
+ else:
137
+ unreachable.append({
138
+ "package": dep,
139
+ "status": "UNREACHABLE",
140
+ "impact": "Declared dependency never imported or executed in reachable AST paths",
141
+ "vex_statement": "NotAffected / VexJustification: inline_component_not_present_or_called"
142
+ })
143
+
144
+ reachability_ratio = (len(reachable) / max(len(declared), 1)) * 100.0
145
+
146
+ return {
147
+ "total_dependencies": len(declared),
148
+ "reachable_count": len(reachable),
149
+ "unreachable_count": len(unreachable),
150
+ "reachability_percentage": round(reachability_ratio, 1),
151
+ "reachable_dependencies": reachable,
152
+ "unreachable_dependencies": unreachable,
153
+ }
154
+
155
+
156
+ def main():
157
+ import sys
158
+ target = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
159
+ result = analyze_reachability(target)
160
+ print(json.dumps(result, indent=2))
161
+
162
+
163
+ if __name__ == "__main__":
164
+ main()
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TorusGuard STRIDE Threat Modeling & Data Flow Diagram (DFD) Synthesizer
4
+ Automatically maps application entry points, trust boundaries, and datastores to generate
5
+ interactive Mermaid DFDs and structured STRIDE risk registers for security compliance.
6
+ """
7
+
8
+ import os
9
+ import re
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Dict, List, Set, Any
13
+
14
+
15
+ def discover_architecture_components(root_dir: Path) -> Dict[str, Any]:
16
+ entrypoints = []
17
+ datastores = set()
18
+ external_sinks = set()
19
+ trust_boundaries = ["Public Internet", "API Perimeter / DMZ", "Protected Core Services", "Isolated Data Tier"]
20
+
21
+ skip_dirs = {"node_modules", ".git", ".torusguard", "dist", "build", ".next", "__pycache__"}
22
+
23
+ # Patterns for HTTP routes
24
+ route_patterns = [
25
+ re.compile(r"""(?:app|router|server)\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]"""),
26
+ re.compile(r"""@(?:app|router)\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]"""),
27
+ re.compile(r"""http\.HandleFunc\s*\(\s*['"]([^'"]+)['"]"""),
28
+ ]
29
+
30
+ # Patterns for datastores & sinks
31
+ db_patterns = [
32
+ (re.compile(r"""(?i)(postgres|pg|prisma|typeorm|sequelize)"""), "PostgreSQL / Relational DB"),
33
+ (re.compile(r"""(?i)(mongo|mongoose)"""), "MongoDB / Document Store"),
34
+ (re.compile(r"""(?i)(redis|ioredis)"""), "Redis / In-Memory Cache"),
35
+ (re.compile(r"""(?i)(sqlite|sqlite3)"""), "SQLite / Local DB"),
36
+ ]
37
+
38
+ sink_patterns = [
39
+ (re.compile(r"""(?i)(stripe|paypal)"""), "Payment Gateway (Stripe/PayPal)"),
40
+ (re.compile(r"""(?i)(openai|anthropic|bedrock)"""), "AI LLM Provider (OpenAI/Anthropic)"),
41
+ (re.compile(r"""(?i)(aws-sdk|boto3|s3)"""), "Cloud Storage (AWS S3)"),
42
+ (re.compile(r"""(?i)(sendgrid|nodemailer|twilio)"""), "Communication Gateway (Twilio/SendGrid)"),
43
+ ]
44
+
45
+ for root, dirs, files in os.walk(root_dir):
46
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
47
+ for f in files:
48
+ ext = os.path.splitext(f)[1].lower()
49
+ if ext not in {".js", ".jsx", ".ts", ".tsx", ".py", ".go"}:
50
+ continue
51
+ file_path = Path(root) / f
52
+ try:
53
+ content = file_path.read_text(encoding="utf-8", errors="ignore")
54
+ rel_path = file_path.relative_to(root_dir).as_posix()
55
+
56
+ # Find routes
57
+ for pat in route_patterns:
58
+ for method, path in pat.findall(content):
59
+ entrypoints.append({
60
+ "method": method.upper(),
61
+ "path": path,
62
+ "file": rel_path
63
+ })
64
+
65
+ # Find DBs
66
+ for pat, name in db_patterns:
67
+ if pat.search(content):
68
+ datastores.add(name)
69
+
70
+ # Find Sinks
71
+ for pat, name in sink_patterns:
72
+ if pat.search(content):
73
+ external_sinks.add(name)
74
+
75
+ except Exception:
76
+ continue
77
+
78
+ return {
79
+ "entrypoints": entrypoints[:25],
80
+ "total_routes_detected": len(entrypoints),
81
+ "datastores": sorted(datastores) if datastores else ["Internal SQLite / Local Memory Store"],
82
+ "external_sinks": sorted(external_sinks) if external_sinks else ["Standard Outbound HTTPS Sinks"],
83
+ "trust_boundaries": trust_boundaries
84
+ }
85
+
86
+
87
+ def generate_mermaid_dfd(components: Dict[str, Any]) -> str:
88
+ lines = [
89
+ "```mermaid",
90
+ "flowchart TB",
91
+ " subgraph Zone1 [Public Internet / Client Tier]",
92
+ " User([Web / Mobile Client])",
93
+ " Attacker([Adversarial Threat Actor])",
94
+ " end",
95
+ "",
96
+ " subgraph Zone2 [API Perimeter & Trust Boundary]",
97
+ " Gateway[API Gateway / Router Handlers]"
98
+ ]
99
+
100
+ for i, ep in enumerate(components["entrypoints"][:4]):
101
+ lines.append(f" Route_{i}[{ep['method']} {ep['path']}]")
102
+
103
+ lines.extend([
104
+ " end",
105
+ "",
106
+ " subgraph Zone3 [Core Application Execution Tier]",
107
+ " AppEngine[TorusGuard Governed App Logic]",
108
+ " AuthModule[Authentication & Tenant Scoper]",
109
+ " end",
110
+ "",
111
+ " subgraph Zone4 [Data & External Integration Tier]"
112
+ ])
113
+
114
+ for i, db in enumerate(components["datastores"]):
115
+ lines.append(f" DB_{i}[({db})]")
116
+
117
+ for i, sink in enumerate(components["external_sinks"]):
118
+ lines.append(f" Sink_{i}[/{sink}/]")
119
+
120
+ lines.extend([
121
+ " end",
122
+ "",
123
+ " User -->|HTTP / TLS| Gateway",
124
+ " Attacker -.->|Untrusted Payloads| Gateway",
125
+ " Gateway --> AppEngine",
126
+ " AppEngine --> AuthModule",
127
+ ])
128
+
129
+ for i in range(len(components["datastores"])):
130
+ lines.append(f" AuthModule -->|Scoped Query| DB_{i}")
131
+
132
+ for i in range(len(components["external_sinks"])):
133
+ lines.append(f" AppEngine -->|Egress HTTPS| Sink_{i}")
134
+
135
+ lines.append("```")
136
+ return "\n".join(lines)
137
+
138
+
139
+ def generate_stride_report(root_dir: Path) -> str:
140
+ comp = discover_architecture_components(root_dir)
141
+ mermaid = generate_mermaid_dfd(comp)
142
+
143
+ report = f"""# TorusGuard Architectural Threat Model & STRIDE Register
144
+
145
+ **Generated by:** TorusGuard Threat Modeling Engine (v2.1.2)
146
+ **Workspace:** `{root_dir.resolve().as_posix()}`
147
+ **Analyzed Routes:** {comp['total_routes_detected']}
148
+ **Identified Data Stores:** {', '.join(comp['datastores'])}
149
+ **External Egress Sinks:** {', '.join(comp['external_sinks'])}
150
+
151
+ ---
152
+
153
+ ## 1. Architectural Data Flow Diagram (DFD)
154
+
155
+ {mermaid}
156
+
157
+ ---
158
+
159
+ ## 2. STRIDE Risk Assessment & Invariant Ledger
160
+
161
+ | STRIDE Category | Threat Description | Attack Vector | TorusGuard Guardrail | Mitigation Status |
162
+ | :--- | :--- | :--- | :---: | :---: |
163
+ | **Spoofing** | Attacker impersonates legitimate user or service via unverified JWT or forged credentials. | Forged token on `{comp['entrypoints'][0]['path'] if comp['entrypoints'] else '/api'}` | `TG-AUTH-001` / `TG-SEC-001` | **Enforced** |
164
+ | **Tampering** | Unsanitized parameter injection modifies database records or query logic across tenants. | Dynamic SQL concatenation or unpartitioned PK lookups | `TG-DB-001` / `TG-DB-002` | **Enforced** |
165
+ | **Repudiation** | Actions executed without persistent audit traces or immutable transaction locks. | Missing audit headers on state-changing API endpoints | `TG-BIZ-002` / `TG-AUDIT-001` | **Monitored** |
166
+ | **Information Disclosure** | Credentials, API tokens, or multi-tenant database partitions exposed to browser clients. | Bundled service keys or error stack traces | `TG-CLIENT-001` / `TG-DB-001` | **Enforced** |
167
+ | **Denial of Service** | Resource exhaustion via unbounded regex backtracking or unthrottled authentication endpoints. | Catastrophic ReDoS input or unrate-limited auth routes | `TG-REDOS-001` / `TG-RATE-001` | **Enforced** |
168
+ | **Elevation of Privilege** | Unsandboxed tool dispatch or privileged container execution breaks host isolation. | Docker socket mounts or missing role checks | `TG-CONT-002` / `TG-AGENT-002` | **Enforced** |
169
+
170
+ ---
171
+
172
+ ## 3. Trust Boundary Verification Checklist
173
+
174
+ - [x] **Boundary 1 (Internet to Gateway):** Enforce strict TLS, rate limiting, and CORS origin verification (`TG-RATE-001`, `TG-WS-001`).
175
+ - [x] **Boundary 2 (Gateway to App Core):** Mandatory tenant scoping and structural prompt isolation for LLMs (`TG-DB-001`, `TG-AGENT-001`).
176
+ - [x] **Boundary 3 (App Core to Data Stores):** Parameterized queries and fail-closed entropy validation (`TG-DB-002`, `TG-SEC-004`).
177
+ - [x] **Boundary 4 (App Core to External Sinks):** SSRF hostname whitelisting and private IP resolution blocking (`TG-SSRF-001`, `TG-SSRF-002`).
178
+
179
+ *Report automatically synced with workspace security posture ledger.*
180
+ """
181
+ return report
182
+
183
+
184
+ def main():
185
+ import sys
186
+ if hasattr(sys.stdout, "reconfigure"):
187
+ sys.stdout.reconfigure(encoding="utf-8")
188
+ target = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
189
+ report_content = generate_stride_report(target)
190
+ out_file = target / "SECURITY_THREAT_MODEL.md"
191
+ out_file.write_text(report_content, encoding="utf-8")
192
+ print(f"[+] Generated architectural STRIDE threat model: {out_file.as_posix()}")
193
+
194
+
195
+ if __name__ == "__main__":
196
+ main()
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: torusguard
3
- description: Universal autonomous security engine: 74 canonical rules across 18 families, polyglot stack detection across 16+ languages, Ponytail remediation bounds (<=35 add, <=25 del), standardized 75-column terminal UI, SARIF v2.1.0 exports, and persistent security memory context.
4
- version: 2.0.0
3
+ description: Universal autonomous security engine: 88 canonical rules across 22 families, polyglot stack detection across 16+ languages, Ponytail remediation bounds (<=35 add, <=25 del), standardized 75-column terminal UI, SARIF v2.1.0 exports, and persistent security memory context.
4
+ version: 2.1.1
5
5
  ---
6
6
 
7
7
  # TorusGuard Master Security Engine & Command Router
@@ -30,6 +30,10 @@ TorusGuard operates with 100% feature parity across the compiled terminal CLI, A
30
30
  | **Authorize** | `torusguard authorize` | `/torusguard authorize` | `.torusguard/skills/torusguard-authorize`| Legal scope definition & safety boundaries |
31
31
  | **Validate** | `torusguard web-validate` | `/torusguard web-validate` | `.torusguard/skills/torusguard-web-validate`| Authorized non-destructive HTTP probing |
32
32
  | **Exploit** | `torusguard exploit-check` | `/torusguard exploit-check`| `.torusguard/skills/torusguard-exploit-check`| Bounded single-step exploitability confirmation |
33
+ | **Container** | `torusguard container` | `/torusguard container` | `.torusguard/skills/torusguard-container` | Audits Dockerfile & Compose for root users, sockets, privileged mode |
34
+ | **Git Mine** | `torusguard git-mine` | `/torusguard git-mine` | `.torusguard/skills/torusguard-git-mine` | Mines git commit history & config for leaked credentials & tokens |
35
+ | **ReDoS** | `torusguard redos` | `/torusguard redos` | `.torusguard/skills/torusguard-redos` | Analyzes regex patterns for catastrophic exponential backtracking |
36
+ | **AI Guard** | `torusguard ai-guard` | `/torusguard ai-guard` | `.torusguard/skills/torusguard-ai-guard` | Audits AI agents & RAG pipelines for prompt injection & tenant leaks |
33
37
  | **Full** | `torusguard full` | `/torusguard full` | `.torusguard/skills/torusguard-full` | End-to-end 7-stage closed-loop execution |
34
38
  | **MCP Server**| `torusguard mcp` | — | Native Stdio JSON-RPC 2.0 | Serves native Model Context Protocol tools to AI coding agents |
35
39
  | **Update** | `torusguard update` | `/torusguard update` | `.torusguard/skills/torusguard-init` | Self-update TorusGuard engine binary |
@@ -144,7 +144,7 @@ def card_divider(title: str = "", border_color: str = CYAN, double: bool = False
144
144
  return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
145
145
  return f" {border_color}{left}{h * 71}{right}{RESET}"
146
146
 
147
- def card_header(title: str, subtitle: str = "", version: str = "v2.1.0", border_color: str = CYAN) -> str:
147
+ def card_header(title: str, subtitle: str = "", version: str = "v2.1.2", border_color: str = CYAN) -> str:
148
148
  """Generate standardized 75-column curved header box."""
149
149
  top = f" {border_color}╭{'─' * 71}╮{RESET}"
150
150
  bottom = f" {border_color}╰{'─' * 71}╯{RESET}"
@@ -164,7 +164,7 @@ def card_header(title: str, subtitle: str = "", version: str = "v2.1.0", border_
164
164
  def print_header():
165
165
  """Print the branded TorusGuard header card."""
166
166
  print()
167
- print(card_header("🛡️ T O R U S G U A R D", "Autonomous Security Engine for AI-Built Applications", version="v2.1.0"))
167
+ print(card_header("🛡️ T O R U S G U A R D", "Autonomous Security Engine for AI-Built Applications", version="v2.1.2"))
168
168
  print()
169
169
 
170
170
 
@@ -247,7 +247,7 @@ def print_already_initialized(target_root, cfg):
247
247
  print(f"""
248
248
  {BOLD}▸ Project Root:{RESET} {GREEN}{target_root}{RESET}
249
249
  {BOLD}▸ Workspace:{RESET} {GREEN}.torusguard/{RESET} {DIM}(Already Initialized){RESET}
250
- {BOLD}▸ Version:{RESET} {CYAN}{cfg.get('version', '2.1.0')}{RESET}
250
+ {BOLD}▸ Version:{RESET} {CYAN}{cfg.get('version', '2.1.1')}{RESET}
251
251
  {BOLD}▸ Severity Floor:{RESET} {YELLOW}{cfg.get('severity_threshold', 'medium')}{RESET}
252
252
 
253
253
  {DIM}To refresh templates or re-scaffold, run:{RESET}