torusguard 0.9.5 → 1.0.0
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 +15 -10
- package/.torusguard/config/torusguard.json +1 -1
- package/.torusguard/schemas/memory-context.schema.json +95 -0
- package/.torusguard/schemas/memory-event.schema.json +70 -0
- package/.torusguard/schemas/memory-pattern.schema.json +65 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/finding_scorer.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/memory_engine.cpython-311.pyc +0 -0
- package/.torusguard/scripts/diff_guard.py +71 -11
- package/.torusguard/scripts/finding_scorer.py +171 -86
- package/.torusguard/scripts/manifest_builder.py +2 -0
- package/.torusguard/scripts/memory_engine.py +912 -0
- package/.torusguard/scripts/run_manager.py +171 -89
- package/.torusguard/skills/torusguard/SKILL.md +4 -3
- package/.torusguard/skills/torusguard/bootstrap.py +344 -56
- package/.torusguard/skills/torusguard-audit/SKILL.md +25 -25
- package/.torusguard/skills/torusguard-harden/SKILL.md +3 -3
- package/.torusguard/skills/torusguard-recheck/SKILL.md +3 -3
- package/.torusguard/workflows/memory.md +53 -0
- package/README.md +11 -2
- package/bin/torusguard.js +99 -5
- package/package.json +1 -1
- package/skills/torusguard/SKILL.md +4 -3
- package/skills/torusguard/__pycache__/bootstrap.cpython-311.pyc +0 -0
- package/skills/torusguard/bootstrap.py +66 -2
- package/skills/torusguard/payload/.manifest.json +15 -10
- package/skills/torusguard/payload/config/torusguard.json +1 -1
- package/skills/torusguard/payload/schemas/memory-context.schema.json +95 -0
- package/skills/torusguard/payload/schemas/memory-event.schema.json +70 -0
- package/skills/torusguard/payload/schemas/memory-pattern.schema.json +65 -0
- package/skills/torusguard/payload/scripts/diff_guard.py +71 -11
- package/skills/torusguard/payload/scripts/finding_scorer.py +171 -86
- package/skills/torusguard/payload/scripts/manifest_builder.py +2 -0
- package/skills/torusguard/payload/scripts/memory_engine.py +912 -0
- package/skills/torusguard/payload/scripts/run_manager.py +171 -89
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +4 -3
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +344 -56
- package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +25 -25
- package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +3 -3
- package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +3 -3
- package/skills/torusguard/payload/workflows/memory.md +53 -0
- package/skills/torusguard-audit/SKILL.md +25 -25
- package/skills/torusguard-harden/SKILL.md +3 -3
- package/skills/torusguard-recheck/SKILL.md +3 -3
- package/skills/torusguard/payload/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/skills/torusguard/payload/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
|
@@ -1,89 +1,171 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
TorusGuard Run Folder Lifecycle Manager
|
|
4
|
-
Creates isolated run folders, tracks manifest.json state,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
runs.append(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Run Folder Lifecycle Manager (v1.0.0)
|
|
4
|
+
Creates isolated run folders, tracks manifest.json state, enumerates execution history,
|
|
5
|
+
and bridges completed run findings to the persistent security memory engine.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import datetime
|
|
11
|
+
import argparse
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, Any, List, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def create_run(base_dir: Path, command: str, target_name: str = "project") -> Path:
|
|
17
|
+
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")
|
|
18
|
+
run_id = f"run-{timestamp}-{command}"
|
|
19
|
+
run_folder = base_dir / run_id
|
|
20
|
+
run_folder.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
|
|
22
|
+
manifest = {
|
|
23
|
+
"run_id": run_id,
|
|
24
|
+
"command": command,
|
|
25
|
+
"target_name": target_name,
|
|
26
|
+
"created_at": datetime.datetime.utcnow().isoformat() + "Z",
|
|
27
|
+
"status": "in_progress",
|
|
28
|
+
"torusguard_version": "1.0.0",
|
|
29
|
+
"findings_count": 0,
|
|
30
|
+
"confirmed_fixed_count": 0,
|
|
31
|
+
"regressed_count": 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
manifest_path = run_folder / "manifest.json"
|
|
35
|
+
with open(manifest_path, "w", encoding="utf-8") as f:
|
|
36
|
+
json.dump(manifest, f, indent=2)
|
|
37
|
+
|
|
38
|
+
return run_folder
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def list_runs(base_dir: Path) -> List[Dict[str, Any]]:
|
|
42
|
+
runs = []
|
|
43
|
+
if not base_dir.is_dir():
|
|
44
|
+
return runs
|
|
45
|
+
|
|
46
|
+
for d in sorted(base_dir.iterdir(), reverse=True):
|
|
47
|
+
if d.is_dir() and d.name.startswith("run-"):
|
|
48
|
+
manifest_file = d / "manifest.json"
|
|
49
|
+
if manifest_file.is_file():
|
|
50
|
+
try:
|
|
51
|
+
with open(manifest_file, "r", encoding="utf-8") as f:
|
|
52
|
+
data = json.load(f)
|
|
53
|
+
runs.append(data)
|
|
54
|
+
except Exception:
|
|
55
|
+
runs.append({"run_id": d.name, "status": "corrupted"})
|
|
56
|
+
else:
|
|
57
|
+
runs.append({"run_id": d.name, "status": "unmanifested"})
|
|
58
|
+
return runs
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sync_run_to_memory(run_folder: Path, findings: Optional[List[Dict[str, Any]]] = None) -> int:
|
|
62
|
+
"""
|
|
63
|
+
Sync findings from a completed run into the .torusguard/memory/ engine.
|
|
64
|
+
Records events based on command type (audit -> audit_finding, apply -> fix_applied, recheck -> fix_verified).
|
|
65
|
+
"""
|
|
66
|
+
manifest_file = run_folder / "manifest.json"
|
|
67
|
+
if not manifest_file.is_file():
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
with open(manifest_file, "r", encoding="utf-8") as f:
|
|
72
|
+
manifest = json.load(f)
|
|
73
|
+
except Exception:
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
command = manifest.get("command", "audit")
|
|
77
|
+
|
|
78
|
+
# If findings not explicitly provided, try reading findings.json in run_folder
|
|
79
|
+
actual_findings = findings
|
|
80
|
+
if actual_findings is None:
|
|
81
|
+
findings_file = run_folder / "findings.json"
|
|
82
|
+
if findings_file.is_file():
|
|
83
|
+
try:
|
|
84
|
+
with open(findings_file, "r", encoding="utf-8") as f:
|
|
85
|
+
actual_findings = json.load(f)
|
|
86
|
+
except Exception:
|
|
87
|
+
actual_findings = []
|
|
88
|
+
|
|
89
|
+
if not actual_findings or not isinstance(actual_findings, list):
|
|
90
|
+
return 0
|
|
91
|
+
|
|
92
|
+
# Import memory_engine in-process
|
|
93
|
+
try:
|
|
94
|
+
scripts_dir = Path(__file__).resolve().parent
|
|
95
|
+
if str(scripts_dir) not in sys.path:
|
|
96
|
+
sys.path.insert(0, str(scripts_dir))
|
|
97
|
+
import memory_engine
|
|
98
|
+
|
|
99
|
+
recorded_count = 0
|
|
100
|
+
for f in actual_findings:
|
|
101
|
+
rule_id = f.get("rule_id") or f.get("id")
|
|
102
|
+
if not rule_id:
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
event_type = "audit_finding"
|
|
106
|
+
if command in ("harden", "apply"):
|
|
107
|
+
event_type = "fix_applied"
|
|
108
|
+
elif command in ("recheck", "verify"):
|
|
109
|
+
event_type = "fix_verified"
|
|
110
|
+
|
|
111
|
+
data = {
|
|
112
|
+
"rule_id": rule_id,
|
|
113
|
+
"file_path": f.get("file_path") or f.get("file"),
|
|
114
|
+
"line_number": f.get("line_number") or f.get("line"),
|
|
115
|
+
"severity": (f.get("severity") or "medium").lower(),
|
|
116
|
+
"confidence_score": f.get("confidence_score") or f.get("confidence", 70),
|
|
117
|
+
"code_hash": f.get("code_hash"),
|
|
118
|
+
"fix_strategy": f.get("fix_strategy") or f.get("strategy"),
|
|
119
|
+
"verification_result": f.get("verification_result") or f.get("result", "fixed"),
|
|
120
|
+
"suppression_reason": f.get("suppression_reason")
|
|
121
|
+
}
|
|
122
|
+
memory_engine.record_event(event_type, data)
|
|
123
|
+
recorded_count += 1
|
|
124
|
+
|
|
125
|
+
if recorded_count > 0:
|
|
126
|
+
memory_engine.distill_patterns()
|
|
127
|
+
|
|
128
|
+
return recorded_count
|
|
129
|
+
except Exception as e:
|
|
130
|
+
# Non-blocking: memory failure should never crash a run
|
|
131
|
+
return 0
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def main():
|
|
135
|
+
parser = argparse.ArgumentParser(description="TorusGuard Run Manager")
|
|
136
|
+
subparsers = parser.add_subparsers(dest="subcommand")
|
|
137
|
+
|
|
138
|
+
create_p = subparsers.add_parser("create")
|
|
139
|
+
create_p.add_argument("--command", "-c", default="audit", help="TorusGuard command (audit, web-validate, harden, etc.)")
|
|
140
|
+
create_p.add_argument("--target", "-t", default="project", help="Target project name")
|
|
141
|
+
create_p.add_argument("--dir", "-d", default=".torusguard/runs", help="Base runs directory")
|
|
142
|
+
|
|
143
|
+
list_p = subparsers.add_parser("list")
|
|
144
|
+
list_p.add_argument("--dir", "-d", default=".torusguard/runs", help="Base runs directory")
|
|
145
|
+
list_p.add_argument("--json", action="store_true", help="Output JSON")
|
|
146
|
+
|
|
147
|
+
sync_p = subparsers.add_parser("sync-memory")
|
|
148
|
+
sync_p.add_argument("--run-folder", "-r", required=True, help="Path to run folder")
|
|
149
|
+
|
|
150
|
+
args = parser.parse_args()
|
|
151
|
+
|
|
152
|
+
if args.subcommand == "create":
|
|
153
|
+
folder = create_run(Path(args.dir), args.command, args.target)
|
|
154
|
+
print(f"Created run folder: {folder}")
|
|
155
|
+
elif args.subcommand == "list":
|
|
156
|
+
runs = list_runs(Path(args.dir))
|
|
157
|
+
if args.json:
|
|
158
|
+
print(json.dumps(runs, indent=2))
|
|
159
|
+
else:
|
|
160
|
+
print(f"Total Runs Found: {len(runs)}")
|
|
161
|
+
for r in runs:
|
|
162
|
+
print(f" - {r.get('run_id')}: command={r.get('command')}, status={r.get('status')}")
|
|
163
|
+
elif args.subcommand == "sync-memory":
|
|
164
|
+
count = sync_run_to_memory(Path(args.run_folder))
|
|
165
|
+
print(f"Synced {count} findings from run to memory.")
|
|
166
|
+
else:
|
|
167
|
+
parser.print_help()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
main()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: torusguard
|
|
3
|
-
description: "Autonomous security engine: 71 rules across 11 families, governed remediation (Ponytail Protocol), SARIF v2.1.0 exports, and
|
|
4
|
-
version: 0.
|
|
3
|
+
description: "Autonomous security engine: 71 rules across 11 families, governed remediation (Ponytail Protocol), SARIF v2.1.0 exports, and persistent security memory context for Python & TypeScript web applications."
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# TorusGuard Master Security Router
|
|
@@ -11,7 +11,7 @@ version: 0.9.5
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
## ⚡ Workspace Bootstrap Check
|
|
14
|
-
1. **If `.torusguard/` exists on disk:** Full governance active. Route
|
|
14
|
+
1. **If `.torusguard/` exists on disk:** Full governance active. Load `.torusguard/memory/context.json` into prompt context for local-first intelligence. Route commands to specialist workflows below.
|
|
15
15
|
2. **If `.torusguard/` is absent (Autonomous AI Mode):** Operates standalone in any AI agent (Kimi, Antigravity, VS Code, Cursor). Apply in-memory security invariants:
|
|
16
16
|
- Server-side auth & tenant scoping (`.filter(tenant=...)`).
|
|
17
17
|
- Zero client-side secrets (`SUPABASE_SERVICE_ROLE_KEY`, live API keys).
|
|
@@ -36,6 +36,7 @@ Load **only** the designated sub-skill on demand to preserve the 1,000–1,500 t
|
|
|
36
36
|
| `/torusguard recheck` | `skills/torusguard-recheck/SKILL.md` | `reviewer` |
|
|
37
37
|
| `/torusguard report` | `skills/torusguard-report/SKILL.md` | `reviewer` |
|
|
38
38
|
| `/torusguard status` | `skills/torusguard-status/SKILL.md` | `reviewer` |
|
|
39
|
+
| `/torusguard memory` | `skills/torusguard/SKILL.md` | `reviewer` |
|
|
39
40
|
| `/torusguard full` | `skills/torusguard-full/SKILL.md` | *All* |
|
|
40
41
|
|
|
41
42
|
*Note: Never load all skills simultaneously. Lazy-load strictly on demand.*
|