mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24: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
141 lines
5.2 KiB
Python
141 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""AWS IAM permission boundary management agent using boto3."""
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
from typing import List
|
|
|
|
try:
|
|
import boto3
|
|
from botocore.exceptions import ClientError
|
|
except ImportError:
|
|
sys.exit("boto3 required: pip install boto3")
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_iam_client(profile: str = "", region: str = "us-east-1"):
|
|
"""Create IAM client with optional profile."""
|
|
session = boto3.Session(profile_name=profile) if profile else boto3.Session()
|
|
return session.client("iam", region_name=region)
|
|
|
|
|
|
def create_permission_boundary(client, policy_name: str, allowed_services: List[str],
|
|
allowed_regions: List[str] = None) -> dict:
|
|
"""Create a permission boundary policy restricting services and regions."""
|
|
statements = [{
|
|
"Sid": "AllowedServices",
|
|
"Effect": "Allow",
|
|
"Action": [f"{svc}:*" for svc in allowed_services],
|
|
"Resource": "*",
|
|
}]
|
|
if allowed_regions:
|
|
statements[0]["Condition"] = {
|
|
"StringEquals": {"aws:RequestedRegion": allowed_regions}
|
|
}
|
|
statements.append({
|
|
"Sid": "DenyBoundaryChanges",
|
|
"Effect": "Deny",
|
|
"Action": ["iam:DeleteRolePermissionsBoundary", "iam:PutRolePermissionsBoundary"],
|
|
"Resource": "*",
|
|
})
|
|
policy_doc = {"Version": "2012-10-17", "Statement": statements}
|
|
try:
|
|
resp = client.create_policy(
|
|
PolicyName=policy_name,
|
|
PolicyDocument=json.dumps(policy_doc),
|
|
Description=f"Permission boundary: {', '.join(allowed_services)}",
|
|
)
|
|
arn = resp["Policy"]["Arn"]
|
|
logger.info("Created boundary policy: %s", arn)
|
|
return {"policy_arn": arn, "policy_document": policy_doc}
|
|
except ClientError as exc:
|
|
return {"error": str(exc)}
|
|
|
|
|
|
def attach_boundary_to_role(client, role_name: str, boundary_arn: str) -> dict:
|
|
"""Attach permission boundary to an IAM role."""
|
|
try:
|
|
client.put_role_permissions_boundary(
|
|
RoleName=role_name, PermissionsBoundary=boundary_arn)
|
|
logger.info("Attached boundary %s to role %s", boundary_arn, role_name)
|
|
return {"role": role_name, "boundary_arn": boundary_arn, "attached": True}
|
|
except ClientError as exc:
|
|
return {"role": role_name, "error": str(exc)}
|
|
|
|
|
|
def audit_roles_without_boundary(client) -> List[dict]:
|
|
"""Find IAM roles that lack a permission boundary."""
|
|
paginator = client.get_paginator("list_roles")
|
|
unbounded = []
|
|
for page in paginator.paginate():
|
|
for role in page["Roles"]:
|
|
if "PermissionsBoundary" not in role:
|
|
unbounded.append({
|
|
"role_name": role["RoleName"],
|
|
"arn": role["Arn"],
|
|
"created": role["CreateDate"].isoformat(),
|
|
})
|
|
logger.info("Found %d roles without permission boundary", len(unbounded))
|
|
return unbounded
|
|
|
|
|
|
def audit_boundary_effectiveness(client, role_name: str) -> dict:
|
|
"""Audit effective permissions for a role with boundary."""
|
|
try:
|
|
role = client.get_role(RoleName=role_name)["Role"]
|
|
boundary = role.get("PermissionsBoundary", {})
|
|
policies_resp = client.list_attached_role_policies(RoleName=role_name)
|
|
inline_resp = client.list_role_policies(RoleName=role_name)
|
|
return {
|
|
"role": role_name,
|
|
"boundary_arn": boundary.get("PermissionsBoundaryArn", "NONE"),
|
|
"attached_policies": [p["PolicyName"] for p in policies_resp["AttachedPolicies"]],
|
|
"inline_policies": inline_resp["PolicyNames"],
|
|
}
|
|
except ClientError as exc:
|
|
return {"role": role_name, "error": str(exc)}
|
|
|
|
|
|
def generate_report(client) -> dict:
|
|
"""Generate permission boundary compliance report."""
|
|
unbounded = audit_roles_without_boundary(client)
|
|
report = {
|
|
"analysis_date": datetime.utcnow().isoformat(),
|
|
"roles_without_boundary": unbounded,
|
|
"roles_without_boundary_count": len(unbounded),
|
|
"recommendations": [],
|
|
}
|
|
if unbounded:
|
|
report["recommendations"].append(
|
|
f"Attach permission boundaries to {len(unbounded)} roles lacking boundaries")
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="AWS IAM Permission Boundary Agent")
|
|
parser.add_argument("--profile", default="", help="AWS CLI profile name")
|
|
parser.add_argument("--region", default="us-east-1")
|
|
parser.add_argument("--audit", action="store_true", help="Audit roles without boundaries")
|
|
parser.add_argument("--output-dir", default=".")
|
|
parser.add_argument("--output", default="iam_boundary_report.json")
|
|
args = parser.parse_args()
|
|
|
|
os.makedirs(args.output_dir, exist_ok=True)
|
|
client = get_iam_client(args.profile, args.region)
|
|
report = generate_report(client)
|
|
out_path = os.path.join(args.output_dir, args.output)
|
|
with open(out_path, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
logger.info("Report saved to %s", out_path)
|
|
print(json.dumps(report, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|