Preflight untrusted repositories before opening them with an AI coding agent
Project description
repo-trust-scan
Preflight an untrusted repository before opening it with Claude Code, Codex, Cursor, Copilot, or another coding agent.
repo-trust-scan is a dependency-free Python CLI and GitHub Action that finds repository-controlled execution surfaces: automatic editor tasks, Claude and Copilot hooks, MCP server configurations, devcontainer lifecycle commands, package install hooks, escaping symlinks, hidden Unicode in agent instructions, download-and-execute chains, and credential-transfer patterns.
It is for developers who clone unfamiliar repositories, security reviewers who triage agent-ready projects, and maintainers who want transparent checks in CI.
uvx repo-trust-scan ./untrusted-repo
A clean result is not proof that a repository is safe. The scanner narrows manual review to known trust-boundary surfaces; it does not execute code, call a model, or claim to detect all malware or prompt injection.
Why this exists
Coding agents read repository instructions and may run project commands with the developer's local permissions. Normal developer conveniences—folder-open tasks, lifecycle scripts, hooks, and devcontainers—therefore become security-relevant before trust is established.
Most scanners answer “is this code vulnerable?” repo-trust-scan asks a narrower first question: what can this repository cause my tools or agent to execute, and what deserves review before I grant trust?
Install
Run once with uv, install as an isolated CLI, or use pip:
uvx repo-trust-scan ./untrusted-repo
pipx install repo-trust-scan
python -m pip install repo-trust-scan
Quick start
Scan without installing from a checkout:
python -m repo_trust_scan scan ../some-repository
The scan verb is optional:
repo-trust-scan ../some-repository
Machine-readable output:
repo-trust-scan scan . --format json
repo-trust-scan scan . --format sarif --output repo-trust-scan.sarif
Choose the CI failure threshold:
repo-trust-scan scan . --fail-on medium
repo-trust-scan scan . --fail-on none
Review an accepted finding without disabling other checks:
repo-trust-scan scan . --ignore RTS006
Use an explicit trusted policy:
repo-trust-scan ./target --config ../security-policy/repo-trust-scan.json
Create a baseline for already-reviewed findings, then surface only changes:
repo-trust-scan baseline ./target --output ../trusted-baselines/target.json
repo-trust-scan ./target --baseline ../trusted-baselines/target.json
Policy and baseline files are never discovered automatically. Keep them outside an untrusted target so the repository cannot suppress its own findings. See examples/trusted-policy.json and the suppression guidance.
Example output
repo-trust-scan scanned 3 text file(s) under /work/untrusted-repo
risk=45/100 critical=0 high=2 medium=1 low=1 skipped=0 suppressed=0
[HIGH] RTS005 .vscode/tasks.json:1 Task 'bootstrap' runs when the folder opens.
fix: Remove runOn=folderOpen or require an explicit, reviewed invocation.
[MEDIUM] RTS006 package.json:1 npm lifecycle script 'postinstall' runs during install or package preparation.
evidence: node scripts/bootstrap.js
Try the intentionally risky fixture:
repo-trust-scan examples/risky-repo --fail-on none
Checks
| ID | Severity | Check |
|---|---|---|
RTS001 |
critical | Symlink resolves outside the repository |
RTS002 |
high | Hidden or bidirectional Unicode in agent-facing instructions |
RTS003 |
high | Remote download piped or chained into an interpreter |
RTS004 |
high | Credential-like path combined with outbound transfer |
RTS005 |
high | VS Code task configured with runOn: folderOpen |
RTS006 |
medium | npm lifecycle script (preinstall, install, postinstall, prepare) |
RTS007 |
medium | Devcontainer lifecycle command |
RTS008 |
medium | Repository-provided Claude Code hook |
RTS009 |
medium | Encoded PowerShell or dynamic encoded shell execution |
RTS010 |
low | Repository Git hook or hook template |
RTS011 |
low | Agent instruction requests automatic command execution |
RTS012 |
medium | Repository-provided GitHub Copilot hook |
RTS013 |
medium | Repository-provided MCP server configuration |
RTS014 |
high | MCP server command launched through a general-purpose shell |
Run repo-trust-scan rules for the installed rule set. See docs/threat-model.md for scope and assumptions and docs/rules.md for interpretation guidance.
Safe workflow for an unfamiliar repository
- Clone it without opening the folder in an IDE or agent.
- Run
repo-trust-scan /path/to/repofrom a trusted directory. - Review findings in the file context; do not execute suggested commands just to investigate them.
- Inspect dependency manifests, lockfiles, build scripts, and binary artifacts with ecosystem-specific tools.
- Use a disposable VM or container when provenance is weak or behavior remains unclear.
- Grant agent permissions only after establishing trust.
GitHub Action
name: Repository trust surfaces
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
security-events: write
jobs:
trust-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.0
- uses: Uky0Yang/repo-trust-scan@v0.2.0
with:
path: .
fail-on: high
upload-sarif: "true"
The Action runs static reads only. It does not install repository dependencies, start containers, or invoke project scripts.
pre-commit
Run the scanner before commits without passing changed filenames as scan targets:
repos:
- repo: https://github.com/Uky0Yang/repo-trust-scan
rev: v0.2.0
hooks:
- id: repo-trust-scan
args: [--fail-on, high]
pre-commit is convenient for repositories you already trust. For unfamiliar repositories, scan from a trusted checkout location before installing any repository tooling.
Design principles
- Deterministic checks with a visible rule ID and remediation
- No model calls and no network calls by the scanner
- No repository code execution
- No runtime Python dependencies
- Stable JSON and SARIF for automation
- Conservative language: findings are review signals, not verdicts
Exit codes
0: scan completed and no finding met--fail-on1: one or more findings met the threshold2: the scan could not run
Contributing
False-positive reports and small, reproducible fixtures are especially useful. Read CONTRIBUTING.md before submitting a rule. Security-sensitive reports belong in SECURITY.md.
See CHANGELOG.md for release notes.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file repo_trust_scan-0.2.0.tar.gz.
File metadata
- Download URL: repo_trust_scan-0.2.0.tar.gz
- Upload date:
- Size: 21.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13a6eaf5a2a1230b809ab01d2a6524e8ed3a9068887aad0c0f3f586456645869
|
|
| MD5 |
5218511bb388657407400f5e2ac75e9d
|
|
| BLAKE2b-256 |
0f276fd489cc8a4743d24c4dd324283dad59617bcbbd9944742d6fbcf5033dca
|
Provenance
The following attestation bundles were made for repo_trust_scan-0.2.0.tar.gz:
Publisher:
release.yml on Uky0Yang/repo-trust-scan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repo_trust_scan-0.2.0.tar.gz -
Subject digest:
13a6eaf5a2a1230b809ab01d2a6524e8ed3a9068887aad0c0f3f586456645869 - Sigstore transparency entry: 2170636965
- Sigstore integration time:
-
Permalink:
Uky0Yang/repo-trust-scan@e10a8dabf25f75eadf6a42850cd46ab86e3421da -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Uky0Yang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e10a8dabf25f75eadf6a42850cd46ab86e3421da -
Trigger Event:
push
-
Statement type:
File details
Details for the file repo_trust_scan-0.2.0-py3-none-any.whl.
File metadata
- Download URL: repo_trust_scan-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85b2f730d03f8f11ad5cb24e029c3f0785731195d97f1e72d0414b845ec5b32b
|
|
| MD5 |
5473937271e2ce37461af42ad0279904
|
|
| BLAKE2b-256 |
f4d4cd53a87142cd8ca4c8cc34adbc57b103fc71575dc7ad48de498a0d96eacd
|
Provenance
The following attestation bundles were made for repo_trust_scan-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Uky0Yang/repo-trust-scan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repo_trust_scan-0.2.0-py3-none-any.whl -
Subject digest:
85b2f730d03f8f11ad5cb24e029c3f0785731195d97f1e72d0414b845ec5b32b - Sigstore transparency entry: 2170637080
- Sigstore integration time:
-
Permalink:
Uky0Yang/repo-trust-scan@e10a8dabf25f75eadf6a42850cd46ab86e3421da -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Uky0Yang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e10a8dabf25f75eadf6a42850cd46ab86e3421da -
Trigger Event:
push
-
Statement type: