mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-02 09:07:41 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# API Reference: Securing Helm Chart Deployments
|
||||
|
||||
## Helm Security Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `helm lint ./chart --strict` | Lint chart with strict mode |
|
||||
| `helm template release ./chart` | Render templates locally |
|
||||
| `helm verify chart.tgz` | Verify chart signature |
|
||||
| `helm package ./chart --sign --key <key>` | Package and sign |
|
||||
| `helm pull repo/chart --verify` | Pull with verification |
|
||||
|
||||
## Security Context Fields
|
||||
|
||||
| Field | Recommended | Description |
|
||||
|-------|------------|-------------|
|
||||
| `runAsNonRoot` | true | Prevent root execution |
|
||||
| `readOnlyRootFilesystem` | true | Immutable filesystem |
|
||||
| `allowPrivilegeEscalation` | false | Block privilege escalation |
|
||||
| `capabilities.drop` | [ALL] | Drop all Linux capabilities |
|
||||
| `seccompProfile.type` | RuntimeDefault | Syscall filtering |
|
||||
|
||||
## Security Checks
|
||||
|
||||
| Check | Severity | Risk |
|
||||
|-------|----------|------|
|
||||
| Privileged container | High | Full host access |
|
||||
| hostNetwork enabled | High | Network namespace escape |
|
||||
| hostPID enabled | High | Process namespace escape |
|
||||
| :latest image tag | Medium | Non-reproducible builds |
|
||||
| Missing resource limits | Medium | Resource exhaustion DoS |
|
||||
| Missing readOnlyRootFilesystem | Medium | Writable filesystem |
|
||||
|
||||
## Template Scanning Tools
|
||||
|
||||
| Tool | Command |
|
||||
|------|---------|
|
||||
| kubesec | `kubesec scan rendered.yaml` |
|
||||
| checkov | `checkov -f rendered.yaml --framework kubernetes` |
|
||||
| trivy | `trivy config rendered.yaml` |
|
||||
| kube-linter | `kube-linter lint rendered.yaml` |
|
||||
|
||||
## Python Libraries
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `subprocess` | stdlib | Execute helm/kubesec CLI |
|
||||
| `re` | stdlib | Pattern matching in rendered YAML |
|
||||
| `yaml` | PyYAML >=6.0 | Parse YAML content |
|
||||
| `json` | stdlib | Report generation |
|
||||
|
||||
## References
|
||||
|
||||
- Helm Security: https://helm.sh/docs/topics/provenance/
|
||||
- Helm Secrets Plugin: https://github.com/jkroepke/helm-secrets
|
||||
- Kubesec: https://kubesec.io/
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for securing Helm chart deployments.
|
||||
|
||||
Validates chart provenance, renders and scans templates for
|
||||
security misconfigurations, checks security contexts, and
|
||||
enforces Helm deployment security baselines.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
|
||||
class HelmSecurityAgent:
|
||||
"""Audits Helm chart deployments for security issues."""
|
||||
|
||||
def __init__(self, chart_path, output_dir="./helm_audit"):
|
||||
self.chart_path = Path(chart_path)
|
||||
self.output_dir = Path(output_dir)
|
||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.findings = []
|
||||
|
||||
def _run(self, cmd, timeout=60):
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||
return result.stdout, result.stderr, result.returncode
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
|
||||
return "", str(e), -1
|
||||
|
||||
def lint_chart(self, values_file=None):
|
||||
"""Run helm lint with strict mode."""
|
||||
cmd = ["helm", "lint", str(self.chart_path), "--strict"]
|
||||
if values_file:
|
||||
cmd.extend(["--values", values_file])
|
||||
stdout, stderr, rc = self._run(cmd)
|
||||
if rc != 0:
|
||||
self.findings.append({
|
||||
"severity": "medium",
|
||||
"type": "Lint Failure",
|
||||
"detail": stderr.strip() or stdout.strip(),
|
||||
})
|
||||
return {"returncode": rc, "output": stdout.strip(), "errors": stderr.strip()}
|
||||
|
||||
def render_templates(self, values_file=None, release_name="audit"):
|
||||
"""Render Helm templates without deploying."""
|
||||
cmd = ["helm", "template", release_name, str(self.chart_path)]
|
||||
if values_file:
|
||||
cmd.extend(["--values", values_file])
|
||||
stdout, stderr, rc = self._run(cmd)
|
||||
if rc == 0:
|
||||
rendered_path = self.output_dir / "rendered.yaml"
|
||||
rendered_path.write_text(stdout)
|
||||
return str(rendered_path)
|
||||
return None
|
||||
|
||||
def check_security_contexts(self, rendered_path):
|
||||
"""Check rendered manifests for security context issues."""
|
||||
issues = []
|
||||
content = Path(rendered_path).read_text()
|
||||
|
||||
checks = [
|
||||
(r"privileged:\s*true", "high", "Privileged container detected"),
|
||||
(r"hostNetwork:\s*true", "high", "Host network access enabled"),
|
||||
(r"hostPID:\s*true", "high", "Host PID namespace shared"),
|
||||
(r"allowPrivilegeEscalation:\s*true", "medium", "Privilege escalation allowed"),
|
||||
(r"runAsUser:\s*0\b", "medium", "Running as root (UID 0)"),
|
||||
]
|
||||
|
||||
positive_checks = [
|
||||
(r"readOnlyRootFilesystem:\s*true", "readOnlyRootFilesystem"),
|
||||
(r"runAsNonRoot:\s*true", "runAsNonRoot"),
|
||||
(r"drop:\s*\n\s*-\s*ALL", "drop ALL capabilities"),
|
||||
]
|
||||
|
||||
for pattern, severity, msg in checks:
|
||||
if re.search(pattern, content):
|
||||
issues.append({"severity": severity, "issue": msg})
|
||||
self.findings.append({"severity": severity, "type": "Security Context", "detail": msg})
|
||||
|
||||
for pattern, name in positive_checks:
|
||||
if not re.search(pattern, content):
|
||||
issues.append({"severity": "medium", "issue": f"Missing: {name}"})
|
||||
self.findings.append({"severity": "medium", "type": "Missing Hardening", "detail": f"Missing: {name}"})
|
||||
|
||||
if "resources:" not in content:
|
||||
issues.append({"severity": "medium", "issue": "No resource limits defined"})
|
||||
self.findings.append({"severity": "medium", "type": "Missing Resources", "detail": "No CPU/memory limits"})
|
||||
|
||||
return issues
|
||||
|
||||
def verify_chart_signature(self, keyring=None):
|
||||
"""Verify Helm chart provenance signature."""
|
||||
tgz = list(self.chart_path.parent.glob(f"{self.chart_path.name}-*.tgz"))
|
||||
if not tgz:
|
||||
return {"verified": False, "reason": "No packaged chart found"}
|
||||
cmd = ["helm", "verify", str(tgz[0])]
|
||||
if keyring:
|
||||
cmd.extend(["--keyring", keyring])
|
||||
stdout, stderr, rc = self._run(cmd)
|
||||
if rc != 0:
|
||||
self.findings.append({"severity": "medium", "type": "Unsigned Chart", "detail": "Chart signature not verified"})
|
||||
return {"verified": rc == 0, "output": stdout.strip() or stderr.strip()}
|
||||
|
||||
def check_image_references(self, rendered_path):
|
||||
"""Check if image references use digests or tags."""
|
||||
content = Path(rendered_path).read_text()
|
||||
issues = []
|
||||
for m in re.finditer(r'image:\s*["\']?([^"\'\s]+)["\']?', content):
|
||||
image = m.group(1)
|
||||
if ":latest" in image:
|
||||
issues.append({"image": image, "issue": "Uses :latest tag"})
|
||||
self.findings.append({"severity": "medium", "type": "Image Tag", "detail": f"latest tag: {image}"})
|
||||
elif "@sha256:" not in image and ":" not in image:
|
||||
issues.append({"image": image, "issue": "No tag or digest specified"})
|
||||
return issues
|
||||
|
||||
def scan_with_kubesec(self, rendered_path):
|
||||
"""Scan rendered templates with kubesec if available."""
|
||||
stdout, stderr, rc = self._run(["kubesec", "scan", rendered_path])
|
||||
if rc < 0:
|
||||
return {"available": False}
|
||||
try:
|
||||
return json.loads(stdout)
|
||||
except json.JSONDecodeError:
|
||||
return {"error": "Parse failed"}
|
||||
|
||||
def generate_report(self, values_file=None):
|
||||
lint = self.lint_chart(values_file)
|
||||
rendered = self.render_templates(values_file)
|
||||
sec_ctx = []
|
||||
images = []
|
||||
if rendered:
|
||||
sec_ctx = self.check_security_contexts(rendered)
|
||||
images = self.check_image_references(rendered)
|
||||
|
||||
report = {
|
||||
"report_date": datetime.utcnow().isoformat(),
|
||||
"chart": str(self.chart_path),
|
||||
"lint_result": lint,
|
||||
"security_context_issues": sec_ctx,
|
||||
"image_issues": images,
|
||||
"findings": self.findings,
|
||||
"total_findings": len(self.findings),
|
||||
}
|
||||
out = self.output_dir / "helm_security_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 <chart_path> [--values values.yaml]")
|
||||
sys.exit(1)
|
||||
chart = sys.argv[1]
|
||||
values = None
|
||||
if "--values" in sys.argv:
|
||||
values = sys.argv[sys.argv.index("--values") + 1]
|
||||
agent = HelmSecurityAgent(chart)
|
||||
agent.generate_report(values)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user