torusguard 0.9.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 +95 -0
- package/.torusguard/ARCHITECTURE.md +168 -0
- package/.torusguard/TORUSGUARD.md +145 -0
- package/.torusguard/agents/auditor.md +41 -0
- package/.torusguard/agents/profiler.md +49 -0
- package/.torusguard/agents/remediator.md +41 -0
- package/.torusguard/agents/reviewer.md +39 -0
- package/.torusguard/agents/validator.md +46 -0
- package/.torusguard/config/scope.json +24 -0
- package/.torusguard/config/slash-commands.json +102 -0
- package/.torusguard/config/torusguard.json +30 -0
- package/.torusguard/references/django-security.md +43 -0
- package/.torusguard/references/drf-security.md +20 -0
- package/.torusguard/references/express-security.md +103 -0
- package/.torusguard/references/fastapi-security.md +20 -0
- package/.torusguard/references/firebase-security.md +78 -0
- package/.torusguard/references/flask-security.md +20 -0
- package/.torusguard/references/nextjs-security.md +96 -0
- package/.torusguard/references/react-vite-security.md +97 -0
- package/.torusguard/references/sqlalchemy-security.md +16 -0
- package/.torusguard/references/supabase-security.md +84 -0
- package/.torusguard/rules/README.md +62 -0
- package/.torusguard/rules/TORUSGUARD.md +145 -0
- package/.torusguard/rules/active/.gitkeep +0 -0
- package/.torusguard/rules/active/TG-DIFF-001.md +20 -0
- package/.torusguard/rules/active/TG-DIFF-002.md +20 -0
- package/.torusguard/rules/active/TG-DIFF-003.md +19 -0
- package/.torusguard/runs/.gitkeep +0 -0
- package/.torusguard/schemas/authorization.schema.json +81 -0
- package/.torusguard/schemas/confidence.schema.json +72 -0
- package/.torusguard/schemas/evidence.schema.json +66 -0
- package/.torusguard/schemas/finding.schema.json +164 -0
- package/.torusguard/schemas/provenance.schema.json +46 -0
- package/.torusguard/schemas/remediation.schema.json +59 -0
- package/.torusguard/schemas/retest.schema.json +53 -0
- package/.torusguard/schemas/runtime-evidence.schema.json +72 -0
- package/.torusguard/scripts/__pycache__/diff_guard.cpython-311.pyc +0 -0
- package/.torusguard/scripts/__pycache__/monorepo_detector.cpython-311.pyc +0 -0
- package/.torusguard/scripts/diff_guard.py +171 -0
- package/.torusguard/scripts/finding_scorer.py +86 -0
- package/.torusguard/scripts/manifest_builder.py +130 -0
- package/.torusguard/scripts/monorepo_detector.py +164 -0
- package/.torusguard/scripts/run_manager.py +89 -0
- package/.torusguard/scripts/safety_gate.py +64 -0
- package/.torusguard/scripts/sarif_exporter.py +116 -0
- package/.torusguard/scripts/stack_detect.py +152 -0
- package/.torusguard/skills/torusguard/SKILL.md +37 -0
- package/.torusguard/skills/torusguard/bootstrap.py +158 -0
- package/.torusguard/skills/torusguard/references/auth-and-sessions.md +56 -0
- package/.torusguard/skills/torusguard/references/business-logic-and-abuse.md +23 -0
- package/.torusguard/skills/torusguard/references/cache-and-sensitive-responses.md +10 -0
- package/.torusguard/skills/torusguard/references/client-code-exposure.md +55 -0
- package/.torusguard/skills/torusguard/references/csrf-and-cross-origin.md +11 -0
- package/.torusguard/skills/torusguard/references/dependency-and-supply-chain.md +22 -0
- package/.torusguard/skills/torusguard/references/django-security.md +43 -0
- package/.torusguard/skills/torusguard/references/drf-security.md +20 -0
- package/.torusguard/skills/torusguard/references/fastapi-security.md +20 -0
- package/.torusguard/skills/torusguard/references/flask-security.md +20 -0
- package/.torusguard/skills/torusguard/references/frontend-no-db.md +53 -0
- package/.torusguard/skills/torusguard/references/graphql-security.md +12 -0
- package/.torusguard/skills/torusguard/references/input-and-injection.md +51 -0
- package/.torusguard/skills/torusguard/references/mass-assignment-and-property-auth.md +22 -0
- package/.torusguard/skills/torusguard/references/platform-hardening.md +56 -0
- package/.torusguard/skills/torusguard/references/python-dependencies.md +16 -0
- package/.torusguard/skills/torusguard/references/python-security-overview.md +12 -0
- package/.torusguard/skills/torusguard/references/rate-limit-and-abuse.md +57 -0
- package/.torusguard/skills/torusguard/references/secrets-and-config.md +64 -0
- package/.torusguard/skills/torusguard/references/sqlalchemy-security.md +16 -0
- package/.torusguard/skills/torusguard/references/ssrf-and-outbound-requests.md +46 -0
- package/.torusguard/skills/torusguard/references/webhook-security.md +15 -0
- package/.torusguard/skills/torusguard/references/websocket-security.md +12 -0
- package/.torusguard/skills/torusguard-apply/SKILL.md +46 -0
- package/.torusguard/skills/torusguard-audit/SKILL.md +70 -0
- package/.torusguard/skills/torusguard-authorize/SKILL.md +47 -0
- package/.torusguard/skills/torusguard-exploit-check/SKILL.md +64 -0
- package/.torusguard/skills/torusguard-full/SKILL.md +70 -0
- package/.torusguard/skills/torusguard-harden/SKILL.md +63 -0
- package/.torusguard/skills/torusguard-init/SKILL.md +52 -0
- package/.torusguard/skills/torusguard-recheck/SKILL.md +53 -0
- package/.torusguard/skills/torusguard-report/SKILL.md +54 -0
- package/.torusguard/skills/torusguard-status/SKILL.md +42 -0
- package/.torusguard/skills/torusguard-verify/SKILL.md +55 -0
- package/.torusguard/skills/torusguard-web-validate/SKILL.md +64 -0
- package/.torusguard/templates/audit-report.template.md +54 -0
- package/.torusguard/templates/authorization.template.md +34 -0
- package/.torusguard/templates/finding-card.template.md +32 -0
- package/.torusguard/templates/remediation-bundle.template.md +35 -0
- package/.torusguard/workflows/apply.md +95 -0
- package/.torusguard/workflows/audit.md +93 -0
- package/.torusguard/workflows/authorize.md +95 -0
- package/.torusguard/workflows/exploit-check.md +97 -0
- package/.torusguard/workflows/harden.md +93 -0
- package/.torusguard/workflows/init.md +95 -0
- package/.torusguard/workflows/recheck.md +95 -0
- package/.torusguard/workflows/report.md +95 -0
- package/.torusguard/workflows/status.md +95 -0
- package/.torusguard/workflows/verify.md +95 -0
- package/.torusguard/workflows/web-validate.md +95 -0
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/bin/torusguard.js +110 -0
- package/install.py +98 -0
- package/package.json +48 -0
- package/skills/torusguard/SKILL.md +41 -0
- package/skills/torusguard/__pycache__/bootstrap.cpython-311.pyc +0 -0
- package/skills/torusguard/bootstrap.py +256 -0
- package/skills/torusguard/payload/.manifest.json +95 -0
- package/skills/torusguard/payload/ARCHITECTURE.md +168 -0
- package/skills/torusguard/payload/TORUSGUARD.md +145 -0
- package/skills/torusguard/payload/agents/auditor.md +41 -0
- package/skills/torusguard/payload/agents/profiler.md +49 -0
- package/skills/torusguard/payload/agents/remediator.md +41 -0
- package/skills/torusguard/payload/agents/reviewer.md +39 -0
- package/skills/torusguard/payload/agents/validator.md +46 -0
- package/skills/torusguard/payload/config/scope.json +24 -0
- package/skills/torusguard/payload/config/slash-commands.json +102 -0
- package/skills/torusguard/payload/config/torusguard.json +30 -0
- package/skills/torusguard/payload/references/django-security.md +43 -0
- package/skills/torusguard/payload/references/drf-security.md +20 -0
- package/skills/torusguard/payload/references/express-security.md +103 -0
- package/skills/torusguard/payload/references/fastapi-security.md +20 -0
- package/skills/torusguard/payload/references/firebase-security.md +78 -0
- package/skills/torusguard/payload/references/flask-security.md +20 -0
- package/skills/torusguard/payload/references/nextjs-security.md +96 -0
- package/skills/torusguard/payload/references/react-vite-security.md +97 -0
- package/skills/torusguard/payload/references/sqlalchemy-security.md +16 -0
- package/skills/torusguard/payload/references/supabase-security.md +84 -0
- package/skills/torusguard/payload/rules/README.md +62 -0
- package/skills/torusguard/payload/rules/TORUSGUARD.md +145 -0
- package/skills/torusguard/payload/rules/active/.gitkeep +0 -0
- package/skills/torusguard/payload/rules/active/TG-DIFF-001.md +20 -0
- package/skills/torusguard/payload/rules/active/TG-DIFF-002.md +20 -0
- package/skills/torusguard/payload/rules/active/TG-DIFF-003.md +19 -0
- package/skills/torusguard/payload/schemas/authorization.schema.json +81 -0
- package/skills/torusguard/payload/schemas/confidence.schema.json +72 -0
- package/skills/torusguard/payload/schemas/evidence.schema.json +66 -0
- package/skills/torusguard/payload/schemas/finding.schema.json +164 -0
- package/skills/torusguard/payload/schemas/provenance.schema.json +46 -0
- package/skills/torusguard/payload/schemas/remediation.schema.json +59 -0
- package/skills/torusguard/payload/schemas/retest.schema.json +53 -0
- package/skills/torusguard/payload/schemas/runtime-evidence.schema.json +72 -0
- package/skills/torusguard/payload/scripts/diff_guard.py +171 -0
- package/skills/torusguard/payload/scripts/finding_scorer.py +86 -0
- package/skills/torusguard/payload/scripts/manifest_builder.py +130 -0
- package/skills/torusguard/payload/scripts/monorepo_detector.py +164 -0
- package/skills/torusguard/payload/scripts/run_manager.py +89 -0
- package/skills/torusguard/payload/scripts/safety_gate.py +64 -0
- package/skills/torusguard/payload/scripts/sarif_exporter.py +116 -0
- package/skills/torusguard/payload/scripts/stack_detect.py +152 -0
- package/skills/torusguard/payload/skills/torusguard/SKILL.md +37 -0
- package/skills/torusguard/payload/skills/torusguard/bootstrap.py +158 -0
- package/skills/torusguard/payload/skills/torusguard/references/auth-and-sessions.md +56 -0
- package/skills/torusguard/payload/skills/torusguard/references/business-logic-and-abuse.md +23 -0
- package/skills/torusguard/payload/skills/torusguard/references/cache-and-sensitive-responses.md +10 -0
- package/skills/torusguard/payload/skills/torusguard/references/client-code-exposure.md +55 -0
- package/skills/torusguard/payload/skills/torusguard/references/csrf-and-cross-origin.md +11 -0
- package/skills/torusguard/payload/skills/torusguard/references/dependency-and-supply-chain.md +22 -0
- package/skills/torusguard/payload/skills/torusguard/references/django-security.md +43 -0
- package/skills/torusguard/payload/skills/torusguard/references/drf-security.md +20 -0
- package/skills/torusguard/payload/skills/torusguard/references/fastapi-security.md +20 -0
- package/skills/torusguard/payload/skills/torusguard/references/flask-security.md +20 -0
- package/skills/torusguard/payload/skills/torusguard/references/frontend-no-db.md +53 -0
- package/skills/torusguard/payload/skills/torusguard/references/graphql-security.md +12 -0
- package/skills/torusguard/payload/skills/torusguard/references/input-and-injection.md +51 -0
- package/skills/torusguard/payload/skills/torusguard/references/mass-assignment-and-property-auth.md +22 -0
- package/skills/torusguard/payload/skills/torusguard/references/platform-hardening.md +56 -0
- package/skills/torusguard/payload/skills/torusguard/references/python-dependencies.md +16 -0
- package/skills/torusguard/payload/skills/torusguard/references/python-security-overview.md +12 -0
- package/skills/torusguard/payload/skills/torusguard/references/rate-limit-and-abuse.md +57 -0
- package/skills/torusguard/payload/skills/torusguard/references/secrets-and-config.md +64 -0
- package/skills/torusguard/payload/skills/torusguard/references/sqlalchemy-security.md +16 -0
- package/skills/torusguard/payload/skills/torusguard/references/ssrf-and-outbound-requests.md +46 -0
- package/skills/torusguard/payload/skills/torusguard/references/webhook-security.md +15 -0
- package/skills/torusguard/payload/skills/torusguard/references/websocket-security.md +12 -0
- package/skills/torusguard/payload/skills/torusguard-apply/SKILL.md +46 -0
- package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +70 -0
- package/skills/torusguard/payload/skills/torusguard-authorize/SKILL.md +47 -0
- package/skills/torusguard/payload/skills/torusguard-exploit-check/SKILL.md +64 -0
- package/skills/torusguard/payload/skills/torusguard-full/SKILL.md +70 -0
- package/skills/torusguard/payload/skills/torusguard-harden/SKILL.md +63 -0
- package/skills/torusguard/payload/skills/torusguard-init/SKILL.md +52 -0
- package/skills/torusguard/payload/skills/torusguard-recheck/SKILL.md +53 -0
- package/skills/torusguard/payload/skills/torusguard-report/SKILL.md +54 -0
- package/skills/torusguard/payload/skills/torusguard-status/SKILL.md +42 -0
- package/skills/torusguard/payload/skills/torusguard-verify/SKILL.md +55 -0
- package/skills/torusguard/payload/skills/torusguard-web-validate/SKILL.md +64 -0
- package/skills/torusguard/payload/templates/audit-report.template.md +54 -0
- package/skills/torusguard/payload/templates/authorization.template.md +34 -0
- package/skills/torusguard/payload/templates/finding-card.template.md +32 -0
- package/skills/torusguard/payload/templates/remediation-bundle.template.md +35 -0
- package/skills/torusguard/payload/workflows/apply.md +95 -0
- package/skills/torusguard/payload/workflows/audit.md +93 -0
- package/skills/torusguard/payload/workflows/authorize.md +95 -0
- package/skills/torusguard/payload/workflows/exploit-check.md +97 -0
- package/skills/torusguard/payload/workflows/harden.md +93 -0
- package/skills/torusguard/payload/workflows/init.md +95 -0
- package/skills/torusguard/payload/workflows/recheck.md +95 -0
- package/skills/torusguard/payload/workflows/report.md +95 -0
- package/skills/torusguard/payload/workflows/status.md +95 -0
- package/skills/torusguard/payload/workflows/verify.md +95 -0
- package/skills/torusguard/payload/workflows/web-validate.md +95 -0
- package/skills/torusguard/references/auth-and-sessions.md +56 -0
- package/skills/torusguard/references/business-logic-and-abuse.md +23 -0
- package/skills/torusguard/references/cache-and-sensitive-responses.md +10 -0
- package/skills/torusguard/references/client-code-exposure.md +55 -0
- package/skills/torusguard/references/csrf-and-cross-origin.md +11 -0
- package/skills/torusguard/references/dependency-and-supply-chain.md +22 -0
- package/skills/torusguard/references/django-security.md +43 -0
- package/skills/torusguard/references/drf-security.md +20 -0
- package/skills/torusguard/references/fastapi-security.md +20 -0
- package/skills/torusguard/references/flask-security.md +20 -0
- package/skills/torusguard/references/frontend-no-db.md +53 -0
- package/skills/torusguard/references/graphql-security.md +12 -0
- package/skills/torusguard/references/input-and-injection.md +51 -0
- package/skills/torusguard/references/mass-assignment-and-property-auth.md +22 -0
- package/skills/torusguard/references/platform-hardening.md +56 -0
- package/skills/torusguard/references/python-dependencies.md +16 -0
- package/skills/torusguard/references/python-security-overview.md +12 -0
- package/skills/torusguard/references/rate-limit-and-abuse.md +57 -0
- package/skills/torusguard/references/secrets-and-config.md +64 -0
- package/skills/torusguard/references/sqlalchemy-security.md +16 -0
- package/skills/torusguard/references/ssrf-and-outbound-requests.md +46 -0
- package/skills/torusguard/references/webhook-security.md +15 -0
- package/skills/torusguard/references/websocket-security.md +12 -0
- package/skills/torusguard-apply/SKILL.md +46 -0
- package/skills/torusguard-audit/SKILL.md +70 -0
- package/skills/torusguard-authorize/SKILL.md +47 -0
- package/skills/torusguard-exploit-check/SKILL.md +64 -0
- package/skills/torusguard-full/SKILL.md +70 -0
- package/skills/torusguard-harden/SKILL.md +63 -0
- package/skills/torusguard-init/SKILL.md +52 -0
- package/skills/torusguard-recheck/SKILL.md +53 -0
- package/skills/torusguard-report/SKILL.md +54 -0
- package/skills/torusguard-status/SKILL.md +42 -0
- package/skills/torusguard-verify/SKILL.md +55 -0
- package/skills/torusguard-web-validate/SKILL.md +64 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Cryptographic Manifest Builder & Verifier
|
|
4
|
+
Generates or validates SHA-256 integrity digests for all .torusguard files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import hashlib
|
|
11
|
+
import argparse
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def compute_file_sha256(filepath):
|
|
16
|
+
"""Compute SHA-256 digest of a file in binary mode."""
|
|
17
|
+
hasher = hashlib.sha256()
|
|
18
|
+
with open(filepath, "rb") as fh:
|
|
19
|
+
while chunk := fh.read(65536):
|
|
20
|
+
hasher.update(chunk)
|
|
21
|
+
return hasher.hexdigest()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def scan_workspace_files(base_dir):
|
|
25
|
+
"""Scan all files inside .torusguard, ignoring runs, pyc, and gitkeep."""
|
|
26
|
+
base_dir = Path(base_dir).resolve()
|
|
27
|
+
file_map = {}
|
|
28
|
+
|
|
29
|
+
for root, dirs, files in os.walk(base_dir):
|
|
30
|
+
if "runs" in dirs:
|
|
31
|
+
dirs.remove("runs")
|
|
32
|
+
if "__pycache__" in dirs:
|
|
33
|
+
dirs.remove("__pycache__")
|
|
34
|
+
|
|
35
|
+
for f in sorted(files):
|
|
36
|
+
if f in [".manifest.json", ".gitkeep"] or f.endswith(".pyc"):
|
|
37
|
+
continue
|
|
38
|
+
full_path = Path(root) / f
|
|
39
|
+
rel_path = full_path.relative_to(base_dir).as_posix()
|
|
40
|
+
file_map[rel_path] = compute_file_sha256(full_path)
|
|
41
|
+
|
|
42
|
+
return file_map
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def check_manifest(base_dir):
|
|
46
|
+
"""Check disk files against existing .manifest.json."""
|
|
47
|
+
base_dir = Path(base_dir).resolve()
|
|
48
|
+
manifest_file = base_dir / ".manifest.json"
|
|
49
|
+
|
|
50
|
+
if not manifest_file.exists():
|
|
51
|
+
print(f"[ERROR] Missing manifest file: {manifest_file}")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
with open(manifest_file, "r", encoding="utf-8") as fh:
|
|
55
|
+
expected = json.load(fh)
|
|
56
|
+
|
|
57
|
+
current = scan_workspace_files(base_dir)
|
|
58
|
+
|
|
59
|
+
# Files expected to change during user initialization/configuration
|
|
60
|
+
USER_CONFIGS = {"config/torusguard.json", "config/scope.json"}
|
|
61
|
+
|
|
62
|
+
missing = set(expected.keys()) - set(current.keys())
|
|
63
|
+
added = set(current.keys()) - set(expected.keys())
|
|
64
|
+
altered_core = [k for k in expected if k in current and expected[k] != current[k] and k not in USER_CONFIGS]
|
|
65
|
+
altered_configs = [k for k in expected if k in current and expected[k] != current[k] and k in USER_CONFIGS]
|
|
66
|
+
|
|
67
|
+
print("================================================================================")
|
|
68
|
+
print("TORUSGUARD INTEGRITY MANIFEST VERIFICATION")
|
|
69
|
+
print("================================================================================")
|
|
70
|
+
print(f"Directory: {base_dir}")
|
|
71
|
+
print(f"Indexed Files: {len(expected)}")
|
|
72
|
+
|
|
73
|
+
if missing:
|
|
74
|
+
print(f"\n[FAIL] Missing files ({len(missing)}):")
|
|
75
|
+
for m in sorted(missing):
|
|
76
|
+
print(f" - {m}")
|
|
77
|
+
|
|
78
|
+
if altered_core:
|
|
79
|
+
print(f"\n[FAIL] Altered / Tampered core files ({len(altered_core)}):")
|
|
80
|
+
for a in sorted(altered_core):
|
|
81
|
+
print(f" - {a}")
|
|
82
|
+
|
|
83
|
+
if altered_configs:
|
|
84
|
+
print(f"\n[INFO] Tailored user project configs ({len(altered_configs)}):")
|
|
85
|
+
for ac in sorted(altered_configs):
|
|
86
|
+
print(f" * {ac}")
|
|
87
|
+
|
|
88
|
+
if added:
|
|
89
|
+
print(f"\n[INFO] Untracked new files ({len(added)}):")
|
|
90
|
+
for ad in sorted(added):
|
|
91
|
+
print(f" + {ad}")
|
|
92
|
+
|
|
93
|
+
if not missing and not altered_core:
|
|
94
|
+
print("\n[PASS] 100% of workspace files match cryptographic SHA-256 signatures cleanly.")
|
|
95
|
+
print("================================================================================")
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
print("\n================================================================================")
|
|
99
|
+
print("INTEGRITY VERIFICATION FAILED")
|
|
100
|
+
print("================================================================================")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def write_manifest(base_dir):
|
|
105
|
+
"""Write freshly computed manifest to .torusguard/.manifest.json."""
|
|
106
|
+
base_dir = Path(base_dir).resolve()
|
|
107
|
+
manifest_file = base_dir / ".manifest.json"
|
|
108
|
+
|
|
109
|
+
current = scan_workspace_files(base_dir)
|
|
110
|
+
with open(manifest_file, "w", encoding="utf-8") as fh:
|
|
111
|
+
json.dump(current, fh, indent=2)
|
|
112
|
+
|
|
113
|
+
print(f"[SUCCESS] Wrote {len(current)} entries to {manifest_file}")
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
parser = argparse.ArgumentParser(description="TorusGuard Manifest Builder & Verifier")
|
|
119
|
+
parser.add_argument("--dir", default=".torusguard", help="Target .torusguard directory")
|
|
120
|
+
parser.add_argument("--write", action="store_true", help="Generate and write manifest")
|
|
121
|
+
parser.add_argument("--check", action="store_true", help="Check workspace against manifest")
|
|
122
|
+
args = parser.parse_args()
|
|
123
|
+
|
|
124
|
+
if args.write:
|
|
125
|
+
success = write_manifest(args.dir)
|
|
126
|
+
else:
|
|
127
|
+
# Default to --check
|
|
128
|
+
success = check_manifest(args.dir)
|
|
129
|
+
|
|
130
|
+
sys.exit(0 if success else 1)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Monorepo & Multi-Package Workspace Detector (v0.9.3)
|
|
4
|
+
Discovers and profiles sub-projects across Turborepo, pnpm, npm, and Python workspaces.
|
|
5
|
+
Pure Python 3.10+ standard library (zero external dependencies).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import argparse
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Dict, List, Any, Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def detect_package_info(abs_pkg_dir: Path, root: Path) -> Dict[str, Any]:
|
|
18
|
+
"""Inspects a single directory to detect language and framework."""
|
|
19
|
+
rel_path = str(abs_pkg_dir.relative_to(root)).replace('\\', '/')
|
|
20
|
+
name = abs_pkg_dir.name
|
|
21
|
+
language = "unknown"
|
|
22
|
+
framework = "generic"
|
|
23
|
+
|
|
24
|
+
pkg_dir = abs_pkg_dir
|
|
25
|
+
|
|
26
|
+
# Check typescript / javascript
|
|
27
|
+
package_json = pkg_dir / "package.json"
|
|
28
|
+
if package_json.is_file() or list(pkg_dir.glob("*.ts")) or list(pkg_dir.glob("*.js")):
|
|
29
|
+
language = "typescript"
|
|
30
|
+
if package_json.is_file():
|
|
31
|
+
try:
|
|
32
|
+
with open(package_json, "r", encoding="utf-8") as f:
|
|
33
|
+
pj = json.load(f)
|
|
34
|
+
name = pj.get("name", name)
|
|
35
|
+
deps = {**pj.get("dependencies", {}), **pj.get("devDependencies", {})}
|
|
36
|
+
if "next" in deps:
|
|
37
|
+
framework = "nextjs"
|
|
38
|
+
elif "express" in deps:
|
|
39
|
+
framework = "express"
|
|
40
|
+
elif "react" in deps:
|
|
41
|
+
framework = "react"
|
|
42
|
+
elif "vue" in deps or "nuxt" in deps:
|
|
43
|
+
framework = "vue"
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
elif list(pkg_dir.glob("*next*")) or "next" in pkg_dir.name:
|
|
47
|
+
framework = "nextjs"
|
|
48
|
+
|
|
49
|
+
# Check python
|
|
50
|
+
pyproject = pkg_dir / "pyproject.toml"
|
|
51
|
+
reqs = pkg_dir / "requirements.txt"
|
|
52
|
+
if pyproject.is_file() or reqs.is_file() or list(pkg_dir.glob("*.py")):
|
|
53
|
+
if language == "unknown":
|
|
54
|
+
language = "python"
|
|
55
|
+
content = ""
|
|
56
|
+
if pyproject.is_file():
|
|
57
|
+
content += pyproject.read_text(encoding="utf-8", errors="replace").lower()
|
|
58
|
+
if reqs.is_file():
|
|
59
|
+
content += reqs.read_text(encoding="utf-8", errors="replace").lower()
|
|
60
|
+
for pyf in pkg_dir.glob("*.py"):
|
|
61
|
+
content += pyf.read_text(encoding="utf-8", errors="replace").lower()
|
|
62
|
+
|
|
63
|
+
if "fastapi" in content or "fastapi" in pkg_dir.name:
|
|
64
|
+
framework = "fastapi"
|
|
65
|
+
elif "django" in content or "django" in pkg_dir.name:
|
|
66
|
+
framework = "django"
|
|
67
|
+
elif "flask" in content or "flask" in pkg_dir.name:
|
|
68
|
+
framework = "flask"
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
"name": name,
|
|
72
|
+
"path": rel_path,
|
|
73
|
+
"language": language,
|
|
74
|
+
"framework": framework
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def scan_workspace(root_dir: str = ".") -> Dict[str, Any]:
|
|
79
|
+
"""Scans root directory for monorepo configuration and sub-packages."""
|
|
80
|
+
root = Path(root_dir).resolve()
|
|
81
|
+
monorepo_type = "single-project"
|
|
82
|
+
sub_packages: List[Dict[str, Any]] = []
|
|
83
|
+
|
|
84
|
+
# 1. Turborepo
|
|
85
|
+
if (root / "turbo.json").is_file():
|
|
86
|
+
monorepo_type = "turborepo"
|
|
87
|
+
|
|
88
|
+
# 2. pnpm workspace
|
|
89
|
+
pnpm_ws = root / "pnpm-workspace.yaml"
|
|
90
|
+
if pnpm_ws.is_file():
|
|
91
|
+
monorepo_type = "pnpm-workspace"
|
|
92
|
+
|
|
93
|
+
# 3. npm / yarn workspaces
|
|
94
|
+
root_pj = root / "package.json"
|
|
95
|
+
if root_pj.is_file():
|
|
96
|
+
try:
|
|
97
|
+
with open(root_pj, "r", encoding="utf-8") as f:
|
|
98
|
+
data = json.load(f)
|
|
99
|
+
if "workspaces" in data:
|
|
100
|
+
monorepo_type = "npm/yarn-workspaces"
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# Scan common monorepo container directories and direct project subfolders
|
|
105
|
+
candidate_folders = ["apps", "packages", "services", "libs", "modules"]
|
|
106
|
+
found_dirs = []
|
|
107
|
+
|
|
108
|
+
for cf in candidate_folders:
|
|
109
|
+
container = root / cf
|
|
110
|
+
if container.is_dir():
|
|
111
|
+
for child in container.iterdir():
|
|
112
|
+
if child.is_dir() and not child.name.startswith(('.', '_')):
|
|
113
|
+
if any((child / m).exists() for m in ["package.json", "pyproject.toml", "requirements.txt", "setup.py", "Cargo.toml", "main.py"]):
|
|
114
|
+
found_dirs.append(child)
|
|
115
|
+
|
|
116
|
+
# Also scan direct subdirectories if not already captured
|
|
117
|
+
for child in root.iterdir():
|
|
118
|
+
if child.is_dir() and not child.name.startswith(('.', '_')) and child.name not in candidate_folders:
|
|
119
|
+
if any((child / m).exists() for m in ["package.json", "pyproject.toml", "requirements.txt", "setup.py", "Cargo.toml", "main.py", "actions.ts"]):
|
|
120
|
+
if child not in found_dirs:
|
|
121
|
+
found_dirs.append(child)
|
|
122
|
+
|
|
123
|
+
if found_dirs:
|
|
124
|
+
if monorepo_type == "single-project":
|
|
125
|
+
monorepo_type = "multi-package-directory"
|
|
126
|
+
for d in found_dirs:
|
|
127
|
+
sub_packages.append(detect_package_info(d, root))
|
|
128
|
+
|
|
129
|
+
# If no monorepo sub-packages found, treat root as primary
|
|
130
|
+
if not sub_packages:
|
|
131
|
+
sub_packages.append(detect_package_info(root, root))
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
"root": str(root).replace('\\', '/'),
|
|
135
|
+
"monorepo_type": monorepo_type,
|
|
136
|
+
"is_monorepo": monorepo_type != "single-project",
|
|
137
|
+
"package_count": len(sub_packages),
|
|
138
|
+
"packages": sub_packages
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def main():
|
|
143
|
+
parser = argparse.ArgumentParser(description="TorusGuard Monorepo & Multi-Package Detector")
|
|
144
|
+
parser.add_argument("--scan", default=".", help="Root directory to scan (default: .)")
|
|
145
|
+
parser.add_argument("--write", action="store_true", help="Write to .torusguard/config/workspaces.json")
|
|
146
|
+
parser.add_argument("--json", action="store_true", help="Output JSON to stdout")
|
|
147
|
+
args = parser.parse_args()
|
|
148
|
+
|
|
149
|
+
res = scan_workspace(args.scan)
|
|
150
|
+
|
|
151
|
+
if args.write:
|
|
152
|
+
config_dir = Path(args.scan) / ".torusguard" / "config"
|
|
153
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
out_file = config_dir / "workspaces.json"
|
|
155
|
+
with open(out_file, "w", encoding="utf-8") as f:
|
|
156
|
+
json.dump(res, f, indent=2)
|
|
157
|
+
print(f"[SUCCESS] Wrote workspace registry to {out_file}")
|
|
158
|
+
|
|
159
|
+
if args.json or not args.write:
|
|
160
|
+
print(json.dumps(res, indent=2))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
main()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Run Folder Lifecycle Manager
|
|
4
|
+
Creates isolated run folders, tracks manifest.json state, and enumerates execution history.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import datetime
|
|
10
|
+
import argparse
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Dict, Any, List
|
|
13
|
+
|
|
14
|
+
def create_run(base_dir: Path, command: str, target_name: str = "project") -> Path:
|
|
15
|
+
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")
|
|
16
|
+
run_id = f"run-{timestamp}-{command}"
|
|
17
|
+
run_folder = base_dir / run_id
|
|
18
|
+
run_folder.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
manifest = {
|
|
21
|
+
"run_id": run_id,
|
|
22
|
+
"command": command,
|
|
23
|
+
"target_name": target_name,
|
|
24
|
+
"created_at": datetime.datetime.utcnow().isoformat() + "Z",
|
|
25
|
+
"status": "in_progress",
|
|
26
|
+
"torusguard_version": "0.8.0",
|
|
27
|
+
"findings_count": 0,
|
|
28
|
+
"confirmed_fixed_count": 0,
|
|
29
|
+
"regressed_count": 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
manifest_path = run_folder / "manifest.json"
|
|
33
|
+
with open(manifest_path, "w", encoding="utf-8") as f:
|
|
34
|
+
json.dump(manifest, f, indent=2)
|
|
35
|
+
|
|
36
|
+
return run_folder
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def list_runs(base_dir: Path) -> List[Dict[str, Any]]:
|
|
40
|
+
runs = []
|
|
41
|
+
if not base_dir.is_dir():
|
|
42
|
+
return runs
|
|
43
|
+
|
|
44
|
+
for d in sorted(base_dir.iterdir(), reverse=True):
|
|
45
|
+
if d.is_dir() and d.name.startswith("run-"):
|
|
46
|
+
manifest_file = d / "manifest.json"
|
|
47
|
+
if manifest_file.is_file():
|
|
48
|
+
try:
|
|
49
|
+
with open(manifest_file, "r", encoding="utf-8") as f:
|
|
50
|
+
data = json.load(f)
|
|
51
|
+
runs.append(data)
|
|
52
|
+
except Exception:
|
|
53
|
+
runs.append({"run_id": d.name, "status": "corrupted"})
|
|
54
|
+
else:
|
|
55
|
+
runs.append({"run_id": d.name, "status": "unmanifested"})
|
|
56
|
+
return runs
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
parser = argparse.ArgumentParser(description="TorusGuard Run Manager")
|
|
61
|
+
subparsers = parser.add_subparsers(dest="subcommand")
|
|
62
|
+
|
|
63
|
+
create_p = subparsers.add_parser("create")
|
|
64
|
+
create_p.add_argument("--command", "-c", default="audit", help="TorusGuard command (audit, web-validate, harden, etc.)")
|
|
65
|
+
create_p.add_argument("--target", "-t", default="project", help="Target project name")
|
|
66
|
+
create_p.add_argument("--dir", "-d", default=".torusguard/runs", help="Base runs directory")
|
|
67
|
+
|
|
68
|
+
list_p = subparsers.add_parser("list")
|
|
69
|
+
list_p.add_argument("--dir", "-d", default=".torusguard/runs", help="Base runs directory")
|
|
70
|
+
list_p.add_argument("--json", action="store_true", help="Output JSON")
|
|
71
|
+
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
if args.subcommand == "create":
|
|
75
|
+
folder = create_run(Path(args.dir), args.command, args.target)
|
|
76
|
+
print(f"Created run folder: {folder}")
|
|
77
|
+
elif args.subcommand == "list":
|
|
78
|
+
runs = list_runs(Path(args.dir))
|
|
79
|
+
if args.json:
|
|
80
|
+
print(json.dumps(runs, indent=2))
|
|
81
|
+
else:
|
|
82
|
+
print(f"Total Runs Found: {len(runs)}")
|
|
83
|
+
for r in runs:
|
|
84
|
+
print(f" - {r.get('run_id')}: command={r.get('command')}, status={r.get('status')}")
|
|
85
|
+
else:
|
|
86
|
+
parser.print_help()
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
main()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard Runtime Safety Gate Evaluator
|
|
4
|
+
Evaluates prospective HTTP/browser validation probes against non-destruction policies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import argparse
|
|
10
|
+
from typing import Dict, Any, Tuple
|
|
11
|
+
|
|
12
|
+
def evaluate_safety_gate(method: str, path: str, body: str = "") -> Tuple[str, str]:
|
|
13
|
+
"""
|
|
14
|
+
Returns (decision, rationale)
|
|
15
|
+
Decisions:
|
|
16
|
+
- Auto-Allowed
|
|
17
|
+
- Approval Required
|
|
18
|
+
- Manual Only (Blocked)
|
|
19
|
+
"""
|
|
20
|
+
method_upper = method.upper()
|
|
21
|
+
|
|
22
|
+
# Block destructive verbs
|
|
23
|
+
if method_upper in ("DELETE", "DROP"):
|
|
24
|
+
return "Manual Only", f"Method '{method_upper}' is destructive and strictly blocked from automated runtime validation."
|
|
25
|
+
|
|
26
|
+
# Block sensitive administrative paths
|
|
27
|
+
destructive_keywords = ["/admin/delete", "/shutdown", "/drop-database", "/purge", "/reset-system"]
|
|
28
|
+
for kw in destructive_keywords:
|
|
29
|
+
if kw in path.lower():
|
|
30
|
+
return "Manual Only", f"Path '{path}' targets destructive administrative endpoint '{kw}'."
|
|
31
|
+
|
|
32
|
+
# Safe read methods
|
|
33
|
+
if method_upper in ("GET", "HEAD", "OPTIONS"):
|
|
34
|
+
return "Auto-Allowed", f"Read-only method '{method_upper}' with non-destructive path."
|
|
35
|
+
|
|
36
|
+
# State-changing methods (POST, PUT, PATCH)
|
|
37
|
+
if method_upper in ("POST", "PUT", "PATCH"):
|
|
38
|
+
# Check if safe canary marker exists
|
|
39
|
+
if "tg-canary" in body.lower() or "torusguard" in body.lower():
|
|
40
|
+
return "Approval Required", f"State-changing method '{method_upper}' contains benign TorusGuard test marker; requires user approval."
|
|
41
|
+
else:
|
|
42
|
+
return "Approval Required", f"State-changing method '{method_upper}' requires operator approval before execution."
|
|
43
|
+
|
|
44
|
+
return "Approval Required", f"Unknown probe profile for {method_upper} {path}."
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
parser = argparse.ArgumentParser(description="TorusGuard Safety Gate Evaluator")
|
|
49
|
+
parser.add_argument("--method", "-m", default="GET", help="HTTP Method")
|
|
50
|
+
parser.add_argument("--path", "-p", default="/api/test", help="Endpoint path")
|
|
51
|
+
parser.add_argument("--body", "-b", default="", help="Request payload body")
|
|
52
|
+
parser.add_argument("--json", action="store_true", help="Output JSON")
|
|
53
|
+
args = parser.parse_args()
|
|
54
|
+
|
|
55
|
+
decision, rationale = evaluate_safety_gate(args.method, args.path, args.body)
|
|
56
|
+
|
|
57
|
+
if args.json:
|
|
58
|
+
print(json.dumps({"decision": decision, "rationale": rationale}, indent=2))
|
|
59
|
+
else:
|
|
60
|
+
print(f"Safety Decision: {decision}")
|
|
61
|
+
print(f"Rationale: {rationale}")
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TorusGuard OASIS SARIF v2.1.0 Exporter
|
|
4
|
+
Converts normalized TorusGuard findings into standard SARIF v2.1.0 format with
|
|
5
|
+
GitHub Code Scanning multi-analysis category separation and partialFingerprints deduplication.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import hashlib
|
|
11
|
+
import argparse
|
|
12
|
+
from typing import List, Dict, Any
|
|
13
|
+
|
|
14
|
+
def generate_sarif(findings: List[Dict[str, Any]], run_id: str = "default", category: str = "torusguard/static") -> Dict[str, Any]:
|
|
15
|
+
rules_map: Dict[str, Dict[str, Any]] = {}
|
|
16
|
+
results: List[Dict[str, Any]] = []
|
|
17
|
+
|
|
18
|
+
for f in findings:
|
|
19
|
+
rule_id = f.get("rule_id", "TG-GEN-001")
|
|
20
|
+
if rule_id not in rules_map:
|
|
21
|
+
rules_map[rule_id] = {
|
|
22
|
+
"id": rule_id,
|
|
23
|
+
"name": f.get("title", rule_id),
|
|
24
|
+
"shortDescription": {"text": f.get("title", rule_id)},
|
|
25
|
+
"fullDescription": {"text": f.get("description", f.get("title", rule_id))},
|
|
26
|
+
"defaultConfiguration": {
|
|
27
|
+
"level": "error" if f.get("severity") in ("Critical", "High") else "warning"
|
|
28
|
+
},
|
|
29
|
+
"properties": {
|
|
30
|
+
"tags": ["security", "torusguard", f.get("category", "general")]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
target = f.get("target", {})
|
|
35
|
+
file_path = target.get("file_path", "unknown")
|
|
36
|
+
line = target.get("start_line", 1)
|
|
37
|
+
|
|
38
|
+
# Generate deduplication fingerprint
|
|
39
|
+
seed = f"{rule_id}:{file_path}:{line}"
|
|
40
|
+
line_hash = hashlib.sha256(seed.encode("utf-8")).hexdigest()[:16]
|
|
41
|
+
|
|
42
|
+
result: Dict[str, Any] = {
|
|
43
|
+
"ruleId": rule_id,
|
|
44
|
+
"message": {"text": f.get("title", "TorusGuard Security Finding")},
|
|
45
|
+
"level": "error" if f.get("severity") in ("Critical", "High") else "warning",
|
|
46
|
+
"locations": [
|
|
47
|
+
{
|
|
48
|
+
"physicalLocation": {
|
|
49
|
+
"artifactLocation": {
|
|
50
|
+
"uri": file_path,
|
|
51
|
+
"uriBaseId": "%SRCROOT%"
|
|
52
|
+
},
|
|
53
|
+
"region": {
|
|
54
|
+
"startLine": line,
|
|
55
|
+
"startColumn": 1
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"partialFingerprints": {
|
|
61
|
+
"primaryLocationLineHash": line_hash
|
|
62
|
+
},
|
|
63
|
+
"properties": {
|
|
64
|
+
"confidence": f.get("confidence", 70),
|
|
65
|
+
"status": f.get("status", "Confirmed"),
|
|
66
|
+
"finding_id": f.get("finding_id", "TG-FIND-UNKNOWN")
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
results.append(result)
|
|
70
|
+
|
|
71
|
+
sarif = {
|
|
72
|
+
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
73
|
+
"version": "2.1.0",
|
|
74
|
+
"runs": [
|
|
75
|
+
{
|
|
76
|
+
"tool": {
|
|
77
|
+
"driver": {
|
|
78
|
+
"name": "TorusGuard",
|
|
79
|
+
"semanticVersion": "0.8.0",
|
|
80
|
+
"informationUri": "https://github.com/githubmofo/TorusGuard",
|
|
81
|
+
"rules": list(rules_map.values())
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"automationDetails": {
|
|
85
|
+
"id": f"{category}/{run_id}"
|
|
86
|
+
},
|
|
87
|
+
"results": results
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
return sarif
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main():
|
|
95
|
+
parser = argparse.ArgumentParser(description="TorusGuard SARIF v2.1.0 Exporter")
|
|
96
|
+
parser.add_argument("--input", "-i", help="Path to findings JSON file")
|
|
97
|
+
parser.add_argument("--output", "-o", help="Output path for sarif.json")
|
|
98
|
+
parser.add_argument("--run-id", default="run-default", help="Unique execution Run ID")
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
|
|
101
|
+
findings = []
|
|
102
|
+
if args.input:
|
|
103
|
+
with open(args.input, "r", encoding="utf-8") as f:
|
|
104
|
+
findings = json.load(f)
|
|
105
|
+
|
|
106
|
+
sarif = generate_sarif(findings, run_id=args.run_id)
|
|
107
|
+
|
|
108
|
+
if args.output:
|
|
109
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
110
|
+
json.dump(sarif, f, indent=2)
|
|
111
|
+
print(f"SARIF log written to: {args.output}")
|
|
112
|
+
else:
|
|
113
|
+
print(json.dumps(sarif, indent=2))
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
main()
|