mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-19 22:19:39 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
# API Reference: Distroless Container Image Analysis Agent
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| trivy CLI | >=0.50 | Container vulnerability scanning (subprocess) |
|
||||
| docker CLI | >=24.0 | Image inspection and property checks (subprocess) |
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
python scripts/agent.py \
|
||||
--images gcr.io/distroless/static-debian12 python:3.12-slim \
|
||||
--compare python:3.12 gcr.io/distroless/python3-debian12 \
|
||||
--output-dir /reports/
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### `run_trivy_scan(image) -> dict`
|
||||
Runs `trivy image --format json --severity CRITICAL,HIGH,MEDIUM`.
|
||||
|
||||
### `get_image_size(image) -> int`
|
||||
Runs `docker inspect --format {{.Size}}` for byte count.
|
||||
|
||||
### `count_vulns_by_severity(scan_data) -> dict`
|
||||
Parses Trivy JSON Results for CRITICAL/HIGH/MEDIUM/LOW counts.
|
||||
|
||||
### `compare_images(base_image, distroless_image) -> dict`
|
||||
Scans both images, computes size and vulnerability reduction percentages.
|
||||
|
||||
### `check_distroless_properties(image) -> dict`
|
||||
Tests for shell access and package manager presence via `docker run`.
|
||||
|
||||
### `generate_report(images, distroless_pairs) -> dict`
|
||||
Full analysis with individual scans, comparisons, and summary.
|
||||
|
||||
## Distroless Properties Checked
|
||||
|
||||
| Property | Check Method |
|
||||
|----------|-------------|
|
||||
| Shell access | `docker run --entrypoint "" image sh -c "echo"` |
|
||||
| Package manager | `docker run --entrypoint "" image which apt/apk/yum` |
|
||||
|
||||
## Output Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": {"images_scanned": 4, "minimal_images": 2},
|
||||
"comparisons": [{"size_reduction_pct": 82.3, "vuln_reduction_pct": 95.0}],
|
||||
"image_scans": [{"image": "gcr.io/distroless/static", "is_minimal": true}]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Distroless container image analysis agent using Trivy for comparing image security posture."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import Dict, List
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_trivy_scan(image: str) -> dict:
|
||||
"""Scan image with Trivy and return JSON results."""
|
||||
cmd = ["trivy", "image", "--format", "json", "--severity", "CRITICAL,HIGH,MEDIUM", image]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
if result.stdout:
|
||||
return json.loads(result.stdout)
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError) as exc:
|
||||
logger.error("Trivy scan failed for %s: %s", image, exc)
|
||||
return {}
|
||||
|
||||
|
||||
def get_image_size(image: str) -> int:
|
||||
"""Get image size using docker inspect."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["docker", "inspect", "--format", "{{.Size}}", image],
|
||||
capture_output=True, text=True, timeout=30)
|
||||
return int(result.stdout.strip()) if result.stdout.strip() else 0
|
||||
except (FileNotFoundError, ValueError):
|
||||
return 0
|
||||
|
||||
|
||||
def count_packages(scan_data: dict) -> int:
|
||||
"""Count total packages found in Trivy scan."""
|
||||
count = 0
|
||||
for result in scan_data.get("Results", []):
|
||||
count += len(result.get("Vulnerabilities", []))
|
||||
return count
|
||||
|
||||
|
||||
def count_vulns_by_severity(scan_data: dict) -> dict:
|
||||
"""Count vulnerabilities by severity from Trivy results."""
|
||||
counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0}
|
||||
for result in scan_data.get("Results", []):
|
||||
for vuln in result.get("Vulnerabilities", []):
|
||||
sev = vuln.get("Severity", "").upper()
|
||||
if sev in counts:
|
||||
counts[sev] += 1
|
||||
counts["total"] = sum(counts.values())
|
||||
return counts
|
||||
|
||||
|
||||
def compare_images(base_image: str, distroless_image: str) -> dict:
|
||||
"""Compare a standard base image against its distroless equivalent."""
|
||||
base_scan = run_trivy_scan(base_image)
|
||||
distroless_scan = run_trivy_scan(distroless_image)
|
||||
base_vulns = count_vulns_by_severity(base_scan)
|
||||
distroless_vulns = count_vulns_by_severity(distroless_scan)
|
||||
base_size = get_image_size(base_image)
|
||||
distroless_size = get_image_size(distroless_image)
|
||||
size_reduction = ((base_size - distroless_size) / base_size * 100) if base_size else 0
|
||||
vuln_reduction = ((base_vulns["total"] - distroless_vulns["total"]) / base_vulns["total"] * 100) if base_vulns["total"] else 0
|
||||
return {
|
||||
"base_image": {"image": base_image, "size_bytes": base_size, "vulnerabilities": base_vulns},
|
||||
"distroless_image": {"image": distroless_image, "size_bytes": distroless_size, "vulnerabilities": distroless_vulns},
|
||||
"size_reduction_pct": round(size_reduction, 1),
|
||||
"vuln_reduction_pct": round(vuln_reduction, 1),
|
||||
}
|
||||
|
||||
|
||||
def check_distroless_properties(image: str) -> dict:
|
||||
"""Check if an image exhibits distroless properties (no shell, no package manager)."""
|
||||
checks = {"has_shell": False, "has_package_manager": False, "has_user": False}
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["docker", "run", "--rm", "--entrypoint", "", image, "sh", "-c", "echo shell_exists"],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
checks["has_shell"] = "shell_exists" in result.stdout
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
try:
|
||||
for pm in ["apt", "apk", "yum", "dnf"]:
|
||||
result = subprocess.run(
|
||||
["docker", "run", "--rm", "--entrypoint", "", image, "which", pm],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
if result.returncode == 0:
|
||||
checks["has_package_manager"] = True
|
||||
break
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
return checks
|
||||
|
||||
|
||||
def generate_report(images: List[str], distroless_pairs: Dict[str, str] = None) -> dict:
|
||||
"""Generate distroless adoption report."""
|
||||
report = {"analysis_date": datetime.utcnow().isoformat(), "image_scans": [], "comparisons": []}
|
||||
for image in images:
|
||||
scan = run_trivy_scan(image)
|
||||
vulns = count_vulns_by_severity(scan)
|
||||
props = check_distroless_properties(image)
|
||||
report["image_scans"].append({
|
||||
"image": image, "vulnerabilities": vulns, "properties": props,
|
||||
"is_minimal": not props["has_shell"] and not props["has_package_manager"],
|
||||
})
|
||||
if distroless_pairs:
|
||||
for base, distroless in distroless_pairs.items():
|
||||
report["comparisons"].append(compare_images(base, distroless))
|
||||
report["summary"] = {
|
||||
"images_scanned": len(images),
|
||||
"minimal_images": sum(1 for s in report["image_scans"] if s["is_minimal"]),
|
||||
}
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Distroless Container Image Analysis Agent")
|
||||
parser.add_argument("--images", nargs="+", required=True, help="Images to analyze")
|
||||
parser.add_argument("--compare", nargs=2, action="append", metavar=("BASE", "DISTROLESS"),
|
||||
help="Compare base vs distroless pairs")
|
||||
parser.add_argument("--output-dir", default=".")
|
||||
parser.add_argument("--output", default="distroless_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
pairs = {c[0]: c[1] for c in args.compare} if args.compare else None
|
||||
report = generate_report(args.images, pairs)
|
||||
out_path = os.path.join(args.output_dir, args.output)
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved to %s", out_path)
|
||||
print(json.dumps(report["summary"], indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user