Files
Anthropic-Cybersecurity-Skills/skills/securing-container-registry-with-harbor/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01: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()