supply-chain-attack-recon – Security skill for AI agents
External recon for software supply-chain attack surface — package-namespace squatting candidates, dependency-confusion vulnerabilities, GitHub Actions injection openings, container image registry exposure, SBOM mining, internal-package-name leakage, and CI/CD configuration exposure. Reconnaissance and identification ON
Install the supply-chain-attack-recon skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/supply-chain-attack-recon-e64fb.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /supply-chain-attack-recon – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/supply-chain-attack-recon-e64fb.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $supply-chain-attack-recon – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/supply-chain-attack-recon-e64fb.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /supply-chain-attack-recon – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/supply-chain-attack-recon-e64fb.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /supply-chain-attack-recon – then describe the task you want done.
SKILL.md content
When to use
Trigger when:
- Target has a public GitHub organization (find via OSINT)
- JS bundles reference internal-looking package names (
@target-internal/...,target-utils,target-shared) - Build logs, SBOMs, or
package-lock.jsonfiles are publicly accessible - Target uses CI/CD that's partially public (GitHub Actions, GitLab CI, Bitrise)
- Docker images on Docker Hub/GHCR/Quay belong to target org
- Findings include
npmrc/pip.conf/gradle.propertieswith internal registry URLs .github/workflows/*.ymlfiles reference internal tooling
Do NOT use for:
- Internal-network artifact registries (out of scope per external boundary)
- Actually publishing typosquats / dep-confusion packages without explicit OK
- Compromising upstream open-source projects (massive blast radius — illegal in most jurisdictions without authorization)
---
The supply-chain attack surface map
Target Org
├── Public GitHub Org → workflow files → secrets exfil opportunities
├── Internal package names in JS/Android bundles → dependency confusion
├── Docker images on public registries → secrets in layers, RCE on pull
├── SBOM / artifact metadata → exact dep versions for known-vuln chaining
├── npmrc / pip.conf in repos → internal registry URL disclosure
├── External package dependencies → typosquat name candidates
└── Build/release pipelines → injection if pull_request_target etc.---
Step 1 — GitHub org discovery
TARGET="<brand>" # set to target brand name
# Direct guesses
for guess in $TARGET "${TARGET}-tech" "${TARGET}corp" "${TARGET}-io" "${TARGET}-eng"; do
curl --max-time 30 --connect-timeout 10 -sI "https://github.com/$guess" | grep -E "HTTP|status" | head -1
done
# Via WHOIS / email-domain → GitHub search
gh search users --owner-affiliations=organization --query "$TARGET" --limit 10
# Via employees → reverse from social media + GitHub profile
# Many employees list their employer org on their GitHub profile---
Step 2 — Enumerate public repos for sensitive artifacts
ORG="targetorg"
# List public repos
gh repo list "$ORG" --limit 100 --json name,description,visibility,defaultBranchRef
# Look for high-signal repo names
gh repo list "$ORG" --limit 100 --json name | jq -r '.[].name' | grep -iE "internal|infra|deploy|config|secret|setup|sdk|api"
# Clone all (small org) or selectively
gh repo clone "$ORG/$repo_name"---
Step 3 — Internal package-name discovery
From JS bundles
# JS bundles are the easiest source of internal npm names
curl --max-time 30 --connect-timeout 10 -sk https://target.com/main.js | grep -oE '@[a-z-]+/[a-z-]+' | sort -u
curl --max-time 30 --connect-timeout 10 -sk https://target.com/main.js | grep -oE 'require\("[^"]+"\)' | sort -u
# Look for scoped names that are NOT public on npm
for pkg in @target/utils @target-internal/api @companybrand/sdk; do
status=$(curl --max-time 30 --connect-timeout 10 -sI "https://registry.npmjs.org/$pkg" | head -1 | awk '{print $2}')
echo " $pkg → $status"
# 404 → name unclaimed on public npm → DEPENDENCY-CONFUSION CANDIDATE
doneFrom GitHub repo package.json files
# Public repos with package.json that reference internal scopes
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
pkg=$(gh api "repos/$ORG/$repo/contents/package.json" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
echo "$pkg" | jq -r '.dependencies // {} | keys[]' 2>/dev/null | grep -E '^@[a-z-]+/'
done | sort -uFrom Python projects
# Internal pip package names
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
gh api "repos/$ORG/$repo/contents/requirements.txt" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null
done | sort -u | grep -vE '^(requests|django|flask|numpy|pandas|...common)'---
Step 4 — Dependency-confusion vulnerability check
For each internal-looking package name discovered:
NAME="@target-internal/utils" # example
# npm check
curl --max-time 30 --connect-timeout 10 -sI "https://registry.npmjs.org/$NAME" | head -1
# 404 → name is registerable → DEPENDENCY-CONFUSION POSSIBLE
# pypi check (no scopes, just name)
NAME="target_utils"
curl --max-time 30 --connect-timeout 10 -sI "https://pypi.org/project/$NAME/" | head -1
# 404 → name is registerable
# rubygems
curl --max-time 30 --connect-timeout 10 -sI "https://rubygems.org/api/v1/gems/$NAME.json" | head -1
# Go modules — slightly different, since module names are URLs
# Check if module path is reachable
curl --max-time 30 --connect-timeout 10 -sI "https://proxy.golang.org/github.com/$ORG/$NAME/@latest" | head -1Severity calibration: Just because a name is unclaimed doesn't mean it's exploitable. You also need:
1. Evidence the target's BUILD SYSTEM resolves names from public registries (not just their internal one)
2. OR evidence the target's package manager is configured insecurely (e.g., .npmrc without @scope:registry= mapping)
3. OR the package would be installed by their builds (it's actually in package.json, not just referenced in dead code)
A 404 on registry without supporting context is INFORMATIONAL only.
---
Step 5 — Typosquat candidates (around external dependencies)
For each external public dependency the target uses:
# Common typosquat patterns:
# Original: "react-router-dom"
# Typos:
# "react-router-doms" (extra s)
# "react-routter-dom" (double t)
# "react-rotuer-dom" (transposed)
# "react--router-dom" (double dash)
# "react-router-dorn" (m→rn)
# "reactrouterdom" (no dashes)
# Generate candidates
python3 -c "
import sys
name='react-router-dom'
for i in range(len(name)):
print(name[:i] + name[i+1:]) # delete
if i < len(name)-1:
print(name[:i] + name[i+1] + name[i] + name[i+2:]) # transpose
"
# Check which candidates are UNCLAIMED on the registry
for candidate in ...; do
status=$(curl --max-time 30 --connect-timeout 10 -sI "https://registry.npmjs.org/$candidate" | head -1 | awk '{print $2}')
[ "$status" = "404" ] && echo " UNCLAIMED: $candidate"
done⚠ EXTERNAL-OFFENSIVE NOTE: publishing a typosquat package to a public registry is an attack on the wider ecosystem. NEVER do this without explicit, written, scope-clarified sign-off. It can affect users outside your engagement and may be illegal.
---
Step 6 — GitHub Actions workflow injection scan
For each public repo with .github/workflows/:
for repo in $(gh repo list "$ORG" --limit 50 --json name --jq '.[].name'); do
workflows=$(gh api "repos/$ORG/$repo/contents/.github/workflows" --jq '.[].name' 2>/dev/null)
for wf in $workflows; do
content=$(gh api "repos/$ORG/$repo/contents/.github/workflows/$wf" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null)
echo "=== $repo/$wf ==="
# High-risk patterns:
# 1. pull_request_target (runs with secrets on PR from forks)
echo "$content" | grep -E 'pull_request_target'
# 2. Untrusted context interpolation
echo "$content" | grep -E '\$\{\{[^}]*github\.(event|head_ref|pull_request)[^}]*\}\}'
# 3. ${{ github.event.* }} into shell run blocks
echo "$content" | grep -B1 -A2 'run:' | grep -E '\$\{\{ ?github\.event\.'
# 4. checkout of PR head with elevated perms
echo "$content" | grep -E 'ref:.*pull_request|head_ref'
# 5. Self-hosted runner without isolation
echo "$content" | grep -E 'runs-on:.*self-hosted'
# 6. Unpinned third-party actions — mutable tag (@v1, @main) vs pinned (@<40-char sha>)
# Mutable tags can be repointed by a compromised action repo (see case #9, tj-actions/changed-files).
echo "$content" | grep -E 'uses: *[^ ]+/[^ ]+@(v?[0-9]+([.][0-9]+)*|main|master|latest)\b' | grep -v '@[0-9a-f]\{40\}'
done
doneInjection patterns to flag (severity guide)
| Pattern | Severity |
|---|---|
| pull_request_target + actions/checkout with ref: pull_request.head.sha + uses repo secrets | Critical — RCE on runner with org secrets |
| ${{ github.event.pull_request.title }} interpolated into shell | Critical — script injection via PR title |
| Third-party action pinned to a mutable tag (uses: org/repo@v1 / @main) instead of a commit SHA | High — repointable supply-chain vector (see case #9) |
| Self-hosted runner reachable from public repo workflows | High — persistent attacker pivot |
| Issue-comment-triggered workflow that runs gh with token | High |
| Workflow downloads from URL that target controls | Medium |
---
Step 7 — Docker / container image registry mining
# Docker Hub
curl --max-time 30 --connect-timeout 10 -s "https://hub.docker.com/v2/repositories/$ORG/?page_size=100" | jq -r '.results[].name'
# GHCR (GitHub Container Registry) — public images visible in repo packages tab
gh api "users/$ORG/packages?package_type=container" 2>/dev/null
gh api "orgs/$ORG/packages?package_type=container" 2>/dev/null
# For each i… (full content is in the download package)
Related skills
- darkmoon-pentest – Start, follow and triage authorized autonomous AI pentest runs on a self-hosted Darkmoon…
- dast-scanning – Perform dynamic application security testing with OWASP ZAP, Burp Suite, and Nikto.
- tls-fingerprint-impersonation – Spoof TLS ClientHello and JA4 fingerprints for browser impersonation.
- database-security – Authorized database security assessment across PostgreSQL, MySQL, MSSQL, MongoDB, and…
- vhost-enumeration – Discover hidden virtual hosts via Host header fuzzing and SSL certificate parsing.
- solidity-security – Master smart contract security best practices to prevent common vulnerabilities and…
- vmware-vcenter-attack – VMware vSphere / vCenter Server external attack matrix — version fingerprinting, the…
- web-enumeration – Sensitive file scanning, path traversal bypass, vHost enum, .env extract, log mining…