mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-09-11 19:00:50 +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.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Privileged Account Access Review — API Reference
|
||||
|
||||
## CSV Input Format
|
||||
|
||||
The agent consumes a CSV file with these columns:
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `username` | string | Account identifier (SAMAccountName or UPN) |
|
||||
| `owner` | string | Assigned account owner / manager |
|
||||
| `roles` | string | Semicolon-separated privilege roles |
|
||||
| `last_used` | string | ISO date `YYYY-MM-DD` of last interactive logon |
|
||||
| `last_certified` | string | ISO date `YYYY-MM-DD` of most recent access review |
|
||||
| `account_type` | string | `human`, `service`, or `shared` |
|
||||
|
||||
## Checks Performed
|
||||
|
||||
### Stale Account Detection
|
||||
Flags accounts whose `last_used` date exceeds a configurable threshold (default 90 days). Accounts without a `last_used` value are automatically flagged as high severity.
|
||||
|
||||
### Shared Account Detection
|
||||
Matches `username` against common shared-account patterns: `admin`, `root`, `service`, `svc_`, `shared`, `generic`, `temp`. Flags accounts matching these patterns that lack an assigned `owner`.
|
||||
|
||||
### Excessive Privilege Detection
|
||||
Compares the `roles` field against high-risk role names: Domain Admin, Enterprise Admin, Schema Admin, Global Admin, Super Admin, Root. Any match triggers a critical finding.
|
||||
|
||||
### Recertification Compliance
|
||||
Compares `last_certified` against a configurable interval (default 180 days). Accounts never certified are flagged as critical.
|
||||
|
||||
## Output Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"report": "privileged_account_access_review",
|
||||
"generated_at": "ISO-8601 timestamp",
|
||||
"total_accounts": 150,
|
||||
"total_findings": 12,
|
||||
"severity_summary": {"critical": 3, "high": 7, "medium": 2},
|
||||
"findings": [
|
||||
{
|
||||
"account": "svc_backup",
|
||||
"issue": "shared_account_no_owner",
|
||||
"severity": "critical",
|
||||
"detail": "Appears shared (matches 'svc_') with no assigned owner"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Compliance Frameworks
|
||||
|
||||
- **NIST SP 800-53 AC-2**: Account Management — periodic review of privileged accounts
|
||||
- **CIS Controls v8 5.3**: Disable dormant accounts after 45 days of inactivity
|
||||
- **PCI DSS 8.1.4**: Remove/disable inactive user accounts within 90 days
|
||||
- **SOX Section 404**: Internal controls over financial reporting require access reviews
|
||||
- **ISO 27001 A.9.2.5**: Review of user access rights at planned intervals
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
python agent.py --input accounts.csv --stale-days 90 --cert-days 180 --output report.json
|
||||
```
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Privileged Account Access Review agent — audits privileged accounts for
|
||||
compliance with least-privilege and periodic recertification requirements."""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_accounts(csv_path: str) -> list[dict]:
|
||||
"""Load privileged account inventory from CSV."""
|
||||
with open(csv_path, newline="", encoding="utf-8") as fh:
|
||||
reader = csv.DictReader(fh)
|
||||
return list(reader)
|
||||
|
||||
|
||||
def check_stale_accounts(accounts: list[dict], max_days: int = 90) -> list[dict]:
|
||||
"""Flag accounts not used within max_days."""
|
||||
findings = []
|
||||
cutoff = datetime.utcnow() - timedelta(days=max_days)
|
||||
for acct in accounts:
|
||||
last_used = acct.get("last_used", "")
|
||||
if not last_used:
|
||||
findings.append({"account": acct.get("username", ""), "issue": "no_last_used_date",
|
||||
"severity": "high", "detail": "Account has no recorded last-used date"})
|
||||
continue
|
||||
try:
|
||||
used_dt = datetime.strptime(last_used, "%Y-%m-%d")
|
||||
if used_dt < cutoff:
|
||||
findings.append({"account": acct.get("username", ""),
|
||||
"issue": "stale_account", "severity": "high",
|
||||
"detail": f"Last used {last_used}, exceeds {max_days}-day threshold"})
|
||||
except ValueError:
|
||||
findings.append({"account": acct.get("username", ""), "issue": "invalid_date",
|
||||
"severity": "medium", "detail": f"Cannot parse last_used: {last_used}"})
|
||||
return findings
|
||||
|
||||
|
||||
def check_shared_accounts(accounts: list[dict]) -> list[dict]:
|
||||
"""Detect shared/generic privileged accounts."""
|
||||
shared_patterns = ["admin", "root", "service", "svc_", "shared", "generic", "temp"]
|
||||
findings = []
|
||||
for acct in accounts:
|
||||
uname = acct.get("username", "").lower()
|
||||
owner = acct.get("owner", "").strip()
|
||||
for pat in shared_patterns:
|
||||
if pat in uname and not owner:
|
||||
findings.append({"account": acct.get("username", ""),
|
||||
"issue": "shared_account_no_owner", "severity": "critical",
|
||||
"detail": f"Appears shared (matches '{pat}') with no assigned owner"})
|
||||
break
|
||||
return findings
|
||||
|
||||
|
||||
def check_excessive_privileges(accounts: list[dict]) -> list[dict]:
|
||||
"""Flag accounts with overly broad privilege sets."""
|
||||
high_risk_roles = {"domain admin", "enterprise admin", "schema admin",
|
||||
"global admin", "super admin", "root"}
|
||||
findings = []
|
||||
for acct in accounts:
|
||||
roles = {r.strip().lower() for r in acct.get("roles", "").split(";")}
|
||||
overlap = roles & high_risk_roles
|
||||
if overlap:
|
||||
findings.append({"account": acct.get("username", ""),
|
||||
"issue": "excessive_privilege", "severity": "critical",
|
||||
"detail": f"Holds high-risk roles: {', '.join(sorted(overlap))}"})
|
||||
return findings
|
||||
|
||||
|
||||
def check_recertification(accounts: list[dict], cert_interval_days: int = 180) -> list[dict]:
|
||||
"""Flag accounts overdue for recertification."""
|
||||
cutoff = datetime.utcnow() - timedelta(days=cert_interval_days)
|
||||
findings = []
|
||||
for acct in accounts:
|
||||
cert_date = acct.get("last_certified", "")
|
||||
if not cert_date:
|
||||
findings.append({"account": acct.get("username", ""),
|
||||
"issue": "never_certified", "severity": "critical",
|
||||
"detail": "Account has never been certified"})
|
||||
continue
|
||||
try:
|
||||
cert_dt = datetime.strptime(cert_date, "%Y-%m-%d")
|
||||
if cert_dt < cutoff:
|
||||
findings.append({"account": acct.get("username", ""),
|
||||
"issue": "overdue_recertification", "severity": "high",
|
||||
"detail": f"Last certified {cert_date}, exceeds {cert_interval_days}-day cycle"})
|
||||
except ValueError:
|
||||
pass
|
||||
return findings
|
||||
|
||||
|
||||
def generate_report(accounts: list[dict], stale_days: int, cert_days: int) -> dict:
|
||||
"""Run all checks and produce a consolidated JSON report."""
|
||||
findings = []
|
||||
findings.extend(check_stale_accounts(accounts, stale_days))
|
||||
findings.extend(check_shared_accounts(accounts))
|
||||
findings.extend(check_excessive_privileges(accounts))
|
||||
findings.extend(check_recertification(accounts, cert_days))
|
||||
|
||||
severity_counts = {}
|
||||
for f in findings:
|
||||
severity_counts[f["severity"]] = severity_counts.get(f["severity"], 0) + 1
|
||||
|
||||
return {
|
||||
"report": "privileged_account_access_review",
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"total_accounts": len(accounts),
|
||||
"total_findings": len(findings),
|
||||
"severity_summary": severity_counts,
|
||||
"findings": findings,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Privileged Account Access Review Agent")
|
||||
parser.add_argument("--input", required=True, help="CSV file with privileged account inventory")
|
||||
parser.add_argument("--stale-days", type=int, default=90, help="Max days of inactivity (default: 90)")
|
||||
parser.add_argument("--cert-days", type=int, default=180, help="Recertification interval in days (default: 180)")
|
||||
parser.add_argument("--output", help="Output JSON file path")
|
||||
args = parser.parse_args()
|
||||
|
||||
accounts = load_accounts(args.input)
|
||||
report = generate_report(accounts, args.stale_days, args.cert_days)
|
||||
|
||||
output = json.dumps(report, indent=2)
|
||||
if args.output:
|
||||
Path(args.output).write_text(output, encoding="utf-8")
|
||||
print(f"Report written to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user