Claude 24f816b6a3
Consolidate 22 sibling repos into layered organism structure
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):

- brain/        LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
                dispatch, A51 channels, and the OSINT cluster
- knowledge/    LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
                MITRE ATT&CK data
- reference/    defensive threat-reference (C3, shhbruh doc) + AdaYaml parser

License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.

Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.

https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
2026-06-10 06:53:01 +00:00

184 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""Agent for securing container registry with Harbor.
Audits Harbor registry security configuration including RBAC,
vulnerability scanning policies, content trust, immutable tags,
and OIDC authentication via Harbor REST API v2.0.
"""
import json
import sys
from pathlib import Path
from datetime import datetime
try:
import requests
except ImportError:
requests = None
class HarborSecurityAgent:
"""Audits and hardens Harbor container registry security."""
def __init__(self, harbor_url, username="admin", password="",
output_dir="./harbor_audit"):
self.base_url = harbor_url.rstrip("/") + "/api/v2.0"
self.auth = (username, password)
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.findings = []
def _get(self, path, params=None):
if not requests:
return {"error": "requests library required"}
resp = requests.get(f"{self.base_url}{path}", auth=self.auth,
params=params, verify=True, timeout=15)
try:
return resp.json()
except (json.JSONDecodeError, ValueError):
return {"status": resp.status_code}
def audit_projects(self):
"""Audit all projects for security configuration."""
projects = self._get("/projects", {"page_size": 100})
if isinstance(projects, dict) and "error" in projects:
return projects
results = []
for p in projects:
meta = p.get("metadata", {})
name = p.get("name", "")
issues = []
if meta.get("public") == "true":
issues.append("Project is public - images accessible without auth")
if meta.get("auto_scan") != "true":
issues.append("Auto-scan on push not enabled")
if meta.get("prevent_vul") != "true":
issues.append("Vulnerability prevention not enabled")
if meta.get("enable_content_trust") != "true":
issues.append("Content trust (Notary) not enabled")
if meta.get("enable_content_trust_cosign") != "true":
issues.append("Cosign content trust not enabled")
for issue in issues:
self.findings.append({
"severity": "high" if "public" in issue or "prevent_vul" in issue else "medium",
"project": name,
"issue": issue,
})
results.append({
"name": name,
"public": meta.get("public"),
"auto_scan": meta.get("auto_scan"),
"prevent_vul": meta.get("prevent_vul"),
"content_trust": meta.get("enable_content_trust"),
"cosign": meta.get("enable_content_trust_cosign"),
"issues": issues,
})
return results
def audit_system_config(self):
"""Check system-level security configuration."""
config = self._get("/configurations")
if isinstance(config, dict) and "error" in config:
return config
checks = []
auth_mode = config.get("auth_mode", {}).get("value", "db_auth")
if auth_mode == "db_auth":
self.findings.append({
"severity": "medium",
"issue": "Using local database auth instead of OIDC/LDAP",
})
checks.append({"check": "auth_mode", "value": auth_mode, "status": "WARN"})
else:
checks.append({"check": "auth_mode", "value": auth_mode, "status": "OK"})
self_reg = config.get("self_registration", {}).get("value", True)
if self_reg:
self.findings.append({
"severity": "high",
"issue": "Self-registration enabled - anyone can create accounts",
})
checks.append({"check": "self_registration", "value": self_reg,
"status": "FAIL" if self_reg else "OK"})
return checks
def list_project_members(self, project_name):
"""List members and their roles for a project."""
members = self._get(f"/projects/{project_name}/members")
if isinstance(members, dict) and "error" in members:
return members
role_map = {1: "ProjectAdmin", 2: "Maintainer", 3: "Developer",
4: "Guest", 5: "LimitedGuest"}
return [
{"username": m.get("entity_name", ""), "role": role_map.get(m.get("role_id"), "Unknown")}
for m in (members if isinstance(members, list) else [])
]
def check_immutable_tags(self, project_name):
"""Check immutable tag rules for a project."""
rules = self._get(f"/projects/{project_name}/immutabletagrules")
if not rules or (isinstance(rules, dict) and "error" in rules):
self.findings.append({
"severity": "medium",
"project": project_name,
"issue": "No immutable tag rules configured",
})
return []
return rules
def audit_logs(self, page_size=20):
"""Retrieve recent audit log entries."""
logs = self._get("/audit-logs", {"page_size": page_size})
if isinstance(logs, dict) and "error" in logs:
return logs
return [
{"operation": l.get("operation"), "resource": l.get("resource"),
"username": l.get("username"), "time": l.get("op_time")}
for l in (logs if isinstance(logs, list) else [])
]
def generate_report(self):
projects = self.audit_projects()
sys_config = self.audit_system_config()
report = {
"report_date": datetime.utcnow().isoformat(),
"harbor_url": self.base_url.replace("/api/v2.0", ""),
"projects_audited": len(projects) if isinstance(projects, list) else 0,
"system_config": sys_config,
"projects": projects,
"findings": self.findings,
"finding_count": len(self.findings),
}
out = self.output_dir / "harbor_audit_report.json"
with open(out, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <harbor_url> [--user admin] [--pass Harbor12345]")
sys.exit(1)
url = sys.argv[1]
user = "admin"
password = "Harbor12345"
if "--user" in sys.argv:
user = sys.argv[sys.argv.index("--user") + 1]
if "--pass" in sys.argv:
password = sys.argv[sys.argv.index("--pass") + 1]
agent = HarborSecurityAgent(url, user, password)
agent.generate_report()
if __name__ == "__main__":
main()