Files
mukul975 27c6414ca5 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
2026-03-10 21:02:12 +01:00

154 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Serverless function security review agent using boto3."""
import json
import re
import boto3
from botocore.exceptions import ClientError
def get_lambda_client(region="us-east-1"):
return boto3.client("lambda", region_name=region)
def get_iam_client(region="us-east-1"):
return boto3.client("iam", region_name=region)
def list_all_functions(client):
functions = []
paginator = client.get_paginator("list_functions")
for page in paginator.paginate():
functions.extend(page["Functions"])
return functions
def check_deprecated_runtime(runtime):
deprecated = [
"python2.7", "python3.6", "python3.7", "nodejs10.x",
"nodejs12.x", "nodejs14.x", "dotnetcore2.1", "dotnetcore3.1",
"ruby2.5", "java8", "go1.x",
]
return runtime in deprecated
def audit_execution_role(iam, role_arn):
findings = []
role_name = role_arn.split("/")[-1]
try:
attached = iam.list_attached_role_policies(RoleName=role_name)
for policy in attached["AttachedPolicies"]:
if policy["PolicyName"] == "AdministratorAccess":
findings.append(f"CRITICAL: Role {role_name} has AdministratorAccess")
version_id = iam.get_policy(PolicyArn=policy["PolicyArn"])["Policy"]["DefaultVersionId"]
doc = iam.get_policy_version(
PolicyArn=policy["PolicyArn"], VersionId=version_id
)["PolicyVersion"]["Document"]
for stmt in doc.get("Statement", []):
actions = stmt.get("Action", [])
if isinstance(actions, str):
actions = [actions]
if "*" in actions or any(a.endswith(":*") for a in actions):
findings.append(
f"WARNING: {role_name} has wildcard action: {actions} "
f"on {stmt.get('Resource', '*')}"
)
except ClientError as e:
findings.append(f"ERROR auditing role {role_name}: {e}")
return findings
SENSITIVE_PATTERNS = [
re.compile(r"(?i)(password|secret|key|token|credential|api.?key)"),
re.compile(r"(?i)(aws.?access|aws.?secret)"),
re.compile(r"(?i)(database.?url|connection.?string|db.?pass)"),
re.compile(r"AKIA[0-9A-Z]{16}"),
]
def check_env_secrets(env_vars):
findings = []
if not env_vars:
return findings
for key, value in env_vars.items():
for pattern in SENSITIVE_PATTERNS:
if pattern.search(key) or pattern.search(str(value)):
masked = value[:4] + "****" + value[-4:] if len(value) > 8 else "****"
findings.append(f"SENSITIVE: {key} = {masked}")
break
return findings
def check_public_access(client, function_name):
findings = []
try:
policy = client.get_policy(FunctionName=function_name)
doc = json.loads(policy["Policy"])
for stmt in doc.get("Statement", []):
principal = stmt.get("Principal", {})
if principal == "*" or principal == {"AWS": "*"}:
findings.append(
f"PUBLIC ACCESS: {function_name} allows public invocation "
f"(statement: {stmt.get('Sid', 'unnamed')})"
)
except ClientError:
pass
try:
urls = client.list_function_url_configs(FunctionName=function_name)
for url_cfg in urls.get("FunctionUrlConfigs", []):
if url_cfg.get("AuthType") == "NONE":
findings.append(
f"UNAUTHENTICATED URL: {function_name} -> {url_cfg['FunctionUrl']}"
)
except ClientError:
pass
return findings
def run_review(region="us-east-1"):
lam = get_lambda_client(region)
iam = get_iam_client(region)
functions = list_all_functions(lam)
report = {
"total_functions": len(functions),
"deprecated_runtimes": [],
"role_findings": [],
"secret_findings": [],
"public_access_findings": [],
}
for func in functions:
name = func["FunctionName"]
runtime = func.get("Runtime", "unknown")
if check_deprecated_runtime(runtime):
report["deprecated_runtimes"].append({"function": name, "runtime": runtime})
report["role_findings"].extend(audit_execution_role(iam, func["Role"]))
env = func.get("Environment", {}).get("Variables", {})
secrets = check_env_secrets(env)
if secrets:
report["secret_findings"].extend(
[{"function": name, "finding": s} for s in secrets]
)
report["public_access_findings"].extend(check_public_access(lam, name))
return report
def print_report(report):
print("Serverless Function Security Review")
print("=" * 40)
print(f"Functions Reviewed: {report['total_functions']}")
for section, label in [
("deprecated_runtimes", "Deprecated Runtimes"),
("role_findings", "Role Issues"),
("secret_findings", "Secrets in Env Vars"),
("public_access_findings", "Public Access"),
]:
items = report[section]
print(f"\n{label}: {len(items)} finding(s)")
for item in items:
print(f" - {item}")
if __name__ == "__main__":
result = run_review()
print_report(result)