A comprehensive supply chain security scanner for authorised security research and defensive security auditing. Clones all repositories of a GitHub organisation and scans them for dangling/abandoned external resource references that could be hijacked by an attacker.
For authorised security research, bug bounty programs, and defensive security auditing only. Only scan organisations you have explicit permission to test.
TrustAudit scans for the Dangling Trust vulnerability class across every layer of the modern software stack:
| Pattern | Risk |
|---|---|
GitHub Actions using abandoned-dev/action@v2 |
Account deleted → RCE in CI |
curl https://domain.com/install.sh | bash |
Domain expired → RCE |
| Jenkins / GitLab CI container images | Namespace claimable → container RCE |
| Pattern | Risk |
|---|---|
FROM ghcr.io/external-org/image:latest |
Org deleted → backdoored base image |
| Docker Compose external images | Same as above |
| Registry | Language | Files Scanned |
|---|---|---|
| npm | JavaScript / Node | package.json, .npmrc |
| PyPI | Python | requirements*.txt, Pipfile, setup.py |
| Go Proxy | Go | go.mod |
| Maven Central | Java / Kotlin | pom.xml |
| NuGet | C# / .NET | *.csproj, NuGet.Config |
| crates.io | Rust | Cargo.toml |
| RubyGems | Ruby | Gemfile |
| Packagist | PHP | composer.json |
| Pattern | Risk |
|---|---|
Terraform source = "git::https://github.com/dev/module" |
Dev deletes account → controls cloud infra |
CloudFormation TemplateURL: https://s3.amazonaws.com/bucket/vpc.yaml |
Bucket claimable → attacker defines IAM |
Git submodules in .gitmodules |
Upstream deleted → RCE on clone |
| DVC remote stores | Bucket claimable → training data poisoning |
SSH ssh-keyscan domain.com in CI |
Domain expires → MITM on CI |
| Pattern | Risk |
|---|---|
AutoModel.from_pretrained("user/model") |
HF account deleted → backdoored weights served to production |
load_dataset("user/dataset") |
Dataset repo claimable → training data poisoning |
!wget https://site.com/model.pkl |
Domain expired → pickle = arbitrary code execution |
| MLflow tracking URIs | S3 bucket claimable → artifact store compromise |
| Weights & Biases artifact references | W&B org deleted → artifact claimable |
Jupyter notebook !pip install |
Package claimable → RCE at notebook run time |
| Pattern | Risk |
|---|---|
brew tap org/tools |
GitHub account deleted → Homebrew formula claimable |
VS Code .vscode/extensions.json |
Publisher account deleted → extension update claimable |
curl https://domain/install.sh | bash |
Domain expired → RCE on developer machines |
| Pattern | Risk |
|---|---|
webhook_url: https://hooks.service.com/... |
Domain expired → attacker receives all payloads |
Every reference gets one of three statuses:
| Status | Meaning | Action |
|---|---|---|
| ✗ DEAD | Resource is gone / claimable | Investigate immediately |
| ✓ ACTIVE | Resource exists and is reachable | No action needed |
| ? UNKNOWN | Could not determine status | Manual check recommended |
| Signal | Meaning |
|---|---|
S3: NoSuchBucket |
CLAIMABLE — bucket name is free |
S3: AccessDenied |
ACTIVE — owned by someone |
GitHub: 404 on user |
CLAIMABLE — account free to register |
GitHub: 404 on repo |
Repo deleted but account may exist |
DNS: NXDOMAIN |
CLAIMABLE — domain not registered |
npm: 404 |
CLAIMABLE — package name free |
git clone https://github.com/your-org/trustaudit
cd trustaudit
pip install -r requirements.txt- Python 3.8+
- Git (for cloning repos)
- GitHub token (recommended — otherwise 60 req/hour rate limit)
python trustaudit.py --org netflix --token ghp_yourtokenpython trustaudit.py --repo https://github.com/org/repo --token ghp_yourtokenpython trustaudit.py --local /path/to/cloned/repo# Limit to first 20 repos (useful for large orgs)
python trustaudit.py --org kubernetes --max-repos 20 --token ghp_xxx
# Process large orgs in batches to reduce peak disk usage
python trustaudit.py --org apple --repo-batch-size 20 --token ghp_xxx
# Scan and run AI triage with Gemini on generated reports
python trustaudit.py --org myorg --token ghp_xxx --analyze gemini --gemini-api-key YOUR_GEMINI_KEY
# Analyze existing report files only (no scanning)
python trustaudit.py --analyze-only --analyze gemini --analyze-files ./trustaudit_output/report.json ./trustaudit_output/report.md --gemini-api-key YOUR_GEMINI_KEY
# Show all severities including LOW
python trustaudit.py --org myorg --min-severity LOW --token ghp_xxx
# Keep cloned repos for further analysis
python trustaudit.py --org myorg --keep --output ./results --token ghp_xxx
# Scan only AI/ML and CI/CD categories
python trustaudit.py --org myorg --categories "AI/ML" "CI/CD" --token ghp_xxx
# More parallel workers for faster validation (careful with rate limits)
python trustaudit.py --org myorg --workers 20 --token ghp_xxxexport GITHUB_TOKEN=ghp_yourtoken
python trustaudit.py --org myorgFor large organisations, use batch mode so each chunk is cloned, scanned, validated, and deleted before the next one starts:
python trustaudit.py --org apple --repo-batch-size 20 --token ghp_yourtokenThis keeps peak disk usage lower while still producing one combined final report.
TrustAudit can send the generated JSON/Markdown reports to Gemini for a second-pass security triage.
Default flow:
- Scan repos.
- Write JSON + Markdown reports.
- Send those reports to Gemini.
- Save a separate AI analysis Markdown file.
Minimal setup:
export GEMINI_API_KEY=your_gemini_key
python trustaudit.py --org myorg --token ghp_yourtoken --repo-batch-size 20 --analyze geminiYou can also analyze existing report files only:
python trustaudit.py --analyze-only --analyze gemini --analyze-files ./trustaudit_output/report.json ./trustaudit_output/report.md --gemini-api-key YOUR_GEMINI_KEYThis is useful when you want an LLM to separate likely false positives from findings worth manual review without rescanning the repositories.
TrustAudit produces three outputs:
╔════════════════════════════════════════════════════════════════╗
║ TrustAudit — Dangling Trust Security Scanner ║
╚════════════════════════════════════════════════════════════════╝
✗ DEAD / CLAIMABLE: 7 (CRITICAL: 2 HIGH: 3 MEDIUM: 2)
✓ ACTIVE: 89
? UNKNOWN: 14
FINDINGS — Claimable / Dead Resources
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CRITICAL SEVERITY
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● some-researcher/bert-finetuned
Type: huggingface_from_pretrained
Category: AI/ML
File: myrepo/model.py:14
Status: ✗ DEAD — CLAIMABLE — HuggingFace account 'some-researcher' does not exist
Impact: Register HF account → push backdoored weights → model executes attacker code in production inference
Code: model = AutoModel.from_pretrained("some-researcher/bert-finetuned")
Machine-readable, suitable for CI integration. Exit codes:
0— No dead/claimable resources found1— Dead resources found (non-critical)2— CRITICAL findings found
Human-readable report for sharing with security teams.
# .github/workflows/trustaudit.yml
name: TrustAudit Supply Chain Scan
on:
schedule:
- cron: '0 6 * * 1' # Weekly Monday 6am
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install TrustAudit
run: pip install -r requirements.txt
- name: Run TrustAudit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python trustaudit.py --local . --output ./trustaudit_results
- name: Upload Results
uses: actions/upload-artifact@v3
if: always()
with:
name: trustaudit-results
path: ./trustaudit_results/| Pattern Name | Category | Severity | What It Finds |
|---|---|---|---|
github_action |
CI/CD | HIGH | uses: owner/repo@tag |
curl_bash |
CI/CD | CRITICAL | curl URL | bash |
wget_pipe |
CI/CD | CRITICAL | wget URL | sh |
github_actions_job_image |
CI/CD | HIGH | image: in workflow |
jenkins_agent_docker |
CI/CD | HIGH | docker.image(...) in Jenkinsfile |
dockerfile_from |
Container | HIGH | FROM image:tag |
docker_compose_image |
Container | HIGH | image: in compose |
npm_package |
Package Registry | HIGH | package.json deps |
npm_private_registry |
Package Registry | CRITICAL | .npmrc custom registry |
pypi_package |
Package Registry | HIGH | requirements.txt |
pip_install_inline |
Package Registry | HIGH | pip install in scripts |
pypi_index_url |
Package Registry | CRITICAL | Custom pip index |
go_module |
Package Registry | MEDIUM | go.mod requires |
go_replace |
Package Registry | HIGH | go.mod replace directives |
maven_repo |
Package Registry | MEDIUM | pom.xml repo URLs |
maven_dependency |
Package Registry | MEDIUM | pom.xml dependencies |
nuget_package |
Package Registry | MEDIUM | *.csproj references |
nuget_source |
Package Registry | HIGH | NuGet.Config sources |
cargo_dependency |
Package Registry | MEDIUM | Cargo.toml deps |
cargo_git_dep |
Package Registry | HIGH | Cargo.toml git sources |
rubygems_source |
Package Registry | MEDIUM | Gemfile gems |
rubygems_git |
Package Registry | HIGH | Gemfile git sources |
composer_package |
Package Registry | MEDIUM | composer.json deps |
terraform_module_git |
Infrastructure | CRITICAL | source = "git::..." |
terraform_module_registry |
Infrastructure | HIGH | source = "org/mod/provider" |
terraform_provider |
Infrastructure | MEDIUM | Provider sources |
cloudformation_template_url |
Infrastructure | CRITICAL | TemplateURL: |
cloudformation_s3_ref |
Infrastructure | HIGH | s3://bucket/ refs |
git_submodule |
Version Control | HIGH | .gitmodules URLs |
dvc_remote |
AI/ML | CRITICAL | .dvc/config remotes |
ssh_known_hosts_scan |
Infrastructure | MEDIUM | ssh-keyscan domain |
huggingface_from_pretrained |
AI/ML | CRITICAL | from_pretrained("user/model") |
huggingface_pipeline |
AI/ML | CRITICAL | pipeline(model="user/model") |
huggingface_dataset |
AI/ML | HIGH | load_dataset("user/dataset") |
pickle_download |
AI/ML | CRITICAL | wget URL.pkl |
model_file_download |
AI/ML | HIGH | wget URL.pt/.h5/.ckpt |
mlflow_tracking_uri |
AI/ML | HIGH | set_tracking_uri(...) |
mlflow_artifact |
AI/ML | HIGH | load_model(...) |
wandb_artifact |
AI/ML | HIGH | use_artifact(...) |
jupyter_wget |
AI/ML | HIGH | !wget in notebooks |
jupyter_pip |
AI/ML | HIGH | !pip install in notebooks |
homebrew_tap |
Developer Tooling | HIGH | brew tap org/repo |
homebrew_install |
Developer Tooling | MEDIUM | brew install tap/formula |
vscode_extension |
Developer Tooling | MEDIUM | .vscode/extensions.json |
install_script_url |
Developer Tooling | HIGH | curl .../install.sh |
webhook_url |
Webhook | HIGH | webhook_url: https://... |
generic_http_url |
External URL | LOW | HTTP URLs in scripts |
This tool is designed for:
- Authorised bug bounty research within defined program scope
- Internal security auditing of your own organisation
- Defensive scanning as part of a CI/CD security pipeline
- Security research with explicit written permission
Never:
- Scan organisations without explicit permission
- Register claimable resources before reporting them to the organisation
- Use findings for anything other than responsible disclosure
The current tool is a point-in-time scanner. A future phase will add:
- Scheduled re-scanning with diff alerting
- Domain expiry monitoring (WHOIS polling)
- GitHub account dormancy tracking
- npm/PyPI maintainer activity monitoring
- Alerting when a previously ACTIVE resource becomes DEAD
This shifts from reactive scanning to predictive research — the most valuable
approach because it catches resources before an attacker can claim them.
The Commands You Should Actually Use
For a first scan on any org — cleanest possible output
bashpython trustaudit.py --org target-org --token ghp_xxx
--min-confidence HIGH
--min-severity HIGH
This shows only findings where the API authoritatively confirmed the resource is gone. No maybes, no manual checks needed. This is your starting point every time.
For bug bounty — CI/CD first (highest payout potential)
bashpython trustaudit.py --org target-org --token ghp_xxx
--categories "CI/CD" "Container" "Kubernetes"
--min-confidence HIGH
--min-severity HIGH
--show-playbook
--show-playbook prints the exact PoC steps for every finding directly in the terminal. You get the exploit chain and remediation together.
For supply chain focused scan
bashpython trustaudit.py --org target-org --token ghp_xxx
--categories "Package Registry" "Infrastructure" "AI/ML"
--min-confidence MEDIUM
--min-severity HIGH
MEDIUM confidence here because private packages legitimately 404 on public PyPI — you want to see those too and verify manually.
For a large org (60+ repos) — wave approach
bash# Wave 1 — fast, 10 mins max, highest impact only
python trustaudit.py --org target-org --token ghp_xxx
--max-repos 20
--categories "CI/CD" "Container" "Infrastructure" "DNS/Subdomain"
--min-confidence HIGH
--workers 20
python trustaudit.py --org target-org --token ghp_xxx
--categories "Package Registry"
--min-confidence HIGH
--workers 15
python trustaudit.py --org target-org --token ghp_xxx
--categories "AI/ML" "Authentication" "Webhook" "Developer Tooling"
--min-confidence MEDIUM
To see only confirmed claimable + full PoC steps
bashpython trustaudit.py --org target-org --token ghp_xxx
--min-confidence HIGH
--min-severity MEDIUM
--show-playbook
--no-markdown
--no-markdown skips writing the file and just gives you terminal output with full PoC chains.
To generate an allowlist after your first scan bash# Step 1 — generate template python trustaudit.py --gen-allowlist
python trustaudit.py --org target-org --token ghp_xxx
--allowlist trustaudit-allowlist.json
--min-confidence HIGH
The Single Most Important Flag Combination
bashpython trustaudit.py --org target-org --token ghp_xxx
--min-confidence HIGH
--min-severity HIGH
--show-playbook
This is the one command that gives you only confirmed claimable resources at HIGH+ severity with the full step-by-step PoC already printed. Everything in the output is directly actionable — no manual verification, no noise filtering needed.