CLI scanner that audits a public GitHub repository or organization and produces structured JSON and HTML reports with health, maintainability, quality, security, and dependency signals. Part of the inspect-software auditing/certification platform.
The scanner does not clone or execute target-repository code. It reads public GitHub API data and raw manifest files, and it queries supported public package registries. OpenSSF Scorecard may run locally against the public repository as a best-effort security input.
uv sync # or: pip install -e .inspect-scan https://github.com/pallets/flask
inspect-scan pallets/flask -o report.json
inspect-scan git@github.com:pallets/flask.git --compact
# Single-file HTML report (score-focused, human-readable)
inspect-scan pallets/flask -o report.json --html report.html
# Re-render HTML from a previously saved JSON report — no network needed
inspect-scan report.json --html report.html
# Scan an organization (profile + repository portfolio)
inspect-scan psf
inspect-scan https://github.com/python
# Store JSON + HTML under ./storage with standardized names
inspect-scan pallets/flask --storage
inspect-scan psf --storage D:/audit-reports
# Enable/disable parts of the scoring methodology (see below)
inspect-scan pallets/flask --disable-category security --disable-metric popularity
inspect-scan pallets/flask --config scan-config.json --html report.htmlAny part of the methodology — a component, a metric, or a whole category — can
be switched off for a scan. Disabled items are removed from scoring and the
remaining weights renormalized (never counted as zero), so scores stay on
the 1–100 scale. The configuration is embedded in the report (config) and
summarized in a Scan configuration section of the HTML.
inspect-scan pallets/flask --disable-category security # drop a category
inspect-scan pallets/flask --disable-metric popularity # drop a metric
inspect-scan pallets/flask --disable-component documentation:Wiki # drop a component
inspect-scan pallets/flask --config scan-config.json # from a file (+ flags merge on top)Category/metric keys and component names are listed in docs/metrics.md.
The Security metric is backed by OpenSSF Scorecard, a neutral, tool-agnostic security standard — so a project earns credit for the practice (any dependency-update tool, any SAST, signed releases, least- privilege workflow tokens, no known-vulnerable deps…), not for a specific vendor's config file. Checks Scorecard can't determine are excluded from the score, never counted as zero.
This needs the scorecard CLI on your PATH:
# e.g. via Go, Homebrew, or a release binary — see the Scorecard README
go install github.com/ossf/scorecard/v5@latestThe scan reuses the same GitHub token it already resolves. Scorecard is
best-effort: if the binary is missing, times out, or fails, the security metric
falls back to coarse file checks and a warning is emitted. Skip it with
--no-scorecard (faster), or disable the whole metric with
--disable-metric security_posture.
--storage [DIR] writes both report formats into a standardized layout:
storage/
repos/<owner>__<repo>.json|.html e.g. repos/pallets__flask.html
orgs/<login>.json|.html e.g. orgs/psf.html
Names are sanitized to filesystem-safe characters and lowercased. The storage
root comes from (highest precedence first): the --storage DIR argument, the
SCANNER_STORAGE variable (environment or .env), then ./storage. Setting
SCANNER_STORAGE enables storage even without the flag. One file pair per
target — a rescan overwrites.
Unauthenticated GitHub API access is limited to 60 requests/hour (a scan uses ~12). Provide a token to raise the limit to 5000/hour — a fine-grained token with public-repo read access is enough. The scanner resolves it from, in order of precedence:
--tokenCLI argumentGITHUB_TOKENorGH_TOKENenvironment variableGITHUB_TOKENorGH_TOKENin a.envfile in the working directory
For workers that scan many repositories, set GITHUB_TOKENS to a
comma-separated list of secondary PATs. The primary GITHUB_TOKEN remains
first; when GitHub reports a rate limit, the scanner retries the request with
the next token. Token values are never logged.
(see .env.example; .env is gitignored)
inspect-scan pallets/flask --token ghp_... # explicit
export GITHUB_TOKEN=ghp_... # environment
cp .env.example .env # or a local .env fileThe report has two strictly separated layers (schema defined as Pydantic
models in src/scanner/models.py):
-
data— raw facts observed from the GitHub API: repo metadata, the owning account's profile (organization or user), popularity, commit/release activity, contributors and bus factor, issue/PR counts, community profile, file-tree signals (CI, tests, linting, security policy, lockfiles, dependency manifests, and the declared dependency list parsed straight from those manifests — name + version constraint, no registry lookup yet), AI-readiness signals (agent-instruction files, bootstrap/typecheck/ container configs, machine-readable interfaces), and — for repos that publish a package — registry facts from PyPI / npm / Packagist / crates.io / RubyGems / Hex (downloads, versions, deprecation). No judgement, no scoring. -
metrics— standardized scores, each an integer 1..100 mapped to a band (critical/at_risk/weak/moderate/good/excellent/exceptional). Fifteen metrics grouped into weighted categories, each with its own rolled-up score, plus a weightedoverallthat is additionally calibrated against the distribution of the public record (seesrc/scanner/calibration.py):Category Weight Metrics Vitality 0.21 development_activity, release_discipline Community & Adoption 0.17 popularity, community_health, ecosystem_adoption Sustainability & Governance 0.23 maintainer_resilience, responsiveness, stewardship, package_maintenance Engineering Quality 0.19 engineering_practices, documentation Security 0.16 security_posture × high_risk_jurisdiction_exposure AI Readiness 0.04 ai_agent_context, ai_verify_loop, ai_code_legibility, ai_interfaces stewardship scores who backs the repo: organization-owned projects (especially with a GitHub-verified domain and reach) score higher than single-personal-account projects. ecosystem_adoption and package_maintenance read real package-registry data (downloads, publish recency, deprecation) for repos that publish to PyPI / npm / Packagist / crates.io / RubyGems / Hex — see docs/ecosystems.md. security_posture is backed by OpenSSF Scorecard (tool-agnostic; see below). high_risk_jurisdiction_exposure is the internal key for an offline, privacy-preserving high-risk jurisdiction exposure signal. It uses high-confidence self-published public profile evidence within the Russia, Iran, and North Korea policy scope; it adjusts Security and overall rating and never improves either. AI Readiness carries a deliberately small 4% weight — sized, together with the calibration curve's top saturation, so a repository with no agent tooling can still score 100/100. Every metric echoes the raw
inputsit was computed from.
The --html flag renders the report into a single-file HTML page focused on
the score: an overall gauge with the standardized band scale, a category radar
chart, a dedicated Ownership section, and metric cards grouped by category
with plain-language explanations, per-criterion pass/fail breakdowns, and the
exact inputs used. Styling/charts/icons load from CDNs (Chart.js, Lucide,
Google Fonts); the page degrades gracefully offline.
Both layers are independently versioned (schema_version for the structure,
metrics_version for the scoring methodology). Full documentation:
- docs/report-schema.md — field-by-field schema
- docs/metrics.md — bands, formulas, weights, worked example
- docs/ecosystems.md — package-registry integration & feature matrix
Notes on semantics:
- File-based signals are heuristics from the git tree of the default branch; they indicate presence, not quality.
- Metrics are signals, not warranties — publicly visible practices, not a code audit. Missing data is excluded and renormalized, never scored as zero.
uv sync --extra dev
uv run pytest0— report produced (warnings, if any, go to stderr)1— GitHub API failure (rate limit, network, repo not found)2— invalid input (unparseable repo URL)
Pull requests are welcome — read CONTRIBUTING.md first. It explains one thing that is not obvious from the outside: this repository is published from a private workspace, so a merged change comes back with a different commit hash. A first pull request also needs CLA.md signed, which is a licence grant, not a copyright assignment.
Participation is covered by the Code of Conduct.
The scanner never clones or executes the code it audits, but it does parse untrusted input from repositories and registries. Report vulnerabilities privately — see SECURITY.md, not a public issue.
GNU Affero General Public License v3.0 or later — see LICENSE.
The network clause is the point: run a modified scanner as a service and you owe its users your changes. Running it on your own machine, in your CI, or against your own dependencies carries no such obligation.
A commercial licence is available for use AGPL does not suit — mail@inspect.software.
src/scanner/data/jurisdiction_places.json is derived from GeoNames data under
CC BY 4.0; attribution and the details are in NOTICE.