repo + doctor: inspect a GitHub or local repository for trust, stack, and security signals before you clone, fork, install, or contribute.
Project description
repoc
repoc = repo doctor. A lightweight CLI that inspects a GitHub or local
repository before you clone, fork, install, or contribute to it.
It answers questions like:
- Can I trust this repository?
- What language and framework does it use?
- Is it still maintained?
- Are there suspicious scripts, install hooks, or possible secrets?
- Is it safe to run install/build commands?
Disclaimer:
repocdoes not prove that a repository is safe. It highlights suspicious patterns, metadata, and maintenance signals that deserve manual review.
Installation
pip install repoc-cli
The PyPI package is repoc-cli; the command it installs is repoc.
Or from source:
git clone https://github.com/GuillaumeYves/repoc
cd repoc
pip install -e ".[dev]"
Requires Python 3.11+.
Usage
# Inspect a public GitHub repository
repoc inspect owner/repo
# Or from a full URL
repoc inspect https://github.com/owner/repo
# Inspect the current working directory
repoc inspect . --local
# JSON output (machine-readable)
repoc inspect owner/repo --format json
# Markdown export
repoc inspect owner/repo --format markdown --output report.md
# Deeper scan — also inspects source files, not just manifests
repoc inspect owner/repo --deep
# Check pinned dependency versions against the OSV.dev vulnerability database
repoc inspect owner/repo --check-deps
# SARIF for GitHub code scanning
repoc inspect owner/repo --format sarif --output repoc.sarif
# Use as a CI gate — exit non-zero if a HIGH (or worse) security finding appears
repoc inspect owner/repo --fail-on high
Common options
| Option | Description |
|---|---|
--local |
Force the target to be interpreted as a local path. |
--format terminal|markdown|json|sarif |
Output format. Default terminal. sarif for GitHub code scanning. |
--output <path> |
Write the report to a file. |
--deep |
Inspect a wider set of source files (downloads a single repo tarball). |
--check-deps |
Look up pinned dependency versions against OSV.dev (network). |
--no-network |
Skip GitHub API calls (local mode only). |
--max-files N |
Cap on how many files to load. Default 500. |
--max-file-size N |
Skip files bigger than N bytes. Default 200000. |
--fail-on low|medium|high|critical |
Exit non-zero (code 1) if a security finding meets/exceeds this severity. |
--github-token <token> |
GitHub token (or set GITHUB_TOKEN). Used in memory only. |
--token-stdin |
Read a token from stdin so it never lands in shell history / argv. |
--login |
One-time GitHub device-code login for this run. Nothing is stored. |
Exit codes: 0 ok · 1 --fail-on gate tripped · 2 bad target/usage ·
3 GitHub/network error or rate limit · 4 authentication error.
GitHub authentication (private repos)
repoc never writes a credential to disk, a keyring, or any config file — a token lives in memory for a single run and is discarded on exit. You can supply one in whichever way fits your trust model:
# 1. Environment variable / flag (raises the rate limit; reads private repos)
export GITHUB_TOKEN=github_pat_xxx
repoc inspect owner/private-repo
# 2. Pipe a token via stdin — keeps it out of shell history and the process list
echo "$GITHUB_TOKEN" | repoc inspect owner/private-repo --token-stdin
# 3. One-time browser device-code login (nothing is persisted)
repoc inspect owner/private-repo --login
For --login, set REPOC_GITHUB_CLIENT_ID to a public GitHub OAuth App client
id (enable Device Flow on the app). The client id is not a secret. If you'd
rather not register an app, use a fine-grained, read-only PAT with --token-stdin.
Use in CI / pre-commit
# .github/workflows/audit.yml
- run: pip install repoc
- run: repoc inspect ${{ github.repository }} --fail-on high
The gate only counts security findings (committed secrets, install hooks, risky code patterns). Maintenance, documentation, and scan-coverage notes never fail it.
Sample output
# Repoc Report: https://github.com/owner/repo
## Verdict
- Risk level: **Medium**
- Trust score: **72/100**
- Project type: Backend API
- Primary language: Python
- Detected stack: FastAPI, SQLAlchemy, Pydantic
## Summary
owner/repo appears to be a Backend API written in Python using FastAPI,
SQLAlchemy, Pydantic. The scan produced low-severity findings only.
...
What it detects
repoc has two depth layers. Code-logic analysis (the security rule packs that read your source) currently covers 7 ecosystems. Identification (language + framework + secret scanning) is broader. Secret scanning runs on every text file regardless of language.
Statically analyzed (security rule packs)
| Ecosystem | What the rules look for |
|---|---|
| Python | eval/exec, os.system, subprocess(shell=True), pickle/marshal.loads, yaml.load without SafeLoader, verify=False, base64, raw sockets |
| JavaScript / TypeScript | eval, new Function, child_process.exec/spawn, fs.rm/unlink, DOM-XSS sinks (innerHTML, document.write), setTimeout("string"), base64, npm install hooks (preinstall/install/postinstall/prepare) |
| PHP | eval, command exec (system/exec/shell_exec/passthru/popen/proc_open), unserialize, create_function, assert("…"), extract($_GET), dynamic include/require and request-controlled LFI/RFI, base64 |
| Ruby | eval, system/exec/backticks, Open3.capture, Marshal.load, YAML.load, Base64.decode64, Net::HTTP |
| Shell | curl|bash, sudo, rm -rf $…, base64 -d, eval, insecure TLS (curl -k), reverse-shell shapes (nc -e, /dev/tcp/…) |
| Dockerfile / Compose | USER root, ADD http://…, curl|bash, --privileged, :latest base image |
| GitHub Actions | pull_request_target, secret expansion, curl|bash, sudo, unpinned actions, script injection via untrusted ${{ github.event.* }} |
Rules run against comment/string-masked source (see Keeping signal high below), so they're tuned to flag real calls, not mentions in comments or strings.
Identified but not yet code-analyzed
repoc detects Go, Rust, Java/Kotlin, C#, C/C++ (and labels ~25 languages in
total) and parses their dependency manifests for frameworks (Gin/Fiber/Cobra,
Actix/Axum/Tokio, Spring/Hibernate, Laravel/Symfony, …). These files are still
secret-scanned, but dedicated code-logic rule packs for them are planned for
future releases — today a risky call in a .go/.java/.rs file is not flagged.
Secret scanning (all languages)
Regex-based and conservative: GitHub PATs, AWS access keys, PEM private keys,
JWTs, DB URLs with credentials, Slack/Discord webhooks, Stripe secret keys,
committed .env files. Matched values are redacted.
Dependency vulnerabilities (--check-deps)
Opt-in. repoc parses pinned versions from lockfiles (requirements.txt,
poetry.lock, Pipfile.lock, package-lock.json, Gemfile.lock, Cargo.lock,
composer.lock, go.mod) and batch-queries OSV.dev for known
advisories. Findings are capped at HIGH severity — repoc can't assess whether a
(possibly transitive) vuln is reachable, so it never escalates a whole repo to
Critical on a dependency alone. Needs network; skipped under --no-network.
Keeping signal high
- Comment/string aware. Code-pattern rules run against a copy of each file
with comments and string literals blanked out, so an
eval(mentioned in a docstring or a# curl … | bashexample line is not reported. (Secret rules still run on the raw text — secrets live inside strings.) - Path context. Findings in
tests/,examples/,fixtures/,docs/,vendor/, and*.example/*.samplefiles are down-ranked one severity tier — a token in a test fixture is rarely a live leak. - Inline suppression. Add
# repoc: ignore,# repoc: ignore PY001, or# repoc: ignore-file(any comment syntax) to silence a known-safe match.
Scan coverage
Every report states how many files were actually inspected. If the --max-files
cap is hit, or oversized/binary files are skipped, repoc emits explicit
COV findings and a Partial scan banner — a capped scan can never look like
a clean bill of health.
Default remote scans are metadata-only.
repoc inspect owner/reporeads manifests, lockfiles, install hooks, workflows, Dockerfiles, and docs — it does not download the source tree. When a repo has source files that weren't inspected, repoc says so (COV003) and tells you to add--deep, which downloads the repo once and scans the code. Local scans (--local) always read the full source tree.
Maintenance & docs
Repository age, archive status, license presence, open-issue backlog, plus
the usual suspects in the repo root: README, LICENSE, CONTRIBUTING,
SECURITY, CODE_OF_CONDUCT, CHANGELOG, docs/, examples/.
Scoring
The trust score is a weighted average:
| Area | Weight |
|---|---|
| Security | 35% |
| Maintenance | 25% |
| Documentation | 20% |
| Popularity | 10% |
| Structure | 10% |
Findings are weighted by category. Committed secrets and npm install hooks are
treated as "stop and look" signals and weigh heavily; risky code patterns
(eval, subprocess, curl | bash) are "worth reviewing" signals that barely
move the score on their own and taper with repetition — twenty subprocess
calls in a large codebase do not zero the score the way twenty private keys
would.
Risk levels are Low, Medium, High, Critical, Unknown. Any CRITICAL
finding, or a HIGH/CRITICAL committed secret or install hook, escalates the risk
on its own; a lone HIGH code pattern (e.g. one eval) does not. (Popularity is
excluded for local scans, where stars/forks are unknown.)
The full breakdown is included in every report so the score is auditable.
Development
git clone https://github.com/GuillaumeYves/repoc
cd repoc
pip install -e ".[dev]"
ruff check .
pytest
The project layout:
repoc/
cli.py Typer entry point
auth.py Ephemeral GitHub auth (device flow, stdin token)
github_client.py Minimal GitHub REST client + tarball fetch
models.py Pydantic models
scoring.py Score aggregation + risk classification
analyzers/ One module per analysis dimension (incl. coverage)
rules/ Regex rule packs + comment/string masking
renderers/ terminal / markdown / json
tests/
.github/workflows/ ci.yml, release.yml
Releasing
Bump __version__ in repoc/__init__.py, commit, then:
git tag v1.0.0
git push origin v1.0.0
The release workflow builds wheels, creates a GitHub release, and publishes to PyPI via Trusted Publishing.
Roadmap
- Code-logic rule packs for Go, Rust, Java, and C# — the languages repoc identifies today but doesn't yet analyze for risky calls.
- Heuristics for typosquat detection on package names.
- Resolve
--check-depsfor range-only manifests (e.g. a barepackage.json). - Pluggable rule packs (drop-in
.toml/.pyrules). - Caching layer for repeated GitHub queries.
License
MIT — see LICENSE.
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 repoc_cli-1.0.0.tar.gz.
File metadata
- Download URL: repoc_cli-1.0.0.tar.gz
- Upload date:
- Size: 70.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5170e17d9a0e3b74d04f3ffb3d1061fe5b1712af88a93358eb7ba574a50558b2
|
|
| MD5 |
57e52cbbd298fd8485b91d0f15eea2a0
|
|
| BLAKE2b-256 |
c5e75d34163886d847e63ed4afcf80274dfa9ba770ee9fe07b64bf688152ccdc
|
Provenance
The following attestation bundles were made for repoc_cli-1.0.0.tar.gz:
Publisher:
release.yml on GuillaumeYves/repoc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoc_cli-1.0.0.tar.gz -
Subject digest:
5170e17d9a0e3b74d04f3ffb3d1061fe5b1712af88a93358eb7ba574a50558b2 - Sigstore transparency entry: 1691282629
- Sigstore integration time:
-
Permalink:
GuillaumeYves/repoc@9e372e5fc73b5248bf88d6d0171f508c2e5f0ffb -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/GuillaumeYves
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9e372e5fc73b5248bf88d6d0171f508c2e5f0ffb -
Trigger Event:
push
-
Statement type:
File details
Details for the file repoc_cli-1.0.0-py3-none-any.whl.
File metadata
- Download URL: repoc_cli-1.0.0-py3-none-any.whl
- Upload date:
- Size: 68.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90d7c06adaa9da5b9ead1c38951a442ba8e481ac5372044edc3224de4d05f237
|
|
| MD5 |
a0d16b837a537f31035cb8718c9068fa
|
|
| BLAKE2b-256 |
6b37bb7ab13a2791683f0b032371f4b1a5151130fd4269c047e95f8c0b462632
|
Provenance
The following attestation bundles were made for repoc_cli-1.0.0-py3-none-any.whl:
Publisher:
release.yml on GuillaumeYves/repoc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoc_cli-1.0.0-py3-none-any.whl -
Subject digest:
90d7c06adaa9da5b9ead1c38951a442ba8e481ac5372044edc3224de4d05f237 - Sigstore transparency entry: 1691282642
- Sigstore integration time:
-
Permalink:
GuillaumeYves/repoc@9e372e5fc73b5248bf88d6d0171f508c2e5f0ffb -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/GuillaumeYves
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9e372e5fc73b5248bf88d6d0171f508c2e5f0ffb -
Trigger Event:
push
-
Statement type: