torusguard 1.3.5 → 1.3.6
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.
- package/.torusguard/.manifest.json +35 -35
- package/.torusguard/runs/report-latest.html +2685 -303
- package/.torusguard/runs/results-latest.sarif +20 -0
- package/.torusguard/runs/run-20260918-104221-audit/findings.json +1 -0
- package/.torusguard/runs/run-20260918-104221-audit/findings.md +8 -0
- package/.torusguard/runs/run-20260918-104221-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260918-104221-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260918-104236-audit/findings.json +2314 -0
- package/.torusguard/runs/run-20260918-104236-audit/findings.md +960 -0
- package/.torusguard/runs/run-20260918-104236-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260918-104236-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260918-105723-audit/findings.json +2382 -0
- package/.torusguard/runs/run-20260918-105723-audit/findings.md +960 -0
- package/.torusguard/runs/run-20260918-105723-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260918-105723-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260918-105731-audit/findings.json +1 -0
- package/.torusguard/runs/run-20260918-105731-audit/findings.md +8 -0
- package/.torusguard/runs/run-20260918-105731-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260918-105731-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260918-105940-audit/findings.json +1 -0
- package/.torusguard/runs/run-20260918-105940-audit/findings.md +8 -0
- package/.torusguard/runs/run-20260918-105940-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260918-105940-audit/summary.md +5 -0
- package/.torusguard/scripts/__pycache__/apply_runner.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/harden_runner.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/html_reporter.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/memory_engine.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/monorepo_detector.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/recheck_runner.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/report_sync.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/run_manager.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/sarif_exporter.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/stack_detect.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/term_ui.cpython-314.pyc +0 -0
- package/.torusguard/scripts/apply_runner.py +11 -5
- package/.torusguard/scripts/audit_runner.py +222 -15
- package/.torusguard/scripts/harden_runner.py +3 -3
- package/.torusguard/scripts/html_reporter.py +2668 -648
- package/.torusguard/scripts/recheck_runner.py +3 -3
- package/.torusguard/scripts/recipes_runner.py +3 -3
- package/.torusguard/scripts/report_sync.py +370 -74
- package/.torusguard/scripts/sarif_exporter.py +48 -8
- package/.torusguard/scripts/stack_detect.py +44 -2
- package/.torusguard/scripts/term_ui.py +1 -1
- package/.torusguard/skills/torusguard/SKILL.md +75 -72
- package/.torusguard/skills/torusguard-apply/SKILL.md +84 -80
- package/.torusguard/skills/torusguard-audit/SKILL.md +87 -83
- package/.torusguard/skills/torusguard-authorize/SKILL.md +7 -2
- package/.torusguard/skills/torusguard-exploit-check/SKILL.md +7 -2
- package/.torusguard/skills/torusguard-full/SKILL.md +7 -2
- package/.torusguard/skills/torusguard-harden/SKILL.md +98 -94
- package/.torusguard/skills/torusguard-init/SKILL.md +68 -64
- package/.torusguard/skills/torusguard-recheck/SKILL.md +81 -77
- package/.torusguard/skills/torusguard-report/SKILL.md +93 -72
- package/.torusguard/skills/torusguard-status/SKILL.md +62 -58
- package/.torusguard/skills/torusguard-verify/SKILL.md +7 -2
- package/.torusguard/skills/torusguard-web-validate/SKILL.md +7 -2
- package/.torusguard/workflows/apply.md +1 -1
- package/.torusguard/workflows/audit.md +1 -1
- package/.torusguard/workflows/authorize.md +1 -1
- package/.torusguard/workflows/exploit-check.md +1 -1
- package/.torusguard/workflows/harden.md +1 -1
- package/.torusguard/workflows/init.md +1 -1
- package/.torusguard/workflows/memory.md +1 -1
- package/.torusguard/workflows/recheck.md +1 -1
- package/.torusguard/workflows/report.md +1 -1
- package/.torusguard/workflows/status.md +1 -1
- package/.torusguard/workflows/verify.md +1 -1
- package/.torusguard/workflows/web-validate.md +1 -1
- package/README.md +14 -8
- package/bin/torusguard.js +56 -20
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +1 -1
- package/skills/torusguard/bootstrap.py +7 -2
- package/skills/torusguard/payload/.manifest.json +35 -35
- package/skills/torusguard/payload/scripts/__pycache__/apply_runner.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/diff_guard.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/harden_runner.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/html_reporter.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/html_reporter.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/memory_engine.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/monorepo_detector.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/recheck_runner.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/report_sync.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/report_sync.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/rules_sync.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/run_manager.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/run_manager.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/sarif_exporter.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/stack_detect.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/stack_detect.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/term_ui.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/term_ui.cpython-314.pyc +0 -0
- package/skills/torusguard/payload/scripts/apply_runner.py +11 -5
- package/skills/torusguard/payload/scripts/audit_runner.py +222 -15
- package/skills/torusguard/payload/scripts/harden_runner.py +3 -3
- package/skills/torusguard/payload/scripts/html_reporter.py +2668 -648
- package/skills/torusguard/payload/scripts/recheck_runner.py +3 -3
- package/skills/torusguard/payload/scripts/recipes_runner.py +3 -3
- package/skills/torusguard/payload/scripts/report_sync.py +370 -74
- package/skills/torusguard/payload/scripts/sarif_exporter.py +48 -8
- package/skills/torusguard/payload/scripts/stack_detect.py +44 -2
- package/skills/torusguard/payload/scripts/term_ui.py +1 -1
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-apply/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-authorize/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-exploit-check/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-full/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-init/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-report/SKILL.md +40 -23
- package/skills/torusguard/payload/skills/torusguard-status/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-verify/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-web-validate/SKILL.md +1 -1
- package/skills/torusguard/payload/workflows/apply.md +1 -1
- package/skills/torusguard/payload/workflows/audit.md +1 -1
- package/skills/torusguard/payload/workflows/authorize.md +1 -1
- package/skills/torusguard/payload/workflows/exploit-check.md +1 -1
- package/skills/torusguard/payload/workflows/harden.md +1 -1
- package/skills/torusguard/payload/workflows/init.md +1 -1
- package/skills/torusguard/payload/workflows/memory.md +1 -1
- package/skills/torusguard/payload/workflows/recheck.md +1 -1
- package/skills/torusguard/payload/workflows/report.md +1 -1
- package/skills/torusguard/payload/workflows/status.md +1 -1
- package/skills/torusguard/payload/workflows/verify.md +1 -1
- package/skills/torusguard/payload/workflows/web-validate.md +1 -1
- package/skills/torusguard-apply/SKILL.md +1 -1
- package/skills/torusguard-audit/SKILL.md +1 -1
- package/skills/torusguard-authorize/SKILL.md +1 -1
- package/skills/torusguard-exploit-check/SKILL.md +1 -1
- package/skills/torusguard-full/SKILL.md +1 -1
- package/skills/torusguard-harden/SKILL.md +1 -1
- package/skills/torusguard-init/SKILL.md +1 -1
- package/skills/torusguard-recheck/SKILL.md +1 -1
- package/skills/torusguard-report/SKILL.md +40 -23
- package/skills/torusguard-status/SKILL.md +1 -1
- package/skills/torusguard-verify/SKILL.md +1 -1
- package/skills/torusguard-web-validate/SKILL.md +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"run_id": "run-20260918-105723-audit",
|
|
3
|
+
"command": "audit",
|
|
4
|
+
"target_name": "TorusGuard",
|
|
5
|
+
"created_at": "2026-09-18 10:57:23 IST",
|
|
6
|
+
"created_at_iso": "2026-09-18T10:57:23.372394+05:30",
|
|
7
|
+
"status": "completed",
|
|
8
|
+
"torusguard_version": "1.3.0",
|
|
9
|
+
"findings_count": 68,
|
|
10
|
+
"confirmed_fixed_count": 0,
|
|
11
|
+
"regressed_count": 0
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"run_id": "run-20260918-105731-audit",
|
|
3
|
+
"command": "audit",
|
|
4
|
+
"target_name": "TorusGuard",
|
|
5
|
+
"created_at": "2026-09-18 10:57:31 IST",
|
|
6
|
+
"created_at_iso": "2026-09-18T10:57:31.006888+05:30",
|
|
7
|
+
"status": "completed",
|
|
8
|
+
"torusguard_version": "1.3.0",
|
|
9
|
+
"findings_count": 0,
|
|
10
|
+
"confirmed_fixed_count": 0,
|
|
11
|
+
"regressed_count": 0
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"run_id": "run-20260918-105940-audit",
|
|
3
|
+
"command": "audit",
|
|
4
|
+
"target_name": "TorusGuard",
|
|
5
|
+
"created_at": "2026-09-18 10:59:40 IST",
|
|
6
|
+
"created_at_iso": "2026-09-18T10:59:40.691005+05:30",
|
|
7
|
+
"status": "completed",
|
|
8
|
+
"torusguard_version": "1.3.0",
|
|
9
|
+
"findings_count": 0,
|
|
10
|
+
"confirmed_fixed_count": 0,
|
|
11
|
+
"regressed_count": 0
|
|
12
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
TorusGuard Governed Patch Application & Snapshot Engine (v1.3.
|
|
3
|
+
TorusGuard Governed Patch Application & Snapshot Engine (v1.3.6)
|
|
4
4
|
Interactive Human Gate authorization, pre-apply .bak rollback snapshots,
|
|
5
5
|
atomic patch application, and Golden Fix Recipe distillation into persistent memory.
|
|
6
6
|
Strict 75-column terminal UI formatting.
|
|
@@ -58,7 +58,7 @@ def box_line(content: str, width: int = 67, border: str = "│", border_color: s
|
|
|
58
58
|
return term_ui.format_box_line(content, width=width, border=border, border_color=border_color)
|
|
59
59
|
return f" {border_color}{border}{RESET} {content}"
|
|
60
60
|
|
|
61
|
-
def box_header(title: str, subtitle: str = "", version: str = "v1.3.
|
|
61
|
+
def box_header(title: str, subtitle: str = "", version: str = "v1.3.6", border_color: str = CYAN) -> str:
|
|
62
62
|
if term_ui:
|
|
63
63
|
return term_ui.card_header(title, subtitle, version, border_color)
|
|
64
64
|
return f"=== {title} ({version}) ==="
|
|
@@ -108,8 +108,14 @@ def execute_rollback(target_root: Path, run_id_arg: Optional[str] = None) -> Non
|
|
|
108
108
|
shutil.copy2(bak_file, dest_file)
|
|
109
109
|
restored_files.append(orig_rel)
|
|
110
110
|
|
|
111
|
+
try:
|
|
112
|
+
import report_sync
|
|
113
|
+
report_sync.record_rollback_results(target_root, restored_files, snapshot_dir.name)
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
|
|
111
117
|
print()
|
|
112
|
-
print(box_header("🛡️ TORUSGUARD ROLLBACK RESTORATION", "Restored original files from pre-apply snapshot", "v1.3.
|
|
118
|
+
print(box_header("🛡️ TORUSGUARD ROLLBACK RESTORATION", "Restored original files from pre-apply snapshot", "v1.3.6"))
|
|
113
119
|
print()
|
|
114
120
|
|
|
115
121
|
print(border_top("Rollback Snapshot Restored"))
|
|
@@ -138,7 +144,7 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
138
144
|
bundle_meta_files = list(bundles_dir.glob("*/metadata.json")) if bundles_dir.is_dir() else []
|
|
139
145
|
if not bundle_meta_files:
|
|
140
146
|
print()
|
|
141
|
-
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.
|
|
147
|
+
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.6"))
|
|
142
148
|
print()
|
|
143
149
|
print(f" {YELLOW}ℹ Zero candidate remediation bundles found in run: {run_folder.name}{RESET}")
|
|
144
150
|
print(f" {GRAY}The findings in this run require manual review or AI-assisted remediation.{RESET}\n")
|
|
@@ -163,7 +169,7 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
163
169
|
|
|
164
170
|
# Header Card
|
|
165
171
|
print()
|
|
166
|
-
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.
|
|
172
|
+
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.6"))
|
|
167
173
|
print()
|
|
168
174
|
|
|
169
175
|
print(border_top("Candidate Bundles Ready for Application"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
TorusGuard Autonomous Static Security Audit Engine (v1.3.
|
|
3
|
+
TorusGuard Autonomous Static Security Audit Engine (v1.3.6)
|
|
4
4
|
Multi-language static pattern & AST security scanner.
|
|
5
5
|
Evaluates source trees against canonical TorusGuard rule families,
|
|
6
6
|
augments confidence via the persistent security memory subsystem,
|
|
@@ -117,7 +117,7 @@ except Exception:
|
|
|
117
117
|
rem = max(0, 68 - vis)
|
|
118
118
|
return f" {border_color}{left}{h} {BOLD}{WHITE}{title}{RESET}{border_color} {h * rem}{right}{RESET}"
|
|
119
119
|
return f" {border_color}{left}{h * 71}{right}{RESET}"
|
|
120
|
-
def card_header(title: str, subtitle: str = "", version: str = "v1.3.
|
|
120
|
+
def card_header(title: str, subtitle: str = "", version: str = "v1.3.6", border_color: str = CYAN) -> str:
|
|
121
121
|
top = f" {border_color}╭{'─' * 71}╮{RESET}"
|
|
122
122
|
bottom = f" {border_color}╰{'─' * 71}╯{RESET}"
|
|
123
123
|
empty = f" {border_color}│{' ' * 71}│{RESET}"
|
|
@@ -948,6 +948,153 @@ RULE_PATTERNS: List[RulePattern] = [
|
|
|
948
948
|
}
|
|
949
949
|
]
|
|
950
950
|
|
|
951
|
+
# ─── Rule Precision Map ───────────────────────────────────────────────────────
|
|
952
|
+
# Calibrates how precise each rule's regex is (0.0 = very broad, 1.0 = definitive)
|
|
953
|
+
# Used to adjust confidence scores: broad rules get lower base evidence quality.
|
|
954
|
+
RULE_PRECISION: Dict[str, float] = {
|
|
955
|
+
"TG-SEC-001": 0.90, "TG-SEC-002": 0.85, "TG-SEC-003": 0.70,
|
|
956
|
+
"TG-SEC-004": 0.55, "TG-SEC-005": 0.80, "TG-SEC-006": 0.75,
|
|
957
|
+
"TG-SEC-007": 0.95,
|
|
958
|
+
"TG-AUTH-001": 0.85, "TG-AUTH-002": 0.50, "TG-AUTH-003": 0.70,
|
|
959
|
+
"TG-AUTH-004": 0.80, "TG-AUTH-005": 0.75, "TG-AUTH-006": 0.65,
|
|
960
|
+
"TG-AUTH-007": 0.60, "TG-AUTH-008": 0.70,
|
|
961
|
+
"TG-DB-001": 0.75, "TG-DB-002": 0.90, "TG-DB-003": 0.80, "TG-DB-004": 0.55,
|
|
962
|
+
"TG-INPUT-001": 0.40, "TG-INPUT-002": 0.85, "TG-INPUT-003": 0.35,
|
|
963
|
+
"TG-INPUT-004": 0.80, "TG-INPUT-005": 0.75, "TG-INPUT-006": 0.70,
|
|
964
|
+
"TG-RATE-001": 0.45, "TG-RATE-002": 0.45, "TG-RATE-003": 0.50,
|
|
965
|
+
"TG-AGENT-001": 0.80, "TG-AGENT-002": 0.85, "TG-AGENT-003": 0.60,
|
|
966
|
+
"TG-AGENT-004": 0.55,
|
|
967
|
+
"TG-SSRF-001": 0.75, "TG-SSRF-002": 0.90, "TG-SSRF-003": 0.50,
|
|
968
|
+
"TG-SSRF-004": 0.65,
|
|
969
|
+
"TG-WEBHOOK-001": 0.55, "TG-WEBHOOK-002": 0.50, "TG-WEBHOOK-003": 0.45,
|
|
970
|
+
"TG-WEBHOOK-004": 0.60,
|
|
971
|
+
"TG-WS-001": 0.50, "TG-WS-002": 0.55, "TG-WS-003": 0.50, "TG-WS-004": 0.55,
|
|
972
|
+
"TG-CSRF-001": 0.60, "TG-CSRF-002": 0.80,
|
|
973
|
+
"TG-GQL-001": 0.55, "TG-GQL-002": 0.50, "TG-GQL-003": 0.50, "TG-GQL-004": 0.85,
|
|
974
|
+
"TG-SUPPLY-001": 0.90, "TG-SUPPLY-002": 0.80, "TG-SUPPLY-003": 0.85,
|
|
975
|
+
"TG-SUPPLY-004": 0.60, "TG-SUPPLY-005": 0.80, "TG-SUPPLY-006": 0.85,
|
|
976
|
+
"TG-BIZ-001": 0.60, "TG-BIZ-002": 0.55, "TG-BIZ-003": 0.65, "TG-BIZ-004": 0.70,
|
|
977
|
+
"TG-CACHE-001": 0.65, "TG-CACHE-002": 0.50, "TG-CACHE-003": 0.55,
|
|
978
|
+
"TG-CLIENT-001": 0.80, "TG-CLIENT-002": 0.75,
|
|
979
|
+
"TG-PLATFORM-001": 0.70, "TG-PLATFORM-002": 0.50, "TG-PLATFORM-003": 0.75,
|
|
980
|
+
"TG-PLATFORM-004": 0.60,
|
|
981
|
+
"TG-DIFF-001": 0.90, "TG-DIFF-002": 0.85, "TG-DIFF-003": 0.80,
|
|
982
|
+
"TG-EDGE-001": 0.55, "TG-EDGE-003": 0.60,
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
# ─── File-Type Discrimination ─────────────────────────────────────────────────
|
|
986
|
+
# Maps file path patterns to sets of rule families that should be SUPPRESSED
|
|
987
|
+
FILE_TYPE_SUPPRESSIONS: List[Tuple[re.Pattern, set]] = [
|
|
988
|
+
# Config & build tool files — suppress injection rules for eval/exec
|
|
989
|
+
(re.compile(r'(?:webpack|vite|rollup|esbuild|jest|vitest|babel|eslint|prettier|postcss|tailwind)\.config', re.IGNORECASE),
|
|
990
|
+
{"TG-INPUT-003"}),
|
|
991
|
+
# Type declaration files — no runtime code
|
|
992
|
+
(re.compile(r'\.d\.ts$'),
|
|
993
|
+
{"TG-INPUT-001", "TG-INPUT-002", "TG-INPUT-003", "TG-INPUT-004", "TG-INPUT-005",
|
|
994
|
+
"TG-RATE-001", "TG-RATE-002", "TG-RATE-003", "TG-DB-001", "TG-DB-004"}),
|
|
995
|
+
# .env.example / .env.sample — template files, not real credentials
|
|
996
|
+
(re.compile(r'\.env\.(?:example|sample|template|defaults)$', re.IGNORECASE),
|
|
997
|
+
{"TG-SEC-003", "TG-SEC-001"}),
|
|
998
|
+
# Lock files — suppress secret scanning
|
|
999
|
+
(re.compile(r'(?:package-lock|yarn\.lock|pnpm-lock|Pipfile\.lock|poetry\.lock)'),
|
|
1000
|
+
{"TG-SEC-001", "TG-SEC-005"}),
|
|
1001
|
+
# Migration files — suppress input validation rules (generated SQL)
|
|
1002
|
+
(re.compile(r'/migrations?/', re.IGNORECASE),
|
|
1003
|
+
{"TG-INPUT-001", "TG-INPUT-002", "TG-RATE-001"}),
|
|
1004
|
+
# Dockerfiles — suppress frontend-only rules, keep container-specific rules
|
|
1005
|
+
(re.compile(r'Dockerfile', re.IGNORECASE),
|
|
1006
|
+
{"TG-INPUT-003", "TG-RATE-001", "TG-RATE-002", "TG-DB-001", "TG-GQL-001"}),
|
|
1007
|
+
]
|
|
1008
|
+
|
|
1009
|
+
def get_suppressed_rules_for_file(file_path: str) -> set:
|
|
1010
|
+
"""Return the set of rule IDs that should be suppressed for this file type."""
|
|
1011
|
+
suppressed: set = set()
|
|
1012
|
+
for pattern, rules in FILE_TYPE_SUPPRESSIONS:
|
|
1013
|
+
if pattern.search(file_path):
|
|
1014
|
+
suppressed.update(rules)
|
|
1015
|
+
return suppressed
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
# ─── Context-Aware Validation Engine ──────────────────────────────────────────
|
|
1019
|
+
def validate_finding_context(
|
|
1020
|
+
rule_id: str, line_idx: int, lines: List[str],
|
|
1021
|
+
file_path: str, full_content: str
|
|
1022
|
+
) -> bool:
|
|
1023
|
+
"""
|
|
1024
|
+
Post-match context validation. Returns False if the finding is a known
|
|
1025
|
+
false-positive pattern based on surrounding code context.
|
|
1026
|
+
"""
|
|
1027
|
+
# Compute lookahead (next 5 lines) and lookbehind (prev 5 lines)
|
|
1028
|
+
lookahead = "\n".join(lines[line_idx:min(line_idx + 6, len(lines))])
|
|
1029
|
+
lookbehind = "\n".join(lines[max(0, line_idx - 5):line_idx])
|
|
1030
|
+
|
|
1031
|
+
# ── TG-INPUT-001: req.body destructuring followed by schema validation ────
|
|
1032
|
+
if rule_id == "TG-INPUT-001":
|
|
1033
|
+
if re.search(r'\.(parse|validate|safeParse|check|verify|sanitize)\s*\(', lookahead):
|
|
1034
|
+
return False
|
|
1035
|
+
# Zod/Joi/Yup schema declared before destructuring
|
|
1036
|
+
if re.search(r'(?:z\.|Joi\.|yup\.|schema\.).*\.(parse|validate)', lookbehind + lookahead):
|
|
1037
|
+
return False
|
|
1038
|
+
|
|
1039
|
+
# ── TG-INPUT-003: eval/exec in sandboxed or build-tool context ────────────
|
|
1040
|
+
if rule_id == "TG-INPUT-003":
|
|
1041
|
+
current_line = lines[line_idx].strip() if line_idx < len(lines) else ""
|
|
1042
|
+
# Suppress if it's a JSON.parse (not dangerous)
|
|
1043
|
+
if "JSON.parse" in current_line or "json.loads" in current_line:
|
|
1044
|
+
return False
|
|
1045
|
+
# Suppress eval in CSS-in-JS tagged templates
|
|
1046
|
+
if re.search(r'styled|css`|keyframes', lookbehind):
|
|
1047
|
+
return False
|
|
1048
|
+
|
|
1049
|
+
# ── TG-RATE-001/002: Rate limiter already applied earlier in file ─────────
|
|
1050
|
+
if rule_id in ("TG-RATE-001", "TG-RATE-002"):
|
|
1051
|
+
if re.search(r'(?:rateLimit|rateLimiter|slowDown|throttle|express-rate-limit)\s*\(', full_content):
|
|
1052
|
+
return False
|
|
1053
|
+
# Check if middleware is applied on the same router
|
|
1054
|
+
if re.search(r'app\.use\s*\([^)]*(?:rateLimit|limiter|throttle)', full_content):
|
|
1055
|
+
return False
|
|
1056
|
+
|
|
1057
|
+
# ── TG-PLATFORM-002: Helmet used anywhere in the file or app setup ────────
|
|
1058
|
+
if rule_id == "TG-PLATFORM-002":
|
|
1059
|
+
if "helmet" in full_content and re.search(r'(?:app|server)\.use\s*\(\s*helmet', full_content):
|
|
1060
|
+
return False
|
|
1061
|
+
|
|
1062
|
+
# ── TG-SSRF-004: requests.get with timeout in kwargs or session defaults ──
|
|
1063
|
+
if rule_id == "TG-SSRF-004":
|
|
1064
|
+
# Check if a session with default timeout is set earlier
|
|
1065
|
+
if re.search(r'session\.(?:request\.)?timeout|DEFAULT_TIMEOUT', full_content):
|
|
1066
|
+
return False
|
|
1067
|
+
|
|
1068
|
+
# ── TG-AUTH-003: JWT verify with algorithms in options object ──────────────
|
|
1069
|
+
if rule_id == "TG-AUTH-003":
|
|
1070
|
+
if re.search(r'algorithms\s*:', lookahead):
|
|
1071
|
+
return False
|
|
1072
|
+
|
|
1073
|
+
# ── TG-GQL-001: ApolloServer with plugins/validation rules elsewhere ──────
|
|
1074
|
+
if rule_id == "TG-GQL-001":
|
|
1075
|
+
if re.search(r'(?:depthLimit|costAnalysis|complexityLimit|validationRules)', full_content):
|
|
1076
|
+
return False
|
|
1077
|
+
|
|
1078
|
+
# ── TG-SEC-004: Logging that redacts sensitive fields ──────────────────────
|
|
1079
|
+
if rule_id == "TG-SEC-004":
|
|
1080
|
+
if re.search(r'(?:redact|mask|sanitize|\*{3,}|\[REDACTED\])', lookahead):
|
|
1081
|
+
return False
|
|
1082
|
+
|
|
1083
|
+
# ── TG-DB-004: findById with ownership check in next few lines ────────────
|
|
1084
|
+
if rule_id == "TG-DB-004":
|
|
1085
|
+
if re.search(r'(?:tenantId|tenant_id|organization_id|user\.id|userId|ownerId)', lookahead):
|
|
1086
|
+
return False
|
|
1087
|
+
|
|
1088
|
+
# ── TG-WEBHOOK-001: Signature verification in handler body ────────────────
|
|
1089
|
+
if rule_id == "TG-WEBHOOK-001":
|
|
1090
|
+
# Check if HMAC/signature verification happens in the function body
|
|
1091
|
+
func_body = "\n".join(lines[line_idx:min(line_idx + 20, len(lines))])
|
|
1092
|
+
if re.search(r'(?:verifySignature|hmac|createHmac|verify_webhook|constructEvent)', func_body):
|
|
1093
|
+
return False
|
|
1094
|
+
|
|
1095
|
+
return True # Keep the finding
|
|
1096
|
+
|
|
1097
|
+
|
|
951
1098
|
# File extensions to scan
|
|
952
1099
|
SOURCE_EXTENSIONS = {
|
|
953
1100
|
".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
@@ -962,11 +1109,16 @@ SKIP_DIRS = {
|
|
|
962
1109
|
}
|
|
963
1110
|
|
|
964
1111
|
def is_test_file(path_str: str) -> bool:
|
|
965
|
-
"""Check if file belongs to tests, fixtures, or test harnesses."""
|
|
966
|
-
clean = path_str.replace("\\", "/").lower()
|
|
967
|
-
markers = [
|
|
968
|
-
|
|
969
|
-
|
|
1112
|
+
"""Check if file belongs to tests, fixtures, or test harnesses across any language."""
|
|
1113
|
+
clean = "/" + path_str.replace("\\", "/").strip("/").lower() + "/"
|
|
1114
|
+
markers = [
|
|
1115
|
+
"/test/", "/tests/", "/spec/", "/specs/", "/fixtures/", "/mock/", "/mocks/",
|
|
1116
|
+
"/harness/", "/qa-runs/", "/test_repos/", "/__tests__/"
|
|
1117
|
+
]
|
|
1118
|
+
if any(m in clean for m in markers):
|
|
1119
|
+
return True
|
|
1120
|
+
filename = Path(path_str).name.lower()
|
|
1121
|
+
return filename.startswith("test_") or "_test." in filename or ".test." in filename or ".spec." in filename
|
|
970
1122
|
|
|
971
1123
|
|
|
972
1124
|
def find_files_to_scan(target_root: Path, include_tests: bool = False) -> List[Path]:
|
|
@@ -987,7 +1139,10 @@ def find_files_to_scan(target_root: Path, include_tests: bool = False) -> List[P
|
|
|
987
1139
|
|
|
988
1140
|
|
|
989
1141
|
def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
990
|
-
"""Scan a single source file against the TorusGuard rule families.
|
|
1142
|
+
"""Scan a single source file against the TorusGuard rule families.
|
|
1143
|
+
Enhanced with context-aware validation, file-type discrimination,
|
|
1144
|
+
developer suppression comments, canary tags, and multi-finding support.
|
|
1145
|
+
"""
|
|
991
1146
|
findings = []
|
|
992
1147
|
try:
|
|
993
1148
|
content = file_path.read_text(encoding="utf-8", errors="replace")
|
|
@@ -998,13 +1153,24 @@ def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
|
998
1153
|
rel_path = str(file_path.relative_to(target_root)).replace("\\", "/")
|
|
999
1154
|
is_test = is_test_file(rel_path)
|
|
1000
1155
|
|
|
1156
|
+
# Phase 1c: File-type discrimination — get suppressed rules for this file
|
|
1157
|
+
suppressed_rules = get_suppressed_rules_for_file(rel_path)
|
|
1158
|
+
|
|
1001
1159
|
for rule in RULE_PATTERNS:
|
|
1002
1160
|
rule_id = rule["rule_id"]
|
|
1003
|
-
|
|
1161
|
+
|
|
1162
|
+
# Skip rules suppressed by file-type discrimination
|
|
1163
|
+
if rule_id in suppressed_rules:
|
|
1004
1164
|
continue
|
|
1165
|
+
|
|
1005
1166
|
patterns: List[Tuple[Pattern[str], str]] = rule["patterns"]
|
|
1167
|
+
found_lines: set = set() # Track matched lines to avoid duplicates
|
|
1168
|
+
|
|
1006
1169
|
for regex, desc in patterns:
|
|
1007
1170
|
for idx, line in enumerate(lines):
|
|
1171
|
+
if idx in found_lines:
|
|
1172
|
+
continue # Already reported a finding on this line for this rule
|
|
1173
|
+
|
|
1008
1174
|
stripped = line.strip()
|
|
1009
1175
|
if stripped.startswith(("#", "//", "/*", "*")):
|
|
1010
1176
|
if "bypass" not in stripped.lower() and "todo" not in stripped.lower():
|
|
@@ -1012,6 +1178,27 @@ def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
|
1012
1178
|
|
|
1013
1179
|
match = regex.search(line)
|
|
1014
1180
|
if match:
|
|
1181
|
+
line_lower = line.lower()
|
|
1182
|
+
prev_line = lines[idx - 1].lower() if idx > 0 else ""
|
|
1183
|
+
|
|
1184
|
+
# Developer suppression annotations (e.g. # torusguard: ignore, // tg-ignore)
|
|
1185
|
+
if "torusguard: ignore" in line_lower or "torusguard: ignore" in prev_line or \
|
|
1186
|
+
"tg-ignore" in line_lower or "tg-ignore" in prev_line:
|
|
1187
|
+
if rule_id != "TG-DIFF-001":
|
|
1188
|
+
continue
|
|
1189
|
+
|
|
1190
|
+
# Developer test canary annotations (# torusguard: test-canary)
|
|
1191
|
+
is_canary = (
|
|
1192
|
+
"torusguard: test-canary" in line_lower or "torusguard: test-canary" in prev_line or
|
|
1193
|
+
"tg-canary" in line_lower or "tg-canary" in prev_line or
|
|
1194
|
+
"test-canary" in line_lower or "test-canary" in prev_line or
|
|
1195
|
+
is_test
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
# Phase 1a: Context-aware false positive suppression
|
|
1199
|
+
if not validate_finding_context(rule_id, idx, lines, rel_path, content):
|
|
1200
|
+
continue # Suppressed by context validation
|
|
1201
|
+
|
|
1015
1202
|
line_num = idx + 1
|
|
1016
1203
|
seed = f"{rule_id}:{rel_path}:{line_num}:{stripped}"
|
|
1017
1204
|
fp = f"{rule_id}-{hashlib.sha256(seed.encode('utf-8')).hexdigest()[:8]}"
|
|
@@ -1025,7 +1212,7 @@ def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
|
1025
1212
|
"rule_id": rule_id,
|
|
1026
1213
|
"title": rule["title"],
|
|
1027
1214
|
"description": desc,
|
|
1028
|
-
"severity": "Low" if is_test else rule["severity"],
|
|
1215
|
+
"severity": "Low" if (is_test or is_canary) else rule["severity"],
|
|
1029
1216
|
"category": rule["category"],
|
|
1030
1217
|
"cluster": rule["cluster"],
|
|
1031
1218
|
"file_path": rel_path,
|
|
@@ -1033,13 +1220,15 @@ def scan_file(file_path: Path, target_root: Path) -> List[Dict[str, Any]]:
|
|
|
1033
1220
|
"matched_text": match.group(0)[:80],
|
|
1034
1221
|
"snippet": snippet,
|
|
1035
1222
|
"is_test": is_test,
|
|
1223
|
+
"is_canary": is_canary,
|
|
1224
|
+
"rule_precision": RULE_PRECISION.get(rule_id, 0.60),
|
|
1036
1225
|
"target": {
|
|
1037
1226
|
"file_path": rel_path,
|
|
1038
1227
|
"start_line": line_num,
|
|
1039
1228
|
"end_line": line_num
|
|
1040
1229
|
}
|
|
1041
1230
|
})
|
|
1042
|
-
|
|
1231
|
+
found_lines.add(idx)
|
|
1043
1232
|
return findings
|
|
1044
1233
|
|
|
1045
1234
|
|
|
@@ -1056,6 +1245,15 @@ def score_and_cluster_findings(findings: List[Dict[str, Any]], target_root: Path
|
|
|
1056
1245
|
scored = []
|
|
1057
1246
|
clusters: Dict[str, List[Dict[str, Any]]] = {}
|
|
1058
1247
|
|
|
1248
|
+
# Phase 1d: Pre-compute cross-file corroboration bonus
|
|
1249
|
+
# If multiple rule families flag the same file, each finding gets +5 confidence
|
|
1250
|
+
file_rule_families: Dict[str, set] = {}
|
|
1251
|
+
for f in findings:
|
|
1252
|
+
fp = f["file_path"]
|
|
1253
|
+
if fp not in file_rule_families:
|
|
1254
|
+
file_rule_families[fp] = set()
|
|
1255
|
+
file_rule_families[fp].add(f["category"])
|
|
1256
|
+
|
|
1059
1257
|
for f in findings:
|
|
1060
1258
|
score = 70
|
|
1061
1259
|
band = "High Confidence"
|
|
@@ -1063,10 +1261,19 @@ def score_and_cluster_findings(findings: List[Dict[str, Any]], target_root: Path
|
|
|
1063
1261
|
|
|
1064
1262
|
if finding_scorer:
|
|
1065
1263
|
try:
|
|
1066
|
-
|
|
1264
|
+
# Phase 1d: Dynamic evidence quality based on rule precision
|
|
1265
|
+
precision = f.get("rule_precision", RULE_PRECISION.get(f["rule_id"], 0.60))
|
|
1266
|
+
eq = max(10, int(35 * precision)) # Scale evidence quality by precision
|
|
1067
1267
|
rs = 0
|
|
1068
|
-
|
|
1069
|
-
|
|
1268
|
+
|
|
1269
|
+
# Corroboration bonus: multiple rule families in same file
|
|
1270
|
+
families_in_file = len(file_rule_families.get(f["file_path"], set()))
|
|
1271
|
+
ic = min(15, 5 + (families_in_file - 1) * 3) if families_in_file > 1 else 5
|
|
1272
|
+
|
|
1273
|
+
# Environmental clarity: production files score higher
|
|
1274
|
+
is_prod = not f.get("is_test", False)
|
|
1275
|
+
ec = 15 if is_prod else 5
|
|
1276
|
+
|
|
1070
1277
|
mr = 0
|
|
1071
1278
|
s, b, facts = finding_scorer.compute_confidence_score(
|
|
1072
1279
|
evidence_quality=eq,
|
|
@@ -1166,7 +1373,7 @@ def print_audit_dashboard(target_root: Path, file_count: int, scored_findings: L
|
|
|
1166
1373
|
stack_str = f"{detected_stack.get('framework')} ({detected_stack.get('language')})"
|
|
1167
1374
|
|
|
1168
1375
|
print()
|
|
1169
|
-
print(card_header("🛡️ TORUSGUARD STATIC SECURITY AUDIT", "Autonomous AST & Invariant Security Scanner", "v1.3.
|
|
1376
|
+
print(card_header("🛡️ TORUSGUARD STATIC SECURITY AUDIT", "Autonomous AST & Invariant Security Scanner", "v1.3.6"))
|
|
1170
1377
|
print()
|
|
1171
1378
|
|
|
1172
1379
|
print(card_border_top("Audit Execution Scope"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
TorusGuard Autonomous Remediation Engine (v1.3.
|
|
3
|
+
TorusGuard Autonomous Remediation Engine (v1.3.6)
|
|
4
4
|
Evaluates audit findings against canonical patch templates, enforces Ponytail bounds
|
|
5
5
|
(<= 35 additions, <= 25 deletions), packages structured candidate remediation bundles,
|
|
6
6
|
and renders unified patch diffs with standardized 75-column terminal UI.
|
|
@@ -58,7 +58,7 @@ def box_line(content: str, width: int = 67, border: str = "│", border_color: s
|
|
|
58
58
|
return term_ui.format_box_line(content, width=width, border=border, border_color=border_color)
|
|
59
59
|
return f" {border_color}{border}{RESET} {content}"
|
|
60
60
|
|
|
61
|
-
def box_header(title: str, subtitle: str = "", version: str = "v1.3.
|
|
61
|
+
def box_header(title: str, subtitle: str = "", version: str = "v1.3.6", border_color: str = CYAN) -> str:
|
|
62
62
|
if term_ui:
|
|
63
63
|
return term_ui.card_header(title, subtitle, version, border_color)
|
|
64
64
|
return f"=== {title} ({version}) ==="
|
|
@@ -564,7 +564,7 @@ def execute_harden(target_root: Path, run_id_arg: Optional[str] = None) -> Dict[
|
|
|
564
564
|
|
|
565
565
|
# Print 75-column Terminal Cards
|
|
566
566
|
print()
|
|
567
|
-
print(box_header("🛡️ TORUSGUARD GOVERNED REMEDIATION ENGINE", "Autonomous Minimal Patch Formulation & Ponytail Packaging", "v1.3.
|
|
567
|
+
print(box_header("🛡️ TORUSGUARD GOVERNED REMEDIATION ENGINE", "Autonomous Minimal Patch Formulation & Ponytail Packaging", "v1.3.6"))
|
|
568
568
|
print()
|
|
569
569
|
|
|
570
570
|
print(border_top("Remediation Scope"))
|