torusguard 2.1.1 → 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.
- package/.torusguard/.manifest.json +8 -0
- package/.torusguard/custom_rules/README.md +24 -0
- package/.torusguard/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
- package/.torusguard/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
- package/.torusguard/scripts/deliberation_tournament.py +136 -0
- package/.torusguard/scripts/reachability_analyzer.py +165 -0
- package/.torusguard/scripts/stride_generator.py +198 -0
- package/.torusguard/skills/torusguard-review/SKILL.md +27 -0
- package/.torusguard/skills/torusguard-threatmodel/SKILL.md +27 -0
- package/.torusguard/workflows/review.md +11 -0
- package/.torusguard/workflows/threatmodel.md +9 -0
- package/README.md +133 -40
- package/package.json +4 -1
- package/skills/torusguard/__pycache__/bootstrap.cpython-314.pyc +0 -0
- package/skills/torusguard/bootstrap.py +2 -2
- package/skills/torusguard/payload/.manifest.json +9 -1
- package/skills/torusguard/payload/custom_rules/README.md +15 -0
- package/skills/torusguard/payload/scripts/deliberation_tournament.py +127 -0
- package/skills/torusguard/payload/scripts/reachability_analyzer.py +164 -0
- package/skills/torusguard/payload/scripts/stride_generator.py +196 -0
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +2 -2
- package/skills/torusguard/payload/skills/torusguard-review/SKILL.md +27 -0
- package/skills/torusguard/payload/skills/torusguard-threatmodel/SKILL.md +27 -0
- package/skills/torusguard/payload/workflows/review.md +11 -0
- package/skills/torusguard/payload/workflows/threatmodel.md +9 -0
- package/skills/torusguard-review/SKILL.md +27 -0
- package/skills/torusguard-threatmodel/SKILL.md +27 -0
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
|
|
14
|
-
<a href="https://github.com/githubmofo/TorusGuard/releases"><img src="https://img.shields.io/badge/version-v2.1.
|
|
15
|
-
<a href="https://www.npmjs.com/package/torusguard"><img src="https://img.shields.io/badge/npm-v2.1.
|
|
14
|
+
<a href="https://github.com/githubmofo/TorusGuard/releases"><img src="https://img.shields.io/badge/version-v2.1.2-orange.svg" alt="Version"></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/torusguard"><img src="https://img.shields.io/badge/npm-v2.1.2-CB3837?logo=npm&logoColor=white" alt="npm: v2.1.2"></a>
|
|
16
16
|
<img src="https://img.shields.io/badge/Privacy-Local_First-success" alt="Privacy: Local First">
|
|
17
17
|
<img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen" alt="Dependencies: Zero">
|
|
18
18
|
<img src="https://img.shields.io/badge/SARIF-v2.1.0-6C3483" alt="SARIF">
|
|
@@ -118,52 +118,139 @@ TorusGuard uses a **tri-track architecture** where intelligence, deterministic e
|
|
|
118
118
|
|
|
119
119
|
```mermaid
|
|
120
120
|
flowchart TD
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
%% =========================================================================
|
|
122
|
+
%% STAGE 1: TRI-MODE INGRESS GATEWAY
|
|
123
|
+
%% =========================================================================
|
|
124
|
+
subgraph IngressGateway["1. Unified Tri-Mode Ingress Gateway"]
|
|
125
|
+
direction LR
|
|
126
|
+
CLI["<b>Mode A: Terminal CLI</b><br/><code>torusguard <cmd></code><br/>25 Deterministic Commands"]
|
|
127
|
+
Chat["<b>Mode B: AI Chat Commands</b><br/><code>/torusguard <cmd></code><br/>Cursor • Claude • Windsurf"]
|
|
128
|
+
MCP["<b>Mode C: Native MCP Server</b><br/><code>torusguard_*</code> (13 Tools • 2 Resources)<br/>Stdio JSON-RPC 2.0 Protocol"]
|
|
125
129
|
end
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
%% =========================================================================
|
|
132
|
+
%% STAGE 2: CORE DISPATCHER & RUNTIME KERNEL
|
|
133
|
+
%% =========================================================================
|
|
134
|
+
Kernel["<b>TorusGuard Core Dispatcher & Runtime Kernel</b><br/><code>cmd/torusguard</code> (Single Standalone Go Binary)<br/>Command Parsing • Flag Evaluation (<code>--yes</code>, <code>--html</code>, <code>--rules</code>) • Sandbox Isolation"]
|
|
128
135
|
|
|
129
|
-
|
|
136
|
+
CLI -->|"Terminal Exec"| Kernel
|
|
137
|
+
Chat -->|"Slash Bridge"| Kernel
|
|
138
|
+
MCP -->|"Agent Tool Call"| Kernel
|
|
139
|
+
|
|
140
|
+
%% =========================================================================
|
|
141
|
+
%% STAGE 3: DETECTION & MULTI-MODAL SUITE
|
|
142
|
+
%% =========================================================================
|
|
143
|
+
subgraph DetectionSuite["2. Polyglot Static AST & Multi-Modal Detection Suite"]
|
|
130
144
|
direction TB
|
|
131
|
-
subgraph
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
subgraph StaticGroup["Static Code & Dependency Analysis"]
|
|
146
|
+
direction LR
|
|
147
|
+
AST["<b>Polyglot AST & Taint Engine</b><br/>Tree-sitter • 88 Rules across 22 Families<br/>Go • TS/JS • Python • Java • C# • Rust"]
|
|
148
|
+
TGQL["<b>TG-QL Declarative AST DSL</b><br/>Custom YAML Pattern Queries<br/>Syntax Trees • Taint Sinks • Constraints"]
|
|
149
|
+
Reach["<b>Reachability & OpenVEX</b><br/>Callgraph Traversal • Reachable CVEs<br/>Zero Ineffective Dependency Alerts"]
|
|
150
|
+
end
|
|
151
|
+
subgraph DeepGroup["Forensics, RegEx & Vision OCR"]
|
|
152
|
+
direction LR
|
|
153
|
+
OCR["<b>Multi-Modal Vision OCR Engine</b><br/>Tesseract v5.4.0 • Leaked Secrets<br/>Architecture Diagrams • Screenshots"]
|
|
154
|
+
ReDoS["<b>Thompson NFA ReDoS Engine</b><br/>Polynomial & Exponential Exploder<br/>Catastrophic Backtracking Loops"]
|
|
155
|
+
GitMine["<b>Git History & Container Audit</b><br/>Commit Packfile Secret Mining<br/>Dockerfile Non-Root Enforcement"]
|
|
138
156
|
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
Kernel -->|"Scan Code & Dependencies"| StaticGroup
|
|
160
|
+
Kernel -->|"Analyze Visuals & Commits"| DeepGroup
|
|
161
|
+
|
|
162
|
+
%% =========================================================================
|
|
163
|
+
%% STAGE 4: CONSENSUS DELIBERATION & TRIAGE
|
|
164
|
+
%% =========================================================================
|
|
165
|
+
subgraph DeliberationTriage["3. Deliberation Tournament & Evidence Triage"]
|
|
166
|
+
direction TB
|
|
167
|
+
Tournament["<b>3-Perspective Deliberation Tournament</b><br/>Vulnerability Hunter vs. Devil's Advocate / Sanitizer Verifier vs. Ponytail Remediator<br/>Eliminates False Positives • Calibrated Confidence Scoring (0-100%)"]
|
|
168
|
+
PRGate{"<b>Differential PR Diff Gate</b><br/><code>torusguard review</code><br/>Incremental Git Diff Changes?"}
|
|
169
|
+
Tournament --> PRGate
|
|
170
|
+
end
|
|
139
171
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
StaticGroup -->|"Raw AST Findings"| Tournament
|
|
173
|
+
DeepGroup -->|"Extracted Secrets & Complexities"| Tournament
|
|
174
|
+
|
|
175
|
+
%% =========================================================================
|
|
176
|
+
%% STAGE 5: GOVERNED REMEDIATION & PONYTAIL LOOP
|
|
177
|
+
%% =========================================================================
|
|
178
|
+
subgraph GovernedRemediation["4. Governed Remediation Loop & Safety Guardrails (Ponytail Protocol)"]
|
|
179
|
+
direction TB
|
|
180
|
+
|
|
181
|
+
Harden["<b>Surgical Patch Formulation</b><br/>Semantic Line Snippet Replacement<br/>Strict Line Budget: ≤35 Additions • ≤25 Deletions"]
|
|
182
|
+
|
|
183
|
+
BoundsCheck{"<b>Ponytail Bounds Check</b><br/>Exceeds 35 Add / 25 Del?"}
|
|
184
|
+
RejectDiff["<b>Diff Rejected</b><br/>Excess Churn Detected<br/>Prompt AI for Minimal Snippet"]
|
|
185
|
+
|
|
186
|
+
SnapshotStore[("<b>Pre-Apply Snapshot Store</b><br/><code>.torusguard/snapshots/<run_id>/</code><br/>Byte-for-Byte Rollback Backup")]
|
|
187
|
+
|
|
188
|
+
HumanGate{"<b>Human Gate Authorization</b><br/>Explicit <code>--yes</code> or Interactive Confirmation"}
|
|
189
|
+
UserAbort["<b>Operation Aborted</b><br/>Zero Files Touched • Safe Exit"]
|
|
190
|
+
|
|
191
|
+
ApplyPatch["<b>Atomic Patch Application</b><br/>Apply Unified Surgical Diff to Disk"]
|
|
192
|
+
|
|
193
|
+
RecheckGate{"<b>Differential Recheck Engine</b><br/><code>torusguard recheck</code><br/>Fix Closed with Zero Regressions?"}
|
|
194
|
+
RollbackExec["<b>Auto-Rollback Triggered!</b><br/>Instant Restoration from Snapshot<br/>Quarantine Candidate Patch"]
|
|
195
|
+
|
|
196
|
+
Harden --> BoundsCheck
|
|
197
|
+
BoundsCheck -->|"Violation"| RejectDiff
|
|
198
|
+
RejectDiff -.->|"Re-prompt AI"| Harden
|
|
199
|
+
BoundsCheck -->|"Pass (Within Bounds)"| SnapshotStore
|
|
200
|
+
SnapshotStore --> HumanGate
|
|
201
|
+
HumanGate -->|"Denied"| UserAbort
|
|
202
|
+
HumanGate -->|"Approved"| ApplyPatch
|
|
203
|
+
ApplyPatch --> RecheckGate
|
|
204
|
+
RecheckGate -->|"Regressions"| RollbackExec
|
|
205
|
+
RollbackExec -.->|"Restore Clean State"| SnapshotStore
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
PRGate -->|"Target Findings"| Harden
|
|
209
|
+
|
|
210
|
+
%% =========================================================================
|
|
211
|
+
%% STAGE 6: LIVING SECURITY LEDGER & ENTERPRISE OUTPUTS
|
|
212
|
+
%% =========================================================================
|
|
213
|
+
subgraph EnterpriseDeliverables["5. Living Security Ledger & Enterprise Deliverables"]
|
|
214
|
+
direction TB
|
|
215
|
+
Ledger[("<b>Living Security Ledger</b><br/><code>security_report.md</code><br/>Synchronized Single Source of Truth • Status: RESOLVED 🟢")]
|
|
216
|
+
|
|
217
|
+
subgraph DeliverableOutputs["Executive Reports & Verified Memory"]
|
|
218
|
+
direction LR
|
|
219
|
+
ThreatModel["<b>STRIDE Threat Model</b><br/><code>SECURITY_THREAT_MODEL.md</code><br/>DFD Architecture Diagrams"]
|
|
220
|
+
SARIF["<b>OASIS SARIF v2.1.0</b><br/>GitHub Advanced Security<br/>CI/CD Security Center"]
|
|
221
|
+
HTMLReport["<b>Executive Dashboard</b><br/>Single-File HTML Report<br/>Interactive Posture Heatmap"]
|
|
222
|
+
GoldenRecipes[("<b>Golden Fix Memory</b><br/><code>.torusguard/recipes/</code><br/>Verified Distilled Fixes")]
|
|
145
223
|
end
|
|
224
|
+
|
|
225
|
+
Ledger --> DeliverableOutputs
|
|
146
226
|
end
|
|
147
227
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
classDef
|
|
157
|
-
classDef
|
|
158
|
-
classDef
|
|
159
|
-
classDef
|
|
160
|
-
classDef
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
class
|
|
228
|
+
RecheckGate -->|"Fix Confirmed (Clean Closure)"| Ledger
|
|
229
|
+
|
|
230
|
+
%% =========================================================================
|
|
231
|
+
%% STYLING AND THEME (Modern Dark Cyber Palette)
|
|
232
|
+
%% =========================================================================
|
|
233
|
+
classDef ingressStyle fill:#0f172a,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
|
|
234
|
+
classDef routerStyle fill:#1e1b4b,stroke:#6366f1,stroke-width:2px,color:#f8fafc;
|
|
235
|
+
classDef scannerStyle fill:#022c22,stroke:#10b981,stroke-width:2px,color:#f8fafc;
|
|
236
|
+
classDef tourneyStyle fill:#2e1065,stroke:#a855f7,stroke-width:2px,color:#f8fafc;
|
|
237
|
+
classDef gateStyle fill:#451a03,stroke:#f59e0b,stroke-width:2px,color:#fef3c7;
|
|
238
|
+
classDef rejectStyle fill:#450a0a,stroke:#ef4444,stroke-width:2px,color:#fee2e2;
|
|
239
|
+
classDef actionStyle fill:#064e3b,stroke:#34d399,stroke-width:2px,color:#f8fafc;
|
|
240
|
+
classDef dbStyle fill:#1e293b,stroke:#94a3b8,stroke-width:2px,color:#f8fafc;
|
|
241
|
+
classDef ledgerStyle fill:#172554,stroke:#3b82f6,stroke-width:2px,color:#eff6ff;
|
|
242
|
+
classDef outputStyle fill:#042f2e,stroke:#14b8a6,stroke-width:2px,color:#f0fdfa;
|
|
243
|
+
|
|
244
|
+
class CLI,Chat,MCP ingressStyle;
|
|
245
|
+
class Kernel routerStyle;
|
|
246
|
+
class AST,TGQL,Reach,OCR,ReDoS,GitMine scannerStyle;
|
|
247
|
+
class Tournament tourneyStyle;
|
|
248
|
+
class PRGate,BoundsCheck,HumanGate,RecheckGate gateStyle;
|
|
249
|
+
class RejectDiff,UserAbort,RollbackExec rejectStyle;
|
|
250
|
+
class Harden,ApplyPatch actionStyle;
|
|
251
|
+
class SnapshotStore,GoldenRecipes dbStyle;
|
|
165
252
|
class Ledger ledgerStyle;
|
|
166
|
-
class
|
|
253
|
+
class ThreatModel,SARIF,HTMLReport outputStyle;
|
|
167
254
|
```
|
|
168
255
|
|
|
169
256
|
> 🌐 **Interactive Architecture Visualizations:**
|
|
@@ -204,7 +291,7 @@ npx torusguard init
|
|
|
204
291
|
```
|
|
205
292
|
|
|
206
293
|
<a href="https://www.npmjs.com/package/torusguard">
|
|
207
|
-
<img src="https://img.shields.io/badge/npm-v2.1.
|
|
294
|
+
<img src="https://img.shields.io/badge/npm-v2.1.2-CB3837?logo=npm&logoColor=white" alt="npm package">
|
|
208
295
|
</a>
|
|
209
296
|
|
|
210
297
|
### Option 2: Build from Source (Recommended for Contributors)
|
|
@@ -282,6 +369,9 @@ torusguard exploit-check
|
|
|
282
369
|
| `init` | Scaffold `.torusguard/` workspace, detect stack, activate rules |
|
|
283
370
|
| `status` | Diagnostic overview of posture, stack, and active rules |
|
|
284
371
|
| `audit` | Static heuristic security scan against active TG-* rules |
|
|
372
|
+
| `review` | Differential PR and Git diff incremental security review |
|
|
373
|
+
| `threatmodel` | Synthesize architectural STRIDE threat model & Mermaid DFDs |
|
|
374
|
+
| `benchmark` | Run SecurityReviewBench precision & recall evaluation suite |
|
|
285
375
|
| `verify` | Live disk line match audit and evidence sufficiency check |
|
|
286
376
|
| `harden` | Validate patches against Ponytail Protocol bounds |
|
|
287
377
|
| `apply` | Apply patches with pre-apply `.bak` rollback snapshots |
|
|
@@ -380,7 +470,7 @@ TorusGuard works natively inside AI coding assistants. Add the configuration fil
|
|
|
380
470
|
|
|
381
471
|
### Native MCP Tools (Agent Toolkit Mode)
|
|
382
472
|
|
|
383
|
-
When configured with `.agents/mcp_config.json` or `mcp_config.json`, AI coding agents gain native tool calling:
|
|
473
|
+
When configured with `.agents/mcp_config.json` or `mcp_config.json`, AI coding agents gain native tool calling (13 Tools & 2 Resources):
|
|
384
474
|
|
|
385
475
|
- `torusguard_audit`: Deep static AST scan + Vision OCR; writes `security_report.md`
|
|
386
476
|
- `torusguard_ocr_scan`: Dedicated image credential analysis via Tesseract (5-10MB bounds)
|
|
@@ -391,6 +481,9 @@ When configured with `.agents/mcp_config.json` or `mcp_config.json`, AI coding a
|
|
|
391
481
|
- `torusguard_verify`: Asserts evidence sufficiency & line-shift invariant fingerprint matches
|
|
392
482
|
- `torusguard_harden`: Validates remediation diff against Ponytail Protocol bounds
|
|
393
483
|
- `torusguard_recheck`: Differential re-scan confirming fix closure
|
|
484
|
+
- `torusguard_review`: Differential PR and Git diff incremental review; gate decisions
|
|
485
|
+
- `torusguard_threatmodel`: Synthesizes STRIDE threat model & Mermaid DFDs (`SECURITY_THREAT_MODEL.md`)
|
|
486
|
+
- `torusguard_benchmark`: Runs SecurityReviewBench self-evaluating precision & recall suite
|
|
394
487
|
- `torusguard_status`: Workspace posture and tech stack inspection
|
|
395
488
|
- `torusguard://security_report`: MCP Resource reading the living security report
|
|
396
489
|
- `torusguard://rules_catalog`: MCP Resource exploring verified rules catalog & Golden Fix patterns
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torusguard",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Security guardrails, governed remediation, and authorized runtime validation for AI-built web applications.",
|
|
5
5
|
"main": "bin/torusguard.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"init": "node bin/torusguard.js init",
|
|
20
20
|
"status": "node bin/torusguard.js status",
|
|
21
21
|
"audit": "node bin/torusguard.js audit",
|
|
22
|
+
"review": "node bin/torusguard.js review",
|
|
23
|
+
"threatmodel": "node bin/torusguard.js threatmodel",
|
|
24
|
+
"benchmark": "node bin/torusguard.js benchmark",
|
|
22
25
|
"harden": "node bin/torusguard.js harden",
|
|
23
26
|
"apply": "node bin/torusguard.js apply",
|
|
24
27
|
"recipes": "node bin/torusguard.js recipes",
|
|
Binary file
|
|
@@ -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.
|
|
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.1", 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.
|
|
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
|
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"core/v6_reporter.py": "95fa2ec5b0e708101b62131152445039192382e409d0f40c35cdcbf96648ca26",
|
|
50
50
|
"core/v6_workflow.py": "3e38afafc3afdbebe8cf76ed24da00435aab27a2ac512a292983f8b1e88ebcb8",
|
|
51
51
|
"core/watcher.py": "b3d4856023ee2f6c65b2938d3c76c246d7c440c58cdc7b642dc27209d68a959a",
|
|
52
|
+
"custom_rules/README.md": "787adf6e38498d895cb8ead6e213e91d41304e31051bfdd0440ea8ae107e0104",
|
|
52
53
|
"references/csharp-security.md": "d6e267855b08ed4fdff26f3b876f089ae1f90a7ec6546cba7e8173380d9daa9d",
|
|
53
54
|
"references/django-security.md": "26ea5535ac435e61489df1e381ac62b19e81702edfe095e78c937be93fa15ea7",
|
|
54
55
|
"references/drf-security.md": "c6796398317b0f264d89fff598404e126d819e69e1ea0a5129924a5eb2a7af9d",
|
|
@@ -197,6 +198,7 @@
|
|
|
197
198
|
"schemas/runtime-evidence.schema.json": "929d2b094c1cd1f7f9db07be1eba5994e8002477eab3fd24140e1e973fd18cc1",
|
|
198
199
|
"scripts/apply_runner.py": "b145a68a4bc10fcacb853f972aee81fa3607e32d282b2b850c5a63d8e72da890",
|
|
199
200
|
"scripts/audit_runner.py": "18ebcd46f8363169db4ff4675a5258e0b93df5b219650de95b9a5e9f51da5ff9",
|
|
201
|
+
"scripts/deliberation_tournament.py": "9581161cdb064a3ed6bfd0c242e71fcb43b0fd083b2eb84bc239ac7cc6568b64",
|
|
200
202
|
"scripts/diff_guard.py": "0dc178cdde1ac0ae1a1ea4b2eb2ce1756b2126f6156909cf55d85249f115fcbd",
|
|
201
203
|
"scripts/finding_scorer.py": "95c7edfe5ee4742be565914f71bb07bd077b2cf4636b769476345389dd85f3cf",
|
|
202
204
|
"scripts/harden_runner.py": "5d5a40029c088ccb66790a9b0128bdefcd35883ef1dac78088ceb9531392a8f1",
|
|
@@ -204,6 +206,7 @@
|
|
|
204
206
|
"scripts/manifest_builder.py": "f1f9a0d63c55cf5ff45e9fd39d1b251c2bce05e777f8c1e325741d340f0128db",
|
|
205
207
|
"scripts/memory_engine.py": "b715d0ca030e95eb06ad6eda99027535df8a7e37d64897c023be6a291cf13dc6",
|
|
206
208
|
"scripts/monorepo_detector.py": "5603674136a5294ebfff89efeca0a546dd4a7deb1d184a2592ff21aff2131f73",
|
|
209
|
+
"scripts/reachability_analyzer.py": "708a690052f9d850fda7641dd5ec972f43e3c3c3ed5fc61221bd73517593ead3",
|
|
207
210
|
"scripts/recheck_runner.py": "89e93fcc6843493d7e5f0bf97065dbdb40106273a00e07f903250665ca25f6fb",
|
|
208
211
|
"scripts/recipes_runner.py": "090c06ef0de3c640a111c5646837233ceeaca494b161bd5edc630cc1371dca0e",
|
|
209
212
|
"scripts/report_sync.py": "5ebef684690200694a53af30db850d9808d03ec710ed21ef75c4de52b5e5217c",
|
|
@@ -213,9 +216,10 @@
|
|
|
213
216
|
"scripts/sarif_exporter.py": "86700765212adb3ab590ca72614bf3ad4ce7ce60e3193f7f3bd1a04d34d10f6e",
|
|
214
217
|
"scripts/stack_detect.py": "c05a1ade4c8692025476bf7ea080a8f07c92e5d8e9a04f94c1654386352ffbdc",
|
|
215
218
|
"scripts/status_runner.py": "4c358c91950c7874bcc6a5f23f2d3fda62b0752efcf6c6b658adf02ed77c7752",
|
|
219
|
+
"scripts/stride_generator.py": "4660713fa65e24119b88b6a050fb41f625d99bad030f1db40d198950d8c55ba8",
|
|
216
220
|
"scripts/term_ui.py": "ca69398f85c86fe89526d6334b3ee866c890273997351113c3cc1765b7bd20c3",
|
|
217
221
|
"skills/torusguard/SKILL.md": "15c69cb10eca1a10344b494888dce57e2baf227c3282bd54424a36ce35fb2460",
|
|
218
|
-
"skills/torusguard/bootstrap.py": "
|
|
222
|
+
"skills/torusguard/bootstrap.py": "b75ad49e9dade87980cde3269ee1f5c3438f944a34a545c44f839d4496086b63",
|
|
219
223
|
"skills/torusguard/references/auth-and-sessions.md": "c3e4b9600cb53fc28c993922dc880accbec5783ac90d5ee98d63b52762d19525",
|
|
220
224
|
"skills/torusguard/references/business-logic-and-abuse.md": "58e87df2864d468af70c1ff39f5d540c687fa846b671457f85046ab2065fe99c",
|
|
221
225
|
"skills/torusguard/references/cache-and-sensitive-responses.md": "81b4a7a08e11dafa4ec58d868c4a35f1f891d0dcdcfbcefaef81e1b9ba94f5a1",
|
|
@@ -258,7 +262,9 @@
|
|
|
258
262
|
"skills/torusguard-recheck/SKILL.md": "a5c4539ec6d091179c8ba53792fba46f26795976f392d2305e357f25a2966c55",
|
|
259
263
|
"skills/torusguard-redos/SKILL.md": "b09b39bd98589613b2107c177e247f6224fb54ab2f028b4339ae01aac9d28c67",
|
|
260
264
|
"skills/torusguard-report/SKILL.md": "509185ea6730601cf9a44e136219cace61cd0523f8e112de03e4ef1c35e60585",
|
|
265
|
+
"skills/torusguard-review/SKILL.md": "6d9ec907821ddf62bbbeaed8751d1532e72ba42702ea3afdaadaf720f66c35e3",
|
|
261
266
|
"skills/torusguard-status/SKILL.md": "e9741a20313a8304bc7565b194949a59ec7cfd4945b197517680164b3197efc0",
|
|
267
|
+
"skills/torusguard-threatmodel/SKILL.md": "549903f39a030a75d88c184b7373d86bca921d8f802732c3674bfe9537b14537",
|
|
262
268
|
"skills/torusguard-verify/SKILL.md": "aeb4d3c7924253607d130e48491d83bf7b7a21707cd1dac2ba97d64f5e259fd6",
|
|
263
269
|
"skills/torusguard-web-validate/SKILL.md": "f1df00e10abd823d9a8c4daa1a3af7f26c5263c6c8e58e05b61b647d01a25489",
|
|
264
270
|
"templates/audit-report.template.md": "71149dd05b01a74287a47781fdc71fd2d1b4b70337da303bf78767d0410a6c90",
|
|
@@ -280,7 +286,9 @@
|
|
|
280
286
|
"workflows/recipes.md": "300c1fed0167e1a299fcd33c07db05630116b18a71ec2513c385862bba0f179b",
|
|
281
287
|
"workflows/redos.md": "f55a4f510849a5ea7e2ae4b08ecc70dff03c1bf21c7fdc664a4ff3ad0396f904",
|
|
282
288
|
"workflows/report.md": "7c718dfef6da1b66ba0fed300b7dfc90438fc3866af5a9b2aae4b3c3acd638e2",
|
|
289
|
+
"workflows/review.md": "ac8edc3c1d64571c7d58769e471fb10855969de72d874acb988c9c3c963f9a45",
|
|
283
290
|
"workflows/status.md": "021a962ae4e6fc81383db2ef42d28dcfc1b3e546c9a10a7a15b9910e531cf0c2",
|
|
291
|
+
"workflows/threatmodel.md": "b509c38754bff5284d768d3e64cea21839ee9abcedb1661e597d291145715cf1",
|
|
284
292
|
"workflows/torusguard-apply.md": "67e1002fc6e9d68e0c7d871c40b6f3c3b9c3919c8e02106a579882729c0f6b9f",
|
|
285
293
|
"workflows/torusguard-audit.md": "9f13b0497553965d30a71ff040a0dc556d526c8bbff136952b8210685632b5d7",
|
|
286
294
|
"workflows/torusguard-authorize.md": "28ca58cfc656e956f7cec8160f2aadc152abcde3eb5ad5588df149007afb0df8",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# TorusGuard Custom Security Rules (TG-QL)
|
|
2
|
+
|
|
3
|
+
Place your custom declarative security rules (`.yaml`, `.yml`, or `.json`) in this directory. TorusGuard compiles and evaluates them dynamically on every audit, review, and MCP invocation with zero Go recompilation.
|
|
4
|
+
|
|
5
|
+
## Example Custom Rule: `admin-auth-guard.yaml`
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
id: TG-CUSTOM-001
|
|
9
|
+
severity: HIGH
|
|
10
|
+
languages: [.js, .ts, .py, .go]
|
|
11
|
+
pattern: "app.post('/api/admin/$PATH', $HANDLER)"
|
|
12
|
+
not_inside: "requireAdminAuth"
|
|
13
|
+
description: "Administrative API endpoint declared without mandatory requireAdminAuth middleware"
|
|
14
|
+
suggested_fix: "Wrap route handler with requireAdminAuth: app.post('/api/admin/$PATH', requireAdminAuth, $HANDLER)"
|
|
15
|
+
```
|
|
@@ -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()
|
|
@@ -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()
|