Skip to content

Repository files navigation

TrustAudit — Dangling Trust Scanner

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.


What It Finds

TrustAudit scans for the Dangling Trust vulnerability class across every layer of the modern software stack:

CI/CD & Build Systems

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

Container Layer

Pattern Risk
FROM ghcr.io/external-org/image:latest Org deleted → backdoored base image
Docker Compose external images Same as above

Package Registries

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

Infrastructure as Code

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

AI / ML Stack

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

Developer Tooling

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

Webhooks

Pattern Risk
webhook_url: https://hooks.service.com/... Domain expired → attacker receives all payloads

How It Classifies Results

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

The Key Technical Distinction

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

Installation

git clone https://github.com/your-org/trustaudit
cd trustaudit
pip install -r requirements.txt

Requirements

  • Python 3.8+
  • Git (for cloning repos)
  • GitHub token (recommended — otherwise 60 req/hour rate limit)

Usage

Scan a GitHub Organisation

python trustaudit.py --org netflix --token ghp_yourtoken

Scan a Single Repo

python trustaudit.py --repo https://github.com/org/repo --token ghp_yourtoken

Scan a Local Directory

python trustaudit.py --local /path/to/cloned/repo

Common Options

# 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_xxx

Environment Variable

export GITHUB_TOKEN=ghp_yourtoken
python trustaudit.py --org myorg

Large Org Workflow

For 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_yourtoken

This keeps peak disk usage lower while still producing one combined final report.

Optional Gemini Triage

TrustAudit can send the generated JSON/Markdown reports to Gemini for a second-pass security triage.

Default flow:

  1. Scan repos.
  2. Write JSON + Markdown reports.
  3. Send those reports to Gemini.
  4. 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 gemini

You 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_KEY

This is useful when you want an LLM to separate likely false positives from findings worth manual review without rescanning the repositories.


Output

TrustAudit produces three outputs:

1. Console (colour-coded)

╔════════════════════════════════════════════════════════════════╗
║          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")

2. JSON Report (trustaudit_org_timestamp.json)

Machine-readable, suitable for CI integration. Exit codes:

  • 0 — No dead/claimable resources found
  • 1 — Dead resources found (non-critical)
  • 2 — CRITICAL findings found

3. Markdown Report (trustaudit_org_timestamp.md)

Human-readable report for sharing with security teams.


CI/CD Integration

# .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/

All 47 Detection Patterns

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

Ethical Use

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

Future: Temporal Monitoring

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

Wave 2 — packages after reviewing wave 1

python trustaudit.py --org target-org --token ghp_xxx
--categories "Package Registry"
--min-confidence HIGH
--workers 15

Wave 3 — everything else

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

Edit trustaudit-allowlist.json to add your org's internal packages

Step 2 — rescan with allowlist applied

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages