mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 14:14:56 +03:00
c47eed6a64
- 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
187 lines
7.0 KiB
Python
187 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Delinea Secret Server PAM agent for privileged credential management."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
try:
|
|
import requests
|
|
except ImportError:
|
|
print("Install requests: pip install requests")
|
|
sys.exit(1)
|
|
|
|
|
|
class SecretServerClient:
|
|
"""Client for Delinea Secret Server REST API."""
|
|
|
|
def __init__(self, base_url, username, password, domain=None):
|
|
self.base_url = base_url.rstrip("/")
|
|
self.session = requests.Session()
|
|
self.token = self._authenticate(username, password, domain)
|
|
self.session.headers.update({"Authorization": f"Bearer {self.token}"})
|
|
|
|
def _authenticate(self, username, password, domain):
|
|
"""Authenticate and retrieve OAuth2 bearer token."""
|
|
url = f"{self.base_url}/oauth2/token"
|
|
data = {"grant_type": "password", "username": username, "password": password}
|
|
if domain:
|
|
data["domain"] = domain
|
|
resp = self.session.post(url, data=data, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()["access_token"]
|
|
|
|
def search_secrets(self, search_text=None, folder_id=None, secret_template_id=None):
|
|
"""Search for secrets with optional filters."""
|
|
params = {}
|
|
if search_text:
|
|
params["filter.searchText"] = search_text
|
|
if folder_id:
|
|
params["filter.folderId"] = folder_id
|
|
if secret_template_id:
|
|
params["filter.secretTemplateId"] = secret_template_id
|
|
resp = self.session.get(f"{self.base_url}/api/v1/secrets", params=params, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
def get_secret(self, secret_id):
|
|
"""Retrieve a secret by ID."""
|
|
resp = self.session.get(f"{self.base_url}/api/v1/secrets/{secret_id}", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def create_secret(self, name, template_id, folder_id, fields):
|
|
"""Create a new secret in the vault."""
|
|
items = [{"fieldId": fid, "itemValue": val} for fid, val in fields.items()]
|
|
data = {"name": name, "secretTemplateId": template_id,
|
|
"folderId": folder_id, "items": items}
|
|
resp = self.session.post(f"{self.base_url}/api/v1/secrets", json=data, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def get_secret_templates(self):
|
|
"""List available secret templates."""
|
|
resp = self.session.get(f"{self.base_url}/api/v1/secret-templates", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
def get_folders(self, parent_id=None):
|
|
"""List folders, optionally under a parent."""
|
|
params = {}
|
|
if parent_id:
|
|
params["filter.parentFolderId"] = parent_id
|
|
resp = self.session.get(f"{self.base_url}/api/v1/folders", params=params, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
def rotate_secret_password(self, secret_id):
|
|
"""Trigger password rotation for a secret (Remote Password Changing)."""
|
|
resp = self.session.post(
|
|
f"{self.base_url}/api/v1/secrets/{secret_id}/change-password", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def get_secret_audit(self, secret_id):
|
|
"""Get audit trail for a specific secret."""
|
|
resp = self.session.get(f"{self.base_url}/api/v1/secrets/{secret_id}/audits", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
def checkout_secret(self, secret_id):
|
|
"""Check out a secret for exclusive access."""
|
|
resp = self.session.post(
|
|
f"{self.base_url}/api/v1/secrets/{secret_id}/check-out", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def checkin_secret(self, secret_id):
|
|
"""Check in a previously checked-out secret."""
|
|
resp = self.session.post(
|
|
f"{self.base_url}/api/v1/secrets/{secret_id}/check-in", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def get_users(self):
|
|
"""List all users."""
|
|
resp = self.session.get(f"{self.base_url}/api/v1/users", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
def get_roles(self):
|
|
"""List all roles."""
|
|
resp = self.session.get(f"{self.base_url}/api/v1/roles", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json().get("records", [])
|
|
|
|
|
|
def run_pam_audit(client):
|
|
"""Run a PAM security audit."""
|
|
print(f"\n{'='*60}")
|
|
print(f" DELINEA SECRET SERVER PAM AUDIT")
|
|
print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
templates = client.get_secret_templates()
|
|
print(f"--- SECRET TEMPLATES ({len(templates)}) ---")
|
|
for t in templates[:10]:
|
|
print(f" [{t['id']}] {t['name']}")
|
|
|
|
folders = client.get_folders()
|
|
print(f"\n--- FOLDERS ({len(folders)}) ---")
|
|
for f in folders[:10]:
|
|
print(f" [{f['id']}] {f['folderName']}")
|
|
|
|
secrets = client.search_secrets()
|
|
print(f"\n--- SECRETS ({len(secrets)}) ---")
|
|
for s in secrets[:10]:
|
|
print(f" [{s['id']}] {s['name']} (Template: {s.get('secretTemplateName', 'N/A')})")
|
|
|
|
users = client.get_users()
|
|
print(f"\n--- USERS ({len(users)}) ---")
|
|
for u in users[:10]:
|
|
print(f" [{u['id']}] {u.get('userName', 'N/A')} - Enabled: {u.get('isDisabled', True)}")
|
|
|
|
roles = client.get_roles()
|
|
print(f"\n--- ROLES ({len(roles)}) ---")
|
|
for r in roles[:10]:
|
|
print(f" [{r['id']}] {r['name']}")
|
|
|
|
print(f"\n{'='*60}\n")
|
|
return {"templates": len(templates), "folders": len(folders),
|
|
"secrets": len(secrets), "users": len(users), "roles": len(roles)}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Delinea Secret Server PAM Agent")
|
|
parser.add_argument("--url", required=True, help="Secret Server base URL")
|
|
parser.add_argument("--username", required=True, help="Username")
|
|
parser.add_argument("--password", required=True, help="Password")
|
|
parser.add_argument("--domain", help="AD domain (optional)")
|
|
parser.add_argument("--audit", action="store_true", help="Run PAM audit")
|
|
parser.add_argument("--search", help="Search secrets by keyword")
|
|
parser.add_argument("--rotate", type=int, help="Rotate password for secret ID")
|
|
parser.add_argument("--output", help="Save report to JSON")
|
|
args = parser.parse_args()
|
|
|
|
client = SecretServerClient(args.url, args.username, args.password, args.domain)
|
|
|
|
if args.audit:
|
|
report = run_pam_audit(client)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
elif args.search:
|
|
results = client.search_secrets(search_text=args.search)
|
|
for s in results:
|
|
print(f" [{s['id']}] {s['name']}")
|
|
elif args.rotate:
|
|
result = client.rotate_secret_password(args.rotate)
|
|
print(json.dumps(result, indent=2))
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|