Scope — assets, data classification, compliance obligations, threat model (pull from loom-threat-model). Define what "in scope" means before touching anything.
Review by layer — app code, APIs, infra/IaC, data pipelines, ML (sections below).
Evidence — every finding cites file:line or config path + a concrete exploit scenario. A finding without a repro is a guess.
Rate — CVSS or Likelihood×Impact; rank most-severe first.
Remediate — specific fix (ideally a diff), not "sanitize inputs".
Run tooling first (loom-security-scan) to clear known-pattern noise, then spend human effort on logic and authorization flaws that scanners miss — that's where audits earn their keep.
No audit trail, secrets in logs, no alerting, tamperable logs
A10
SSRF
Server fetches attacker-controlled URLs → internal/metadata (see below)
For APIs, cross-check the OWASP API Security Top 10 — API1 is BOLA (object-level authz), the single most frequent API defect.
Vulnerability Patterns (wrong → right)
# A03 SQL injection
query = f"SELECT * FROM users WHERE id = {uid}" # ✗ string interpolation
cursor.execute("SELECT * FROM users WHERE id = %s", (uid,)) # ✓ parameterized
# A03 Command injection
os.system(f"ping {host}") # ✗
subprocess.run(["ping", "-c", "1", host]) # ✓ arg vector, no shell
# A03 XSS
return f"<h1>Welcome {username}</h1>" # ✗
return f"<h1>Welcome {escape(username)}</h1>" # ✓ context-aware output encoding
# A01 Path traversal
open(os.path.join(base, filename)) # ✗ filename="../../etc/passwd"
p = os.path.normpath(os.path.join(base, filename)) # ✓ normalize then confine
if not p.startswith(base + os.sep): raise SecurityError
# A08 Insecure deserialization
pickle.loads(user_input) # ✗ RCE by design
json.loads(user_input) # ✓ data-only format
A10 SSRF — defense is an allowlist, never a blocklist
Any server-side fetch of a user-influenced URL (webhooks, image proxies, PDF renderers, URL previews, importers) can be pointed at internal services or the cloud metadata endpoint 169.254.169.254 to steal IAM credentials.
# ✗ blocklist — bypassed by DNS rebinding, redirects, encoded IPs
if host in ("localhost", "127.0.0.1"): reject()
# ✓ allowlist scheme + host; resolve then re-check; forbid redirects to non-allowlisted
if scheme not in {"https"} or host not in ALLOWED_HOSTS: reject()
ip = resolve(host)
if ip_is_private(ip) or host not in ALLOWED_HOSTS: reject() # re-check post-DNS (rebinding)
fetch(url, allow_redirects=False, timeout=5)
Blocklists miss: DNS-rebinding, HTTP→internal redirects, IPv6 ([::1], [::ffff:127.0.0.1]), and decimal/octal/hex IP encodings (http://2130706433/ = 127.0.0.1). Enforce IMDSv2 on AWS so a bare SSRF can't read credentials.
Access-control (IDOR/BOLA), timing-safe secret comparison, JWT algorithm confusion, and password-hash choice (argon2id, bcrypt 72-byte truncation) are covered with wrong-vs-right in loom-auth — audit against that checklist rather than duplicating here. Runtime secret leakage (argv/ps, env inheritance, logs/stack traces) is in loom-security-scan.
Infrastructure & Cloud
IAM — least privilege; no *:*; audit privilege-escalation paths (pass-role, policy-attach); no long-lived keys where roles work.
Storage — no public buckets/blobs; encryption at rest (KMS-managed keys, not hardcoded); block public ACLs org-wide.
Network — no 0.0.0.0/0 on 22/3389/DB ports; segment; security-group minimalism.
Metadata — IMDSv2 required (SSRF hardening).
Containers — non-root, read-only rootfs, drop capabilities, no --privileged, no secrets in image layers/env; scan images (loom-security-scan).
K8s — RBAC least-privilege, NetworkPolicies default-deny, Pod Security Standards (restricted), etcd encryption, no automountServiceAccountToken unless needed.
IaC — scan Terraform/CFN with tfsec/checkov (loom-security-scan); review before apply.
Handy audit probes (tool details in loom-security-scan):
aws s3api get-bucket-policy-status --bucket B # public?
aws ec2 describe-security-groups \
--query "SecurityGroups[?IpPermissions[?IpRanges[?CidrIp=='0.0.0.0/0']]]"
kubectl get networkpolicies -A # gaps = flat network
Map findings to CWE/CVE/OWASP IDs so they're deduplicable and trackable.
Compliance (essentials)
SOC2 — access control (MFA, quarterly access reviews, least privilege), encryption at rest/transit, monitoring/SIEM + alerting, incident-response runbooks, change management. It's controls + evidence.
PCI-DSS — never store CAV2/CVC2/PIN; encrypt PAN, mask to last-4 in UI/logs; segment the CDE; audit trails for all cardholder-data access; quarterly scans + annual pentest.
Run npx skillmds@latest add cosmix/loom-security-audit in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Comprehensive security audits across applications, APIs, infrastructure, and data pipelines. It is listed under Security on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
cosmix (@cosmix) published this skill. Their other Agent Skills are listed on their SkillMD profile.