Files
Anthropic-Cybersecurity-Skills/skills/auditing-gcp-iam-permissions/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

161 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""Agent for auditing GCP IAM permissions using google-cloud libraries."""
import json
import argparse
from datetime import datetime
from google.cloud import asset_v1
from google.cloud import resourcemanager_v3
def search_iam_policies(scope, query=""):
"""Search IAM policies across the GCP organization."""
client = asset_v1.AssetServiceClient()
request = asset_v1.SearchAllIamPoliciesRequest(scope=scope, query=query, page_size=500)
results = []
for result in client.search_all_iam_policies(request=request):
for binding in result.policy.bindings:
results.append({
"resource": result.resource,
"role": binding.role,
"members": list(binding.members),
})
return results
def find_primitive_roles(scope):
"""Find all IAM bindings using primitive roles (Owner, Editor)."""
query = "policy:roles/owner OR policy:roles/editor"
return search_iam_policies(scope, query)
def find_public_bindings(scope):
"""Find resources accessible to allUsers or allAuthenticatedUsers."""
query = "policy:allUsers OR policy:allAuthenticatedUsers"
return search_iam_policies(scope, query)
def list_service_accounts(project_id):
"""List all service accounts in a project with key info."""
from google.cloud import iam_admin_v1
client = iam_admin_v1.IAMClient()
request = iam_admin_v1.ListServiceAccountsRequest(name=f"projects/{project_id}")
accounts = []
for sa in client.list_service_accounts(request=request):
sa_info = {
"email": sa.email,
"display_name": sa.display_name,
"disabled": sa.disabled,
"user_managed_keys": [],
}
key_request = iam_admin_v1.ListServiceAccountKeysRequest(
name=sa.name,
key_types=[iam_admin_v1.ListServiceAccountKeysRequest.KeyType.USER_MANAGED],
)
keys = client.list_service_account_keys(request=key_request)
for key in keys.keys:
sa_info["user_managed_keys"].append({
"name": key.name.split("/")[-1],
"valid_after": str(key.valid_after_time),
"valid_before": str(key.valid_before_time),
})
accounts.append(sa_info)
return accounts
def get_project_iam_policy(project_id):
"""Get IAM policy for a specific project."""
client = resourcemanager_v3.ProjectsClient()
request = {"resource": f"projects/{project_id}"}
policy = client.get_iam_policy(request=request)
bindings = []
for binding in policy.bindings:
bindings.append({"role": binding.role, "members": list(binding.members)})
return bindings
def analyze_permissions(scope, identity):
"""Analyze what resources an identity can access."""
client = asset_v1.AssetServiceClient()
request = asset_v1.AnalyzeIamPolicyRequest(
analysis_query=asset_v1.IamPolicyAnalysisQuery(
scope=scope,
identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
identity=identity
),
)
)
response = client.analyze_iam_policy(request=request)
results = []
for entry in response.main_analysis.analysis_results:
for acl in entry.access_control_lists:
resources = [r.full_resource_name for r in acl.resources]
accesses = [a.role for a in acl.accesses]
results.append({"resources": resources, "roles": accesses})
return results
def classify_risk(bindings):
"""Classify risk for IAM bindings."""
critical = []
high = []
for b in bindings:
role = b.get("role", "")
members = b.get("members", [])
if "allUsers" in members or "allAuthenticatedUsers" in members:
critical.append(b)
elif role in ("roles/owner", "roles/editor"):
for m in members:
if "serviceAccount" in m:
critical.append(b)
break
else:
high.append(b)
return {"critical": critical, "high": high}
def main():
parser = argparse.ArgumentParser(description="GCP IAM Permissions Audit Agent")
parser.add_argument("--org-id", help="GCP Organization ID")
parser.add_argument("--project-id", help="GCP Project ID")
parser.add_argument("--identity", help="Identity to analyze (user:email or serviceAccount:email)")
parser.add_argument("--output", default="gcp_iam_audit.json")
parser.add_argument("--action", choices=[
"primitive_roles", "public_access", "service_accounts",
"analyze_identity", "full_audit"
], default="full_audit")
args = parser.parse_args()
scope = f"organizations/{args.org_id}" if args.org_id else f"projects/{args.project_id}"
report = {"audit_date": datetime.utcnow().isoformat(), "scope": scope, "findings": {}}
if args.action in ("primitive_roles", "full_audit"):
primitives = find_primitive_roles(scope)
report["findings"]["primitive_roles"] = primitives
print(f"[+] Primitive role bindings: {len(primitives)}")
if args.action in ("public_access", "full_audit"):
public = find_public_bindings(scope)
report["findings"]["public_access"] = public
print(f"[+] Public access bindings: {len(public)}")
if args.action in ("service_accounts", "full_audit") and args.project_id:
sas = list_service_accounts(args.project_id)
report["findings"]["service_accounts"] = sas
keys_count = sum(len(sa["user_managed_keys"]) for sa in sas)
print(f"[+] Service accounts: {len(sas)}, user-managed keys: {keys_count}")
if args.action == "analyze_identity" and args.identity:
access = analyze_permissions(scope, args.identity)
report["findings"]["identity_access"] = access
print(f"[+] Resources accessible by {args.identity}: {len(access)}")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()