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
package/package.json
CHANGED
|
@@ -1,42 +1,72 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: torusguard
|
|
3
|
-
description:
|
|
4
|
-
version: 1.3.
|
|
3
|
+
description: Universal autonomous security engine: 71 canonical rules across 11 families, polyglot stack detection across 16+ languages, Ponytail remediation bounds (<=35 add, <=25 del), standardized 75-column terminal UI, SARIF v2.1.0 exports, and persistent security memory context.
|
|
4
|
+
version: 1.3.4
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# TorusGuard Master Security Router
|
|
7
|
+
# TorusGuard Master Security Engine & Command Router
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Core Invariant:** If the browser or client receives it, users can inspect it. Never expose database credentials, master keys, or private API secrets in client bundles. Always isolate multi-tenant database queries.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
## Command Catalog: Terminal CLI & AI Chat Parity
|
|
14
|
+
|
|
15
|
+
TorusGuard operates with 100% feature parity across both the terminal CLI and AI chat slash commands:
|
|
16
|
+
|
|
17
|
+
| Capability | Terminal CLI Command | AI Chat Slash Command | Specialist Skill | Purpose |
|
|
18
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
19
|
+
| **Init** | `npx torusguard init` | `/torusguard init` | `skills/torusguard-init` | Stack discovery, rule activation, workspace scaffolding |
|
|
20
|
+
| **Status** | `npx torusguard status` | `/torusguard status` | `skills/torusguard-status` | Posture score, active rules, run history, scope check |
|
|
21
|
+
| **Audit** | `npx torusguard audit` | `/torusguard audit` | `skills/torusguard-audit` | Static AST scan, invariant fingerprinting, confidence scoring |
|
|
22
|
+
| **Verify** | `npx torusguard verify` | `/torusguard verify` | `skills/torusguard-verify` | Evidence sufficiency audit & line match calibration |
|
|
23
|
+
| **Harden** | `npx torusguard harden` | `/torusguard harden` | `skills/torusguard-harden` | Ponytail patch formulation ($\le 35$ add, $\le 25$ del) |
|
|
24
|
+
| **Apply** | `npx torusguard apply [--yes]` | `/torusguard apply` | `skills/torusguard-apply` | Human Gate review, `.bak` snapshots, patch application |
|
|
25
|
+
| **Rollback** | `npx torusguard rollback` | `/torusguard rollback` | `skills/torusguard-apply` | Instant 1-step rollback from pre-apply snapshots |
|
|
26
|
+
| **Recheck** | `npx torusguard recheck` | `/torusguard recheck` | `skills/torusguard-recheck` | Targeted differential AST scan, fix closure verification |
|
|
27
|
+
| **Recipes** | `npx torusguard recipes` | `/torusguard recipes` | `skills/torusguard-harden` | Explore verified Golden Fix Recipes from persistent memory |
|
|
28
|
+
| **Report** | `npx torusguard report --html` | `/torusguard report` | `skills/torusguard-report` | Executive posture reporting, SARIF v2.1.0 & visual HTML |
|
|
29
|
+
| **Authorize** | `npx torusguard authorize` | `/torusguard authorize` | `skills/torusguard-authorize`| Legal scope definition & safety boundaries |
|
|
30
|
+
| **Validate** | `npx torusguard web-validate` | `/torusguard web-validate` | `skills/torusguard-web-validate`| Authorized non-destructive HTTP probing |
|
|
31
|
+
| **Exploit** | `npx torusguard exploit-check` | `/torusguard exploit-check`| `skills/torusguard-exploit-check`| Bounded single-step exploitability confirmation |
|
|
32
|
+
| **Full** | `npx torusguard full` | `/torusguard full` | `skills/torusguard-full` | End-to-end 7-stage closed-loop execution |
|
|
33
|
+
| **Sync** | `npx torusguard rules sync` | `/torusguard rules sync` | `skills/torusguard-init` | Synchronize rules across AI editors (Cursor, Claude, Antigravity) |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Non-Negotiable Invariants
|
|
38
|
+
|
|
39
|
+
1. **Browser-Code Truth:** If the client receives it, it is public. Zero hardcoded secrets (`SUPABASE_SERVICE_ROLE_KEY`, private tokens, live API keys) in frontend code.
|
|
40
|
+
2. **Multi-Tenant Isolation:** All database lookups must be scoped by organization or user ownership (`tenant_id`, `organization_id`, `where: { tenantId }`).
|
|
41
|
+
3. **Ponytail Churn Bounds:** Patches must be minimal and surgical ($\le 35$ additions, $\le 25$ deletions). Never perform full-file rewrites.
|
|
42
|
+
4. **Standardized 75-Column Terminal:** All CLI terminal output is strictly normalized to 75 visual columns with Unicode emoji width calculation, ANSI escape handling, and visual truncation with ellipsis (`...`).
|
|
43
|
+
5. **Zero Security Bypasses:** Never insert `# nosec`, `verify=False`, `InsecureSkipVerify: true`, `[AllowAnonymous]`, or `csrf().disable()`.
|
|
44
|
+
6. **Snapshots Before Edits:** Every code modification must capture a byte-for-byte pre-apply backup in `.torusguard/snapshots/<run_id>/` before touching disk code.
|
|
20
45
|
|
|
21
46
|
---
|
|
22
47
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
## Workspace Layout Structure
|
|
49
|
+
```
|
|
50
|
+
.torusguard/
|
|
51
|
+
├── config/
|
|
52
|
+
│ ├── torusguard.json # Detected stack, settings, and rule counts
|
|
53
|
+
│ └── scope.json # Authorized runtime validation targets & TTL
|
|
54
|
+
├── memory/
|
|
55
|
+
│ ├── context.json # Aggregated security memory context
|
|
56
|
+
│ ├── patterns.json # Distilled Golden Fix Recipes
|
|
57
|
+
│ └── events.json # Historical audit, apply, and recheck events
|
|
58
|
+
├── rules/
|
|
59
|
+
│ └── active/ # Active TG-* rule definitions
|
|
60
|
+
├── runs/
|
|
61
|
+
│ └── run-YYYYMMDD-HHMMSS-audit/
|
|
62
|
+
│ ├── findings.json # Machine-readable AST findings
|
|
63
|
+
│ ├── findings.md # Actionable markdown finding cards
|
|
64
|
+
│ ├── remediation.md # Formulated candidate patch catalog
|
|
65
|
+
│ ├── diff_summary.md # Summary of applied diffs
|
|
66
|
+
│ ├── recheck.md # Differential recheck status transitions
|
|
67
|
+
│ ├── report.html # Single-file visual dark-mode HTML report
|
|
68
|
+
│ ├── results.sarif # OASIS SARIF v2.1.0 log
|
|
69
|
+
│ └── bundles/ # Formulated Ponytail remediation bundles
|
|
70
|
+
└── snapshots/
|
|
71
|
+
└── run-YYYYMMDD-HHMMSS-audit/ # Pre-apply .bak files for instant rollback
|
|
72
|
+
```
|
|
@@ -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,
|