torusguard 1.3.3 → 1.3.4
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/runs/report-latest.html +3 -3
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-115-9a059e/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-115-9a059e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-115-9a059e/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-119-61557c/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-119-61557c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-119-61557c/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-265-996ca3/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-265-996ca3/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-265-996ca3/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-503-d11cf8/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-503-d11cf8/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-503-d11cf8/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-79-91f41f/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-79-91f41f/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-db-004-79-91f41f/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-107-516a40/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-107-516a40/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-107-516a40/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-128-7809a1/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-128-7809a1/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-128-7809a1/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-256-c8a22e/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-256-c8a22e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-002-256-c8a22e/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-003-53-d167b4/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-003-53-d167b4/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-input-003-53-d167b4/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-platform-001-183-47fd5e/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-platform-001-183-47fd5e/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-platform-001-183-47fd5e/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-209-4f6424/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-209-4f6424/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-209-4f6424/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-257-89bf34/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-257-89bf34/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-257-89bf34/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-341-ce228c/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-341-ce228c/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-341-ce228c/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-355-7b190b/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-355-7b190b/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-355-7b190b/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-64-5af31a/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-64-5af31a/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-64-5af31a/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-9-f51467/metadata.json +15 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-9-f51467/minimal_patch_plan.md +21 -0
- package/.torusguard/runs/run-20260910-120238-audit/bundles/bnd-tg-sec-001-9-f51467/patch.diff +9 -0
- package/.torusguard/runs/run-20260910-120238-audit/findings.json +1520 -0
- package/.torusguard/runs/run-20260910-120238-audit/findings.md +560 -0
- package/.torusguard/runs/run-20260910-120238-audit/manifest.json +13 -0
- package/.torusguard/runs/run-20260910-120238-audit/recheck.md +86 -0
- package/.torusguard/runs/run-20260910-120238-audit/remediation.md +262 -0
- package/.torusguard/runs/run-20260910-120238-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260910-121238-audit/findings.json +794 -0
- package/.torusguard/runs/run-20260910-121238-audit/findings.md +344 -0
- package/.torusguard/runs/run-20260910-121238-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260910-121238-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260910-130454-audit/findings.json +893 -0
- package/.torusguard/runs/run-20260910-130454-audit/findings.md +386 -0
- package/.torusguard/runs/run-20260910-130454-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260910-130454-audit/summary.md +5 -0
- package/.torusguard/runs/run-20260910-130519-audit/findings.json +563 -0
- package/.torusguard/runs/run-20260910-130519-audit/findings.md +246 -0
- package/.torusguard/runs/run-20260910-130519-audit/manifest.json +12 -0
- package/.torusguard/runs/run-20260910-130519-audit/summary.md +5 -0
- package/.torusguard/scripts/__pycache__/audit_runner.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/html_reporter.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/rules_sync.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/stack_detect.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/term_ui.cpython-311.pyc +0 -0
- package/.torusguard/scripts/apply_runner.py +138 -100
- package/.torusguard/scripts/audit_runner.py +219 -116
- package/.torusguard/scripts/harden_runner.py +144 -64
- package/.torusguard/scripts/recheck_runner.py +80 -63
- package/.torusguard/scripts/recipes_runner.py +38 -31
- package/.torusguard/scripts/term_ui.py +162 -0
- package/.torusguard/skills/torusguard/SKILL.md +61 -31
- package/.torusguard/skills/torusguard-apply/SKILL.md +80 -46
- package/.torusguard/skills/torusguard-audit/SKILL.md +83 -70
- package/.torusguard/skills/torusguard-authorize/SKILL.md +5 -4
- package/.torusguard/skills/torusguard-exploit-check/SKILL.md +6 -5
- package/.torusguard/skills/torusguard-full/SKILL.md +1 -1
- package/.torusguard/skills/torusguard-harden/SKILL.md +94 -63
- package/.torusguard/skills/torusguard-init/SKILL.md +64 -52
- package/.torusguard/skills/torusguard-recheck/SKILL.md +77 -53
- package/.torusguard/skills/torusguard-report/SKILL.md +72 -54
- package/.torusguard/skills/torusguard-status/SKILL.md +58 -42
- package/.torusguard/skills/torusguard-verify/SKILL.md +8 -7
- package/.torusguard/skills/torusguard-web-validate/SKILL.md +8 -7
- package/README.md +4 -3
- package/bin/torusguard.js +544 -437
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +61 -31
- package/skills/torusguard/payload/scripts/apply_runner.py +138 -100
- package/skills/torusguard/payload/scripts/audit_runner.py +219 -116
- package/skills/torusguard/payload/scripts/harden_runner.py +144 -64
- package/skills/torusguard/payload/scripts/recheck_runner.py +80 -63
- package/skills/torusguard/payload/scripts/recipes_runner.py +38 -31
- package/skills/torusguard/payload/scripts/term_ui.py +162 -0
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +61 -31
- package/skills/torusguard/payload/skills/torusguard-apply/SKILL.md +80 -46
- package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +83 -70
- package/skills/torusguard/payload/skills/torusguard-authorize/SKILL.md +5 -4
- package/skills/torusguard/payload/skills/torusguard-exploit-check/SKILL.md +6 -5
- package/skills/torusguard/payload/skills/torusguard-full/SKILL.md +1 -1
- package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +94 -63
- package/skills/torusguard/payload/skills/torusguard-init/SKILL.md +64 -52
- package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +77 -53
- package/skills/torusguard/payload/skills/torusguard-report/SKILL.md +72 -54
- package/skills/torusguard/payload/skills/torusguard-status/SKILL.md +58 -42
- package/skills/torusguard/payload/skills/torusguard-verify/SKILL.md +8 -7
- package/skills/torusguard/payload/skills/torusguard-web-validate/SKILL.md +8 -7
- package/skills/torusguard-apply/SKILL.md +80 -48
- package/skills/torusguard-audit/SKILL.md +83 -75
- 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 +94 -67
- package/skills/torusguard-init/SKILL.md +64 -53
- package/skills/torusguard-recheck/SKILL.md +77 -54
- package/skills/torusguard-report/SKILL.md +72 -55
- package/skills/torusguard-status/SKILL.md +58 -43
- package/skills/torusguard-verify/SKILL.md +1 -1
- package/skills/torusguard-web-validate/SKILL.md +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# TorusGuard Security Audit Findings: run-20260910-130519-audit
|
|
2
|
+
- Target Root: `C:\Users\Admin\Desktop\TorusGuard`
|
|
3
|
+
- Total Scored Findings: `17`
|
|
4
|
+
- Identified Clusters: `5`
|
|
5
|
+
- Generated At: `2026-09-10 13:05:19 IST`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
## Summary of Findings
|
|
9
|
+
|
|
10
|
+
### [Critical] TG-SEC-001 - Hardcoded Secret or API Key in Tracked Source
|
|
11
|
+
- **Finding ID:** `TG-SEC-001-bb5e4b77`
|
|
12
|
+
- **Location:** `examples/vulnerable-react-express/server/index.js:9`
|
|
13
|
+
- **Confidence Score:** `80/100` (High Confidence)
|
|
14
|
+
- **Cluster:** `cluster-credentials-exposure`
|
|
15
|
+
- **Description:** Hardcoded JWT signing secret
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
// TG-SEC-001: Hardcoded JWT secret
|
|
19
|
+
const JWT_SECRET = 'FAKE_DEMO_JWT_SECRET_NOT_FOR_PRODUCTION';
|
|
20
|
+
|
|
21
|
+
// TG-PLATFORM-001: Wildcard CORS with credentials
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### [Critical] TG-SEC-001 - Hardcoded Secret or API Key in Tracked Source
|
|
25
|
+
- **Finding ID:** `TG-SEC-001-c393d596`
|
|
26
|
+
- **Location:** `examples/vulnerable-react-express/server/index.js:18`
|
|
27
|
+
- **Confidence Score:** `80/100` (High Confidence)
|
|
28
|
+
- **Cluster:** `cluster-credentials-exposure`
|
|
29
|
+
- **Description:** Hardcoded credential or API secret string
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
const users = [
|
|
33
|
+
{ id: '1', email: 'alice@demo.local', password: 'password123', name: 'Alice' },
|
|
34
|
+
{ id: '2', email: 'bob@demo.local', password: 'secret456', name: 'Bob' },
|
|
35
|
+
];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### [Critical] TG-DB-002 - Privileged Database Secret or Master Role Key in Client Code
|
|
39
|
+
- **Finding ID:** `TG-DB-002-d9bd69df`
|
|
40
|
+
- **Location:** `demo/playground/vulnerable_nextjs/actions.ts:9`
|
|
41
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
42
|
+
- **Cluster:** `cluster-client-leak`
|
|
43
|
+
- **Description:** Service role key or raw database connection string reference in frontend code
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
// Finding 1: TG-CLIENT-001 - Backend admin key exposed in client-accessible code
|
|
47
|
+
export const SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.demo_secret_token_1234567890";
|
|
48
|
+
|
|
49
|
+
interface UserRecord {
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
53
|
+
- **Finding ID:** `TG-DIFF-001-671d3513`
|
|
54
|
+
- **Location:** `skills/torusguard/payload/scripts/harden_runner.py:246`
|
|
55
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
56
|
+
- **Cluster:** `cluster-transport-security`
|
|
57
|
+
- **Description:** Disabled TLS certificate verification (verify=False)
|
|
58
|
+
```
|
|
59
|
+
# Strategy 8: TLS Verification Bypass (TG-DIFF-001)
|
|
60
|
+
elif rule_id == "TG-DIFF-001":
|
|
61
|
+
if "verify=False" in target_line:
|
|
62
|
+
fixed_line = target_line.replace("verify=False", "verify=True")
|
|
63
|
+
new_lines[line_number - 1] = fixed_line
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
67
|
+
- **Finding ID:** `TG-DIFF-001-b6549484`
|
|
68
|
+
- **Location:** `skills/torusguard/payload/scripts/harden_runner.py:256`
|
|
69
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
70
|
+
- **Cluster:** `cluster-transport-security`
|
|
71
|
+
- **Description:** Disabled Go TLS certificate verification (InsecureSkipVerify: true)
|
|
72
|
+
```
|
|
73
|
+
applied_rule = True
|
|
74
|
+
fix_explanation = "Restored TLS certificate authority validation (rejectUnauthorized: true)"
|
|
75
|
+
elif "InsecureSkipVerify: true" in target_line:
|
|
76
|
+
fixed_line = target_line.replace("InsecureSkipVerify: true", "InsecureSkipVerify: false")
|
|
77
|
+
new_lines[line_number - 1] = fixed_line
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
81
|
+
- **Finding ID:** `TG-DIFF-001-c4cfaa8a`
|
|
82
|
+
- **Location:** `skills/torusguard/payload/scripts/harden_runner.py:251`
|
|
83
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
84
|
+
- **Cluster:** `cluster-transport-security`
|
|
85
|
+
- **Description:** Disabled Node.js TLS rejectUnauthorized
|
|
86
|
+
```
|
|
87
|
+
applied_rule = True
|
|
88
|
+
fix_explanation = "Restored mandatory TLS certificate verification (verify=True)"
|
|
89
|
+
elif "rejectUnauthorized: false" in target_line:
|
|
90
|
+
fixed_line = target_line.replace("rejectUnauthorized: false", "rejectUnauthorized: true")
|
|
91
|
+
new_lines[line_number - 1] = fixed_line
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
95
|
+
- **Finding ID:** `TG-DIFF-001-7db54483`
|
|
96
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:180`
|
|
97
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
98
|
+
- **Cluster:** `cluster-transport-security`
|
|
99
|
+
- **Description:** Disabled TLS certificate verification (verify=False)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
if not bypass_guards:
|
|
103
|
+
bypass_rule = "4. **No Security Bypasses:** Never insert `# nosec`, `verify=False`, `@csrf_exempt`, or `bypass_auth=True`."
|
|
104
|
+
else:
|
|
105
|
+
bypass_rule = f"4. **No Security Bypasses:** Never insert `# nosec`, `verify=False`, or {', '.join(bypass_guards[:2])}."
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
109
|
+
- **Finding ID:** `TG-DIFF-001-77bc2f9b`
|
|
110
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:157`
|
|
111
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
112
|
+
- **Cluster:** `cluster-transport-security`
|
|
113
|
+
- **Description:** Disabled Go TLS certificate verification (InsecureSkipVerify: true)
|
|
114
|
+
```
|
|
115
|
+
if "go" in stack_lower:
|
|
116
|
+
sql_guards.append("`db.Query(..., args)` / GORM")
|
|
117
|
+
bypass_guards.append("`InsecureSkipVerify: true`")
|
|
118
|
+
if "rust" in stack_lower:
|
|
119
|
+
sql_guards.append("`sqlx::query!` / Diesel")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
123
|
+
- **Finding ID:** `TG-DIFF-001-cb5ae7e9`
|
|
124
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:172`
|
|
125
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
126
|
+
- **Cluster:** `cluster-transport-security`
|
|
127
|
+
- **Description:** Disabled Node.js TLS rejectUnauthorized
|
|
128
|
+
```
|
|
129
|
+
if "typescript" in stack_lower or "javascript" in stack_lower or "next" in stack_lower or "express" in stack_lower:
|
|
130
|
+
sql_guards.append("Prisma / Drizzle binders")
|
|
131
|
+
bypass_guards.append("`rejectUnauthorized: false`")
|
|
132
|
+
|
|
133
|
+
if not sql_guards:
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### [Critical] TG-DIFF-001 - Disabled TLS / SSL Certificate Validation
|
|
137
|
+
- **Finding ID:** `TG-DIFF-001-0e4204af`
|
|
138
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:169`
|
|
139
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
140
|
+
- **Cluster:** `cluster-transport-security`
|
|
141
|
+
- **Description:** Disabled PHP curl SSL verification
|
|
142
|
+
```
|
|
143
|
+
if "php" in stack_lower:
|
|
144
|
+
sql_guards.append("PDO prepared statements / Eloquent")
|
|
145
|
+
bypass_guards.append("`CURLOPT_SSL_VERIFYPEER => false`")
|
|
146
|
+
if "typescript" in stack_lower or "javascript" in stack_lower or "next" in stack_lower or "express" in stack_lower:
|
|
147
|
+
sql_guards.append("Prisma / Drizzle binders")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### [High] TG-INPUT-003 - Unsafe Dynamic Code or HTML Execution
|
|
151
|
+
- **Finding ID:** `TG-INPUT-003-fea9421e`
|
|
152
|
+
- **Location:** `examples/vulnerable-react-express/client/src/App.jsx:53`
|
|
153
|
+
- **Confidence Score:** `80/100` (High Confidence)
|
|
154
|
+
- **Cluster:** `cluster-injection`
|
|
155
|
+
- **Description:** React dangerouslySetInnerHTML rendering raw unescaped HTML
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
{/* TG-INPUT-003: Unsafe HTML rendering */}
|
|
159
|
+
<div dangerouslySetInnerHTML={{ __html: bio }} />
|
|
160
|
+
|
|
161
|
+
<button onClick={fetchUser}>Fetch user (IDOR demo)</button>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### [High] TG-PLATFORM-001 - Wildcard Permissive CORS Configuration with Credentials
|
|
165
|
+
- **Finding ID:** `TG-PLATFORM-001-7dd35cb4`
|
|
166
|
+
- **Location:** `examples/vulnerable-react-express/server/index.js:12`
|
|
167
|
+
- **Confidence Score:** `80/100` (High Confidence)
|
|
168
|
+
- **Cluster:** `cluster-auth-bypass`
|
|
169
|
+
- **Description:** Wildcard CORS origin configured in Express middleware
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
// TG-PLATFORM-001: Wildcard CORS with credentials
|
|
173
|
+
app.use(cors({ origin: '*', credentials: true }));
|
|
174
|
+
|
|
175
|
+
// TG-PLATFORM-004: No explicit JSON body size limit
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### [High] TG-PLATFORM-001 - Wildcard Permissive CORS Configuration with Credentials
|
|
179
|
+
- **Finding ID:** `TG-PLATFORM-001-1a813dde`
|
|
180
|
+
- **Location:** `skills/torusguard/payload/scripts/harden_runner.py:202`
|
|
181
|
+
- **Confidence Score:** `80/100` (High Confidence)
|
|
182
|
+
- **Cluster:** `cluster-auth-bypass`
|
|
183
|
+
- **Description:** Wildcard CORS origin enabled across entire application
|
|
184
|
+
```
|
|
185
|
+
# Strategy 5: Permissive CORS with Credentials (TG-PLATFORM-001)
|
|
186
|
+
elif rule_id == "TG-PLATFORM-001":
|
|
187
|
+
if "CORS_ALLOW_ALL_ORIGINS = True" in target_line:
|
|
188
|
+
replacement = f'{indent}CORS_ALLOWED_ORIGINS = [os.environ.get("ALLOWED_ORIGIN", "https://app.example.com")]'
|
|
189
|
+
new_lines[line_number - 1] = replacement
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### [High] TG-AUTH-004 - Insecure Session Cookie or Disabled CSRF Protection
|
|
193
|
+
- **Finding ID:** `TG-AUTH-004-f60870ef`
|
|
194
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:180`
|
|
195
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
196
|
+
- **Cluster:** `cluster-auth-bypass`
|
|
197
|
+
- **Description:** Decorator disabling route-level authentication or CSRF protection
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
if not bypass_guards:
|
|
201
|
+
bypass_rule = "4. **No Security Bypasses:** Never insert `# nosec`, `verify=False`, `@csrf_exempt`, or `bypass_auth=True`."
|
|
202
|
+
else:
|
|
203
|
+
bypass_rule = f"4. **No Security Bypasses:** Never insert `# nosec`, `verify=False`, or {', '.join(bypass_guards[:2])}."
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### [High] TG-AUTH-004 - Insecure Session Cookie or Disabled CSRF Protection
|
|
207
|
+
- **Finding ID:** `TG-AUTH-004-6b0c7ebf`
|
|
208
|
+
- **Location:** `skills/torusguard/payload/scripts/rules_sync.py:166`
|
|
209
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
210
|
+
- **Cluster:** `cluster-auth-bypass`
|
|
211
|
+
- **Description:** Attribute disabling ASP.NET authentication
|
|
212
|
+
```
|
|
213
|
+
if "c#" in stack_lower or "dotnet" in stack_lower:
|
|
214
|
+
sql_guards.append("EF Core LINQ / `FromSqlInterpolated`")
|
|
215
|
+
bypass_guards.append("`[AllowAnonymous]`")
|
|
216
|
+
if "php" in stack_lower:
|
|
217
|
+
sql_guards.append("PDO prepared statements / Eloquent")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### [Medium] TG-SEC-004 - Sensitive Credential or Token Logging
|
|
221
|
+
- **Finding ID:** `TG-SEC-004-dbbc17f7`
|
|
222
|
+
- **Location:** `examples/vulnerable-react-express/client/src/App.jsx:30`
|
|
223
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
224
|
+
- **Cluster:** `cluster-credentials-exposure`
|
|
225
|
+
- **Description:** Plaintext credential or token logged to standard output
|
|
226
|
+
```
|
|
227
|
+
const data = await res.json();
|
|
228
|
+
// TG-CLIENT-002: Sensitive console logging
|
|
229
|
+
console.log('Login token:', data.token, 'config:', clientConfig);
|
|
230
|
+
setMessage(JSON.stringify(data));
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### [Medium] TG-SEC-004 - Sensitive Credential or Token Logging
|
|
235
|
+
- **Finding ID:** `TG-SEC-004-08753da5`
|
|
236
|
+
- **Location:** `examples/vulnerable-react-express/server/index.js:28`
|
|
237
|
+
- **Confidence Score:** `70/100` (High Confidence)
|
|
238
|
+
- **Cluster:** `cluster-credentials-exposure`
|
|
239
|
+
- **Description:** Plaintext credential or token logged to standard output
|
|
240
|
+
```
|
|
241
|
+
app.post('/api/login', (req, res) => {
|
|
242
|
+
const { email, password } = req.body;
|
|
243
|
+
console.log('Login attempt:', { email, password }); // TG-SEC-004
|
|
244
|
+
const user = users.find((u) => u.email === email && u.password === password);
|
|
245
|
+
if (!user) return res.status(401).json({ error: 'User not found' }); // enumeration
|
|
246
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"run_id": "run-20260910-130519-audit",
|
|
3
|
+
"command": "audit",
|
|
4
|
+
"target_name": "TorusGuard",
|
|
5
|
+
"created_at": "2026-09-10 13:05:19 IST",
|
|
6
|
+
"created_at_iso": "2026-09-10T13:05:19.697735+05:30",
|
|
7
|
+
"status": "completed",
|
|
8
|
+
"torusguard_version": "1.3.0",
|
|
9
|
+
"findings_count": 17,
|
|
10
|
+
"confirmed_fixed_count": 0,
|
|
11
|
+
"regressed_count": 0
|
|
12
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,8 +1,9 @@
|
|
|
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.3)
|
|
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
|
+
Strict 75-column terminal UI formatting.
|
|
6
7
|
|
|
7
8
|
Pure Python 3.10+ standard library (zero external dependencies).
|
|
8
9
|
"""
|
|
@@ -15,9 +16,18 @@ import shutil
|
|
|
15
16
|
import hashlib
|
|
16
17
|
import argparse
|
|
17
18
|
import datetime
|
|
18
|
-
import unicodedata
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Dict, List, Any, Optional
|
|
20
|
+
from typing import Dict, List, Any, Optional
|
|
21
|
+
|
|
22
|
+
# Ensure scripts dir is in sys.path for term_ui import
|
|
23
|
+
scripts_dir = Path(__file__).resolve().parent
|
|
24
|
+
if str(scripts_dir) not in sys.path:
|
|
25
|
+
sys.path.insert(0, str(scripts_dir))
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import term_ui
|
|
29
|
+
except ImportError:
|
|
30
|
+
term_ui = None
|
|
21
31
|
|
|
22
32
|
# Windows console UTF-8 support
|
|
23
33
|
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
|
|
@@ -32,7 +42,7 @@ IST = datetime.timezone(datetime.timedelta(hours=5, minutes=30), name="IST")
|
|
|
32
42
|
def get_ist_now() -> datetime.datetime:
|
|
33
43
|
return datetime.datetime.now(IST)
|
|
34
44
|
|
|
35
|
-
# ───
|
|
45
|
+
# ─── Formatting Helpers ────────────────────────────────────────────────────────
|
|
36
46
|
BOLD = "\033[1m"
|
|
37
47
|
DIM = "\033[2m"
|
|
38
48
|
RESET = "\033[0m"
|
|
@@ -43,25 +53,25 @@ WHITE = "\033[97m"
|
|
|
43
53
|
GRAY = "\033[90m"
|
|
44
54
|
RED = "\033[31m"
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return
|
|
56
|
+
def box_line(content: str, width: int = 67, border: str = "│", border_color: str = CYAN) -> str:
|
|
57
|
+
if term_ui:
|
|
58
|
+
return term_ui.format_box_line(content, width=width, border=border, border_color=border_color)
|
|
59
|
+
return f" {border_color}{border}{RESET} {content}"
|
|
60
|
+
|
|
61
|
+
def box_header(title: str, subtitle: str = "", version: str = "v1.3.3", border_color: str = CYAN) -> str:
|
|
62
|
+
if term_ui:
|
|
63
|
+
return term_ui.card_header(title, subtitle, version, border_color)
|
|
64
|
+
return f"=== {title} ({version}) ==="
|
|
65
|
+
|
|
66
|
+
def border_top(title: str = "", border_color: str = CYAN, double: bool = False) -> str:
|
|
67
|
+
if term_ui:
|
|
68
|
+
return term_ui.card_border_top(title, border_color=border_color, double=double)
|
|
69
|
+
return "┌" + "─" * 71 + "┐"
|
|
60
70
|
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return
|
|
71
|
+
def border_bottom(border_color: str = CYAN, double: bool = False) -> str:
|
|
72
|
+
if term_ui:
|
|
73
|
+
return term_ui.card_border_bottom(border_color=border_color, double=double)
|
|
74
|
+
return "└" + "─" * 71 + "┘"
|
|
65
75
|
|
|
66
76
|
|
|
67
77
|
def find_latest_audit_run(runs_dir: Path) -> Optional[Path]:
|
|
@@ -98,17 +108,17 @@ def execute_rollback(target_root: Path, run_id_arg: Optional[str] = None) -> Non
|
|
|
98
108
|
shutil.copy2(bak_file, dest_file)
|
|
99
109
|
restored_files.append(orig_rel)
|
|
100
110
|
|
|
101
|
-
print(
|
|
102
|
-
print(
|
|
103
|
-
print(
|
|
104
|
-
print(f" {CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}\n")
|
|
111
|
+
print()
|
|
112
|
+
print(box_header("🛡️ TORUSGUARD ROLLBACK RESTORATION", "Restored original files from pre-apply snapshot", "v1.3.3"))
|
|
113
|
+
print()
|
|
105
114
|
|
|
106
|
-
print(
|
|
107
|
-
print(
|
|
108
|
-
print(
|
|
115
|
+
print(border_top("Rollback Snapshot Restored"))
|
|
116
|
+
print(box_line(f"Snapshot Run: {WHITE}{snapshot_dir.name}{RESET}"))
|
|
117
|
+
print(box_line(f"Files Restored: {GREEN}{len(restored_files)} file(s) cleanly reverted{RESET}"))
|
|
109
118
|
for rf in restored_files:
|
|
110
|
-
print(
|
|
111
|
-
print(
|
|
119
|
+
print(box_line(f" ✔ {rf}"))
|
|
120
|
+
print(border_bottom())
|
|
121
|
+
print()
|
|
112
122
|
sys.exit(0)
|
|
113
123
|
|
|
114
124
|
|
|
@@ -127,21 +137,17 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
127
137
|
bundles_dir = run_folder / "bundles"
|
|
128
138
|
bundle_meta_files = list(bundles_dir.glob("*/metadata.json")) if bundles_dir.is_dir() else []
|
|
129
139
|
if not bundle_meta_files:
|
|
130
|
-
print(
|
|
131
|
-
print(
|
|
132
|
-
print(
|
|
133
|
-
print(format_box_line(f"{DIM}Human-Gate Authorization, Snapshots & Golden Recipe Distillation{RESET}", border_color=CYAN))
|
|
134
|
-
print(f" {CYAN}│ │{RESET}")
|
|
135
|
-
print(f" {CYAN}╰─────────────────────────────────────────────────────────────────────────╯{RESET}\n")
|
|
136
|
-
|
|
140
|
+
print()
|
|
141
|
+
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.3"))
|
|
142
|
+
print()
|
|
137
143
|
print(f" {YELLOW}ℹ Zero candidate remediation bundles found in run: {run_folder.name}{RESET}")
|
|
138
144
|
print(f" {GRAY}The findings in this run require manual review or AI-assisted remediation.{RESET}\n")
|
|
139
|
-
print(
|
|
140
|
-
print(
|
|
141
|
-
print(
|
|
142
|
-
print(
|
|
143
|
-
print(
|
|
144
|
-
print(
|
|
145
|
+
print(border_top("Recommended Next Steps"))
|
|
146
|
+
print(box_line("1. In AI Chat: Run /torusguard-harden to synthesize custom fixes"))
|
|
147
|
+
print(box_line(f"2. Inspect findings: .torusguard/runs/{run_folder.name}/findings.md"))
|
|
148
|
+
print(box_line("3. View visual posture report: npx torusguard report --html"))
|
|
149
|
+
print(border_bottom())
|
|
150
|
+
print()
|
|
145
151
|
sys.exit(0)
|
|
146
152
|
|
|
147
153
|
bundles = []
|
|
@@ -156,18 +162,16 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
156
162
|
snapshot_dir.mkdir(parents=True, exist_ok=True)
|
|
157
163
|
|
|
158
164
|
# Header Card
|
|
159
|
-
print(
|
|
160
|
-
print(
|
|
161
|
-
print(
|
|
162
|
-
|
|
163
|
-
print(
|
|
164
|
-
print(f"
|
|
165
|
-
|
|
166
|
-
print(f"
|
|
167
|
-
print(
|
|
168
|
-
print(
|
|
169
|
-
print(format_box_line(f"Rollback Safety:Automatic pre-apply snapshot to .torusguard/snapshots/{run_folder.name}"))
|
|
170
|
-
print(f" {CYAN}└─────────────────────────────────────────────────────────────────────────┘{RESET}\n")
|
|
165
|
+
print()
|
|
166
|
+
print(box_header("🛡️ TORUSGUARD GOVERNED PATCH APPLIER", "Human-Gate Authorization, Snapshots & Golden Recipe Distillation", "v1.3.3"))
|
|
167
|
+
print()
|
|
168
|
+
|
|
169
|
+
print(border_top("Candidate Bundles Ready for Application"))
|
|
170
|
+
print(box_line(f"Active Run: {WHITE}{run_folder.name}{RESET}"))
|
|
171
|
+
print(box_line(f"Total Bundles: {GREEN}{len(bundles)} candidate surgical patches formulated{RESET}"))
|
|
172
|
+
print(box_line(f"Rollback Safety:Automatic pre-apply snapshot to .torusguard/snapshots/"))
|
|
173
|
+
print(border_bottom())
|
|
174
|
+
print()
|
|
171
175
|
|
|
172
176
|
applied_count = 0
|
|
173
177
|
skipped_count = 0
|
|
@@ -176,10 +180,10 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
176
180
|
applied_bundles = []
|
|
177
181
|
|
|
178
182
|
# Import memory_engine for Golden Recipe distillation
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
try:
|
|
184
|
+
import memory_engine
|
|
185
|
+
except ImportError:
|
|
186
|
+
memory_engine = None
|
|
183
187
|
|
|
184
188
|
for idx, b in enumerate(bundles, 1):
|
|
185
189
|
rule_id = b.get("rule_id", "TG-SEC")
|
|
@@ -228,39 +232,67 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
228
232
|
print(f" {RED}✖ Target file not found: {target_file}{RESET}\n")
|
|
229
233
|
continue
|
|
230
234
|
|
|
231
|
-
# 1. Pre-apply Snapshot (.bak)
|
|
235
|
+
# 1. Pre-apply Snapshot (.bak) if not already created
|
|
232
236
|
bak_dest = snapshot_dir / (rel_path + ".bak")
|
|
233
|
-
bak_dest.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
if not bak_dest.is_file():
|
|
238
|
+
bak_dest.parent.mkdir(parents=True, exist_ok=True)
|
|
239
|
+
shutil.copy2(target_file, bak_dest)
|
|
240
|
+
|
|
241
|
+
# 2. Write Patched Content (Sequential Line-Aware or Full Patch)
|
|
242
|
+
target_line = b.get("target_line")
|
|
243
|
+
replacement_line = b.get("replacement_line")
|
|
244
|
+
curr_text = target_file.read_text(encoding="utf-8", errors="replace")
|
|
245
|
+
curr_lines = curr_text.splitlines()
|
|
246
|
+
|
|
247
|
+
if target_line and replacement_line is not None:
|
|
248
|
+
applied_line = False
|
|
249
|
+
# Try expected line index
|
|
250
|
+
if 0 <= line_no - 1 < len(curr_lines) and curr_lines[line_no - 1] == target_line:
|
|
251
|
+
curr_lines[line_no - 1] = replacement_line
|
|
252
|
+
applied_line = True
|
|
253
|
+
else:
|
|
254
|
+
# Search across whole file for the target line
|
|
255
|
+
for i, cl in enumerate(curr_lines):
|
|
256
|
+
if cl == target_line:
|
|
257
|
+
curr_lines[i] = replacement_line
|
|
258
|
+
applied_line = True
|
|
259
|
+
break
|
|
260
|
+
if applied_line:
|
|
261
|
+
target_file.write_text("\n".join(curr_lines) + "\n", encoding="utf-8")
|
|
262
|
+
elif patched_content:
|
|
263
|
+
target_file.write_text(patched_content, encoding="utf-8")
|
|
264
|
+
elif patched_content:
|
|
238
265
|
target_file.write_text(patched_content, encoding="utf-8")
|
|
239
266
|
else:
|
|
240
267
|
continue
|
|
241
268
|
|
|
242
269
|
# 3. Distill into Golden Fix Recipe in Memory Engine
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
fn
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
270
|
+
if memory_engine:
|
|
271
|
+
try:
|
|
272
|
+
fn = getattr(memory_engine, "record_golden_recipe", getattr(memory_engine, "store_golden_recipe", None))
|
|
273
|
+
if fn:
|
|
274
|
+
before_lines = target_file.read_text(encoding="utf-8").splitlines()
|
|
275
|
+
before_snip = before_lines[max(0, line_no - 1)] if before_lines else ""
|
|
276
|
+
fn(
|
|
277
|
+
rule_id=rule_id,
|
|
278
|
+
before_snippet=before_snip,
|
|
279
|
+
after_snippet=desc,
|
|
280
|
+
diff_snippet=diff,
|
|
281
|
+
file_type=target_file.suffix,
|
|
282
|
+
description=desc,
|
|
283
|
+
root_dir=target_root
|
|
284
|
+
)
|
|
285
|
+
# Record event
|
|
286
|
+
record_ev = getattr(memory_engine, "record_event", None)
|
|
287
|
+
if record_ev:
|
|
288
|
+
record_ev("fix_applied", {
|
|
289
|
+
"rule_id": rule_id,
|
|
290
|
+
"file_path": rel_path,
|
|
291
|
+
"line_number": line_no,
|
|
292
|
+
"bundle_id": b.get("bundle_id")
|
|
293
|
+
}, root_dir=target_root)
|
|
294
|
+
except Exception:
|
|
295
|
+
pass
|
|
264
296
|
|
|
265
297
|
applied_count += 1
|
|
266
298
|
applied_bundles.append(b)
|
|
@@ -268,7 +300,11 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
268
300
|
diff_summary_lines.append("```diff")
|
|
269
301
|
diff_summary_lines.append(diff.strip())
|
|
270
302
|
diff_summary_lines.append("```\n")
|
|
271
|
-
|
|
303
|
+
try:
|
|
304
|
+
rel_snapshot = bak_dest.relative_to(target_root)
|
|
305
|
+
except Exception:
|
|
306
|
+
rel_snapshot = bak_dest.name
|
|
307
|
+
print(f" {GREEN}✔ Applied successfully.{RESET} (Snapshot saved: {rel_snapshot})\n")
|
|
272
308
|
|
|
273
309
|
# Write apply artifacts in run folder
|
|
274
310
|
(run_folder / "diff_summary.md").write_text("\n".join(diff_summary_lines), encoding="utf-8")
|
|
@@ -294,18 +330,20 @@ def execute_apply(target_root: Path, run_id_arg: Optional[str] = None, auto_appr
|
|
|
294
330
|
pass
|
|
295
331
|
|
|
296
332
|
# Summary Box
|
|
297
|
-
print(
|
|
298
|
-
print(
|
|
299
|
-
print(
|
|
300
|
-
print(
|
|
301
|
-
print(
|
|
302
|
-
print(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
print(
|
|
306
|
-
print(
|
|
307
|
-
print(
|
|
308
|
-
print(f"
|
|
333
|
+
print(border_top("Application Summary"))
|
|
334
|
+
print(box_line(f"Applied Patches:{GREEN}{applied_count} applied cleanly{RESET}"))
|
|
335
|
+
print(box_line(f"Skipped / Pass: {GRAY}{skipped_count} skipped{RESET}"))
|
|
336
|
+
print(box_line(f"Golden Recipes: {GREEN}{applied_count} distilled into persistent memory{RESET}"))
|
|
337
|
+
print(box_line(f"Snapshot Safety:.torusguard/snapshots/{run_folder.name}"))
|
|
338
|
+
print(border_bottom())
|
|
339
|
+
print()
|
|
340
|
+
|
|
341
|
+
print(border_top("Next Governed Action", border_color=GREEN, double=True))
|
|
342
|
+
print(box_line(f"Differential Recheck: {BOLD}{WHITE}npx torusguard recheck{RESET} (Verify fix closure)", border="║", border_color=GREEN))
|
|
343
|
+
print(box_line(f"View Golden Recipes: {CYAN}npx torusguard recipes{RESET} (Inspect memory)", border="║", border_color=GREEN))
|
|
344
|
+
print(box_line(f"Emergency Rollback: {YELLOW}npx torusguard rollback{RESET} (Instant revert)", border="║", border_color=GREEN))
|
|
345
|
+
print(border_bottom(border_color=GREEN, double=True))
|
|
346
|
+
print()
|
|
309
347
|
|
|
310
348
|
return {
|
|
311
349
|
"applied_count": applied_count,
|