96 lines
2.6 KiB
Python
96 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Audit the fleet shared palace for privacy violations.
|
|
Ensures no raw drawers, full source paths, or private workspace leaks exist.
|
|
|
|
Usage:
|
|
python audit_mempalace_privacy.py /path/to/fleet/palace
|
|
|
|
Exit codes:
|
|
0 = clean
|
|
1 = violations found
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import chromadb
|
|
except ImportError:
|
|
print("ERROR: chromadb not installed")
|
|
sys.exit(1)
|
|
|
|
VIOLATION_KEYWORDS = [
|
|
"/root/wizards/",
|
|
"/home/",
|
|
"/Users/",
|
|
"private_key",
|
|
"-----BEGIN",
|
|
"GITEA_TOKEN=",
|
|
"OPENAI_API_KEY",
|
|
"ANTHROPIC_API_KEY",
|
|
]
|
|
|
|
|
|
def audit(palace_path: Path):
|
|
violations = []
|
|
client = chromadb.PersistentClient(path=str(palace_path))
|
|
try:
|
|
col = client.get_collection("mempalace_drawers")
|
|
except Exception as e:
|
|
print(f"ERROR: Could not open collection: {e}")
|
|
sys.exit(1)
|
|
|
|
all_data = col.get(include=["documents", "metadatas"])
|
|
docs = all_data["documents"]
|
|
metas = all_data["metadatas"]
|
|
|
|
for doc, meta in zip(docs, metas):
|
|
source = meta.get("source_file", "")
|
|
doc_type = meta.get("type", "")
|
|
|
|
# Rule 1: Fleet palace should only contain closets or explicitly typed entries
|
|
if doc_type not in ("closet", "summary", "fleet"):
|
|
violations.append(
|
|
f"VIOLATION: Document type is '{doc_type}' (expected closet/summary/fleet). "
|
|
f"Source: {source}"
|
|
)
|
|
|
|
# Rule 2: No full absolute paths from private workspaces
|
|
if any(abs_path in source for abs_path in ["/root/wizards/", "/home/", "/Users/"]):
|
|
violations.append(
|
|
f"VIOLATION: Source contains absolute path: {source}"
|
|
)
|
|
|
|
# Rule 3: No raw secrets in document text
|
|
for kw in VIOLATION_KEYWORDS:
|
|
if kw in doc:
|
|
violations.append(
|
|
f"VIOLATION: Document contains sensitive keyword '{kw}'. Source: {source}"
|
|
)
|
|
break # one violation per doc is enough
|
|
|
|
return violations
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description="Audit fleet palace privacy")
|
|
parser.add_argument("palace", default="/var/lib/mempalace/fleet", nargs="?", help="Path to fleet palace")
|
|
args = parser.parse_args()
|
|
|
|
violations = audit(Path(args.palace))
|
|
|
|
if violations:
|
|
print(f"FAIL: {len(violations)} privacy violation(s) found")
|
|
for v in violations:
|
|
print(f" {v}")
|
|
sys.exit(1)
|
|
else:
|
|
print("PASS: No privacy violations detected")
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|