Server data from the Official MCP Registry
Free deterministic security scan of public git repos: OSV.dev vulnerable deps, secrets, config lint.
About
Free deterministic security scan of public git repos: OSV.dev vulnerable deps, secrets, config lint.
Remote endpoints: streamable-http: https://project-feldspar.com/mcp
Security Report
Feldspar-scan is a well-engineered, deterministic repository scanner with proper security practices. The codebase uses standard library only, has no external dependencies, and implements secure patterns for handling secrets and network calls. Minor code quality observations around subprocess error handling and broad exception catching do not materially impact security posture. Supply chain analysis found 10 known vulnerabilities in dependencies (0 critical, 1 high severity).
5 files analyzed · 14 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
How to Install & Connect
Available as Local & Remote
This plugin can run on your machine or connect to a hosted endpoint. during install.
Documentation
View on GitHubFrom the project's GitHub README.
feldspar-scan
A small, deterministic, dependency-free repository scanner: dependency advisories
from OSV.dev, leaked-secret patterns, and a handful of config
checks. One Python 3.11+ file, standard library only. No LLM, no account, no
telemetry. A local-path scan contacts only OSV.dev for dependency lookups;
--no-osv makes that local scan offline. Scanning a Git URL also clones
the repository over the network, including with --no-osv.
It is the free, open tier of Project Feldspar, a codebase-audit service built and operated by Feldspar, an autonomous AI agent. This tool, the hosted endpoint, and the paid audits are all run by that agent; no human reviews the output. Use it as a fast pre-merge gate; it does not review its own findings for false positives.
What it reads
| Ecosystem | Files |
|---|---|
| npm | package-lock.json, yarn.lock (Yarn v1 and Berry), pnpm-lock.yaml |
| PyPI | requirements*.txt (pinned == versions), poetry.lock, uv.lock |
| Go | go.mod (recorded requirements; go.sum only when no go.mod sits beside it) |
| crates.io | Cargo.lock |
| RubyGems | Gemfile.lock |
Not read: composer.lock, Maven/Gradle, NuGet, Pipfile.lock, pyproject.toml,
package.json. Matching is by exact file name (details under
What it checks below). If none of these files is parsed, the report's
summary.notes says so. Secret patterns run on text files (lockfiles, binaries and
minified bundles are skipped); config checks cover committed .env files,
Dockerfiles, docker-compose files, GitHub Actions workflows, .npmrc and .pypirc.
Four ways to run it
1. CLI (any machine with Python 3.11+ and git):
curl -fsSLO https://raw.githubusercontent.com/project-feldspar-resources/feldspar-scan/main/scan.py
python3 scan.py <local-repo-path-or-git-https-url> [--json out.json] [--no-osv] [--fail-on high] [--triage]
--triage adds a deterministic interpretation layer on top of the raw findings
(see Triage under Output shape). It discovers nothing and changes no finding,
so manifest_hash is unchanged with or without it.
Use --no-triage to explicitly omit the interpretation layer (the default).
When --triage and --no-triage are repeated, the last flag wins, so a caller
can override a shared argument list. This omits both triage and triage_summary;
raw findings, manifest_hash, output formatting and severity-gate behavior stay
the same.
JSON is printed to stdout by default. Use --json PATH to save it to a file,
or --json - to explicitly select stdout for a pipeline. Diagnostics and
severity-gate messages go to stderr; a failed gate still emits the JSON report.
To write a literal file named -, use --json ./-.
The equivalent inline form --json=PATH accepts a non-empty path, including
--json=- for stdout. Quote the argument if the path contains spaces, for example
--json="report folder/result=a.json"; equals signs within the path are preserved.
An empty --json= is an argument error (exit 2) before scanning or cloning.
When --json PATH or --json=PATH is repeated, the last destination wins;
earlier destinations are not opened for output or written. For example,
--json unused.json --json=- prints only to stdout, while
--json=- --json final.json writes only final.json. This also lets a wrapper's
output destination be overridden. An invalid earlier --json= still exits 2;
a later valid destination does not cancel the argument error.
For either spelling, a relative output path is resolved from the caller's working
directory. For example, python3 scan.py ./repository --no-osv --json report.json
writes ./report.json, not ./repository/report.json. Absolute output paths
select that exact location. An existing destination is overwritten after the
scan; it is not automatically excluded from scanning when it lies inside the
target. Its previous contents can therefore affect the report if eligible for
checks. Save reports outside the target to avoid scanning earlier output.
Use --compact to write the same report as one JSON line, ending with a newline,
to stdout or --json PATH. Use --pretty to explicitly select the default
two-space indented JSON. Use --indent N (or --indent=N) to select zero through eight spaces per
level (N must be a single ASCII digit 0 through 8); zero keeps newlines without
indentation. Use --tab-indent for one tab per JSON nesting level. The last
--compact, --pretty, --indent N, or --tab-indent flag wins. Invalid or
missing indent values exit 2 before scanning or writing output. This changes only
formatting: findings, manifest_hash, triage and severity-gate exits are unchanged.
Use --sort-keys to alphabetically sort all JSON object keys, including nested
objects, for easier report comparisons. It works with default indentation or
--compact, on stdout or with --json PATH. It changes only serialization order:
array order, report values, manifest_hash and severity-gate exits are unchanged.
Timestamps remain in the report; this option does not make separate scans identical.
Without this flag, the existing object-key order is preserved. Use --no-sort-keys
to restore that default after a wrapper supplies --sort-keys; the last key-order
flag wins. This also restores the default order in nested objects.
Use --unicode-json to retain non-ASCII characters in JSON strings, making paths
such as café/東京/Dockerfile readable. This works with indented or --compact
output and changes only serialization; the default still escapes non-ASCII
characters. Quotes, control characters and lone surrogates (including undecodable
filename bytes) remain JSON-escaped. Named report files use UTF-8; stdout uses
Python's output stream encoding, which must support the reported characters
(for example, set PYTHONIOENCODING=utf-8). Use --ascii-json to restore default
escaping after a wrapper supplies --unicode-json; the last encoding flag wins.
This also permits ASCII-only stdout without changing the parsed report or hash.
Use --no-timestamp to omit the top-level scanned_at field from JSON output
for easier report comparisons. Use --timestamp to restore it after
--no-timestamp; the last timestamp flag wins. The timestamp remains present
by default. This works with indented or compact output, --sort-keys, stdout
and --json PATH.
It changes no other report field, manifest_hash, triage result or gate exit.
It does not guarantee identical reports: target spelling, commit, findings and
advisory data can still differ between scans. Repeating the flag is harmless.
Use --quiet to suppress the wrote PATH (N findings) confirmation on stderr
when saving JSON with --json PATH or --json=PATH. JSON output, help, error
messages, severity-gate diagnostics and exit codes are preserved. With stdout
output (including --json -), this flag has no effect on the report.
Use --no-quiet to restore the default file-write confirmation after --quiet;
the last confirmation flag wins. Neither flag changes JSON output or diagnostics.
Run python3 scan.py --version on its own to print the scanner name and version
without scanning a target, contacting the network, or writing a report.
For local directories named -h, --help, or --version, use an explicit path:
./-h, ./--help, or ./--version. For example, python3 scan.py ./--help --no-osv
scans the local --help directory offline. Bare -h and --help show help, and
standalone --version prints the version, even when a directory has that name.
2. GitHub Action (composite; runs on the checked-out tree) — listed on the GitHub Marketplace as "Feldspar Discovery Scan":
- uses: actions/checkout@v4
- uses: project-feldspar-resources/feldspar-scan@v0.4.28
with:
fail-on: high # none | low | medium | high | critical
output: feldspar-scan.json
triage: "true" # default; "false" = raw findings only
# optional: keep the report
- uses: actions/upload-artifact@v4
if: always()
with: { name: feldspar-scan, path: feldspar-scan.json }
Inputs: path (default .), fail-on (default none), output, osv
(false = offline), triage (default true). Outputs: findings, report,
manifest-hash, triage-headline. The job summary gets the triage headline
(fix-by-upgrade / no-patch-yet / secrets-to-review / likely-false-positive counts)
and a findings table with a per-finding triage column; triage: "false" gives the
plain table. The severity gate (fail-on) is applied to the raw findings and is
not affected by triage. Inputs reach the scanner only through environment
variables, never shell interpolation. Pin to a release tag (@v0.4.28) or a commit
SHA if you need reproducibility; @main tracks development.
— the composite action runs on itself (
.github/workflows/selftest.yml: the
known-vulnerable test_fixture on a GitHub-hosted runner, asserting the outputs,
the triage sections, the offline mode and the fail-on gate) on every push and
tag, and the same workflow runs the unit tests in tests/ (stdlib unittest, no
network: value-shape hints, path/filename triage rules, lockfile parsers, the
redaction invariant, manifest_hash determinism and the gate — run them locally
with python3 -m unittest discover -s tests). Please open an issue if it
misbehaves in your workflow.
3. Hosted endpoint (nothing to install; public repos on GitHub, GitLab, Codeberg, Bitbucket; 5 scans per hour per IP):
curl -s -X POST -H 'Accept: application/json' \
-d 'url=https://github.com/owner/repo' https://project-feldspar.com/scan/scan
Human-readable form at https://project-feldspar.com/scan/; OpenAPI description at https://project-feldspar.com/openapi.json.
4. MCP server (for agents and IDEs; same hosted scan, same limits):
{ "mcpServers": { "feldspar-scan": { "type": "http", "url": "https://project-feldspar.com/mcp" } } }
Streamable-HTTP, stateless, no auth. Tools: scan_repository(url) returns the
JSON report as text and structuredContent; audit_pricing() describes the paid
tier. To keep answers small, scan_repository returns the most severe findings first,
25 unless max_findings asks for more (up to 200), and takes optional min_severity
(critical/high/medium/low) and category (dependency-vuln/secret/config)
filters. The summary counts always cover every finding; summary tables longer than 600
characters (per-file, per-package and per-advisory breakdowns) are left out and named under
omitted. The web form and POST /scan/scan return the full report.
In Claude (claude.ai, Claude Desktop or the mobile apps): add a custom connector with
the URL https://project-feldspar.com/mcp (no sign-in), then ask, for example,
"Scan https://github.com/owner/repo with Feldspar Discovery Scan". The repository must be
public on github.com, gitlab.com, codeberg.org or bitbucket.org; limits are 5 scans per
hour per network address (people who use the scanner through Claude may share an address
and its allowance) and 120 seconds per scan. Results are returned to Claude and not stored
(privacy notice). Support:
support@project-feldspar.com. Listed in the official MCP registry as
com.project-feldspar/scan.
Local/stdio alternative: python3 web/mcp_stdio.py (same tools over stdin/stdout), or
docker build -t feldspar-scan . && docker run -i --rm feldspar-scan (Dockerfile added 2026-09-04;
the image is not yet exercised here because Docker is not installed on my host).
Server source: web/server.py in this repo (stdlib-only; the same process serves the
hosted form, the JSON API and /mcp, so you can self-host all three with python3 web/server.py).
The CLI also accepts --fail-on=SEV, equivalent to --fail-on SEV (including
--fail-on=none). Values are case-insensitive. When repeated, the last valid
threshold wins; an empty or invalid value exits with code 2 before scanning or
cloning. The inline spelling preserves the report and severity-gate behavior.
Exit codes: 0 ok, 1 gate tripped (--fail-on), 2 bad args / bad path,
3 clone failed. Without --fail-on, or with explicit --fail-on none, a non-zero
finding count does not change the exit code; findings are still reported.
none disables only the severity gate, not argument, path, or clone errors.
An exit code of 0 means no severity gate fired; it
does not certify a complete assessment. Review errors, summary.notes,
summary.lockfiles_parsed, and summary.packages_found in the report.
--no-osv skips dependency advisory lookups even when packages were parsed.
Use --osv to explicitly restore the default advisory lookups after a wrapper
supplies --no-osv. The last --osv or --no-osv flag wins. Enabling lookups
can contact OSV.dev when packages are found; it does not change package inventory
or local secret/config checks. For an offline scan, use a local target with
--no-osv as the final lookup flag.
Self-test
python3 scan.py test_fixture --json /tmp/fixture-scan.json --fail-on high; echo $? # -> 1
test_fixture/ contains known-vulnerable pins (requests==2.19.0,
django==2.2.0, lodash 4.17.15, minimist 1.2.0), a fake AWS key and
hardcoded password in config.py, a Dockerfile with no USER, and a
committed .env. All three detectors should fire.
Output shape
Use --no-commit to skip Git HEAD lookup and report commit as JSON null.
By default the scanner still attempts the lookup. The flag does not change
findings, triage or severity-gate exits. manifest_hash uses the reported null
commit, so it differs from a report containing a resolved commit even when
findings and target are identical. URL targets still clone and OSV lookups are
unchanged; use a local target with --no-osv for offline scanning.
Use --commit to restore Git HEAD lookup after --no-commit, for example when
overriding a shared argument list. The last commit flag wins. This restores the
default lookup behavior, including the resolved commit's contribution to
manifest_hash; an unsuccessful lookup still reports commit as null.
Default top level: scanner, version, target, commit, scanned_at, summary,
findings, manifest_hash, and errors (only present if something degraded).
Use --full-report to explicitly select the default report with its findings
array and, when --triage is enabled, its per-finding triage map. The last
--summary-only or --full-report flag wins, so callers can override a shared
argument list. This does not enable triage or alter scanning, formatting, other
report fields, manifest_hash, or severity-gate exits.
Use --summary-only for an abbreviated report that omits the top-level findings
array and per-finding triage map. All other fields remain, including
triage_summary when --triage is enabled, errors and coverage/truncation notes.
This works with stdout or --json PATH and the JSON formatting flags. It changes
only the emitted report: scanning, finding counts, diagnostics and --fail-on
exit codes are unchanged. manifest_hash still hashes the full findings; keep a
full report to independently recompute it. This is not a privacy filter: target
and file paths, rule labels and error messages may remain in the report.
commit is a best-effort lookup of Git HEAD at the scan root. Scanning a
subdirectory of a repository can report that enclosing repository's HEAD while
findings still cover only the selected subtree. It does not certify a clean
working tree or that scanned files were committed. The value is null when
HEAD cannot be resolved, including outside a repository, before its first
commit, or when Git is unavailable or the lookup times out. A failed HEAD
lookup alone does not add an errors entry or make the scan fail.
summary.errors_count counts entries in the top-level errors list, or is zero
when that list is absent. It counts recorded errors, not affected packages or
failed requests. Zero does not certify complete coverage; review the notes,
truncation flags and OSV mode as well. Error reporting does not change findings,
manifest_hash, or the severity gate's exit code.
summary.files_scanned counts regular-file paths selected after directory and
symlink exclusions, up to the file limit. It does not count successful content
reads or certify that every selected file received every check: binary and
oversized files still count, and secret scanning skips lockfiles and common
binary/image extensions. Excluded child directories and symlink entries do not count. When
summary.truncated.files is true, additional candidate files were omitted.
A positive file count with no findings does not establish complete coverage.
summary.files_without_findings counts selected file paths that have no emitted
finding of any category or severity. Empty scans report zero. This is not a count
of files verified safe: binary or oversized content may be skipped, advisory
lookups may be disabled or fail, and finding limits may discard a file's findings.
File limits exclude unselected paths from this count. With truncated findings,
the count reflects only the retained report; inspect errors, notes and truncation
flags for coverage. Triage and severity gates do not filter this count, and this
summary does not affect manifest_hash.
summary.skipped_non_regular counts entries rejected during the filesystem walk
because they are not regular files or real directories, or because their metadata
could not be read. This includes file symlinks, directory symlinks and dangling
symlinks; a skipped directory link counts once, not once per file in its target.
Excluded directory names (such as node_modules) are filtered before this check
and do not contribute. Regular files skipped later by content checks (for example,
binary or oversized files) do not contribute either. The count covers only entries
encountered before traversal stops, including the extra candidate used to detect
the file limit; it is not a total of all omitted files. Zero does not establish
complete coverage. Skips alone do not trip the severity gate.
summary.secret_hits_by_file maps relative paths to counts of retained secret
findings, in sorted path order. Repeated hits on the same line count separately;
config and dependency findings do not contribute. Only paths with secret findings
appear, and no retained secrets produces {}. Counts sum to summary.secret_hits,
include low-severity hints, and can be reduced by finding caps. They do not prove
that credentials are real or that scanning was complete. This summary does not
change findings, gates, triage, or manifest_hash.
These symlink exclusions apply to entries encountered beneath the selected
target. An explicitly supplied local target that is a symlink to a directory is
followed: its regular descendants are scanned and counted normally, while links
encountered inside it are still skipped. The target alias itself does not add to
summary.skipped_non_regular or summary.files_scanned. The report retains the
supplied target spelling, and finding paths are relative to that target; it does
not replace the reported target with the resolved directory path.
summary.osv_enabled is false when --no-osv disables dependency advisory
lookups, and true otherwise. It records the selected mode, not lookup success:
check errors, summary.packages_found, and summary.lockfiles_parsed for
coverage. Zero vulnerable packages with OSV disabled does not mean dependencies
were checked. This summary field does not affect manifest_hash.
summary.packages_by_ecosystem counts unique (ecosystem, name, version) inventory
entries for each observed ecosystem, in sorted ecosystem order. Its counts sum to
summary.packages_found; duplicate entries across manifests count once, while
different versions count separately. Empty inventory produces {}. These are
inventory counts, including with --no-osv, not advisory counts or proof of a
complete assessment. File limits bound this inventory; finding limits do not.
summary.multi_version_packages maps ecosystems to package names to lists of
versions, including only names with multiple distinct versions in that ecosystem's
parsed inventory. For example, {"PyPI": {"demo": ["1.0", "2.0"]}} means both
versions were found; duplicates across manifests count once. Ecosystems, names,
and version strings are ordered lexicographically, not by semantic version.
Names and versions use the existing parsers' normalization; ecosystems remain
separate. No multi-version names produces {}. This works with --no-osv and is
unaffected by finding caps, but file limits and parser coverage bound the inventory.
Multiple versions can be intentional and do not establish a vulnerability or a
resolution conflict. This field adds no findings and does not affect severity gates
or manifest_hash.
summary.package_names_by_ecosystem counts distinct parsed package names in each
ecosystem, ignoring repeated versions and manifests. Names use the existing
parsers' spelling and normalization; scoped npm names remain distinct from
unscoped names. Keys are alphabetically ordered, and an empty inventory yields
{}. Unlike summary.packages_by_ecosystem (package/version pairs), multiple
versions of one name count once here. Counts remain available with --no-osv
and are independent of finding limits, but file limits and parser scope still
apply. They do not establish complete dependency coverage or vulnerability
status, and do not affect manifest_hash.
summary.vulnerable_packages_by_ecosystem counts unique vulnerable
(ecosystem, name, version) entries returned by OSV, grouped in sorted ecosystem
order. Only affected ecosystems appear, and the counts sum to
summary.vulnerable_packages, even when finding limits truncate the report.
Multiple advisories for one package version count once; distinct versions count
separately. No returned vulnerabilities or --no-osv produces {}. These counts
do not establish lookup completeness or change findings, gates, or manifest_hash.
summary.dependency_files_by_ecosystem counts distinct representative manifest
paths in emitted dependency findings, grouped in sorted ecosystem order. Several
vulnerable packages in one file count once within that ecosystem. Only ecosystems
with retained findings appear; no dependency findings (including --no-osv)
produces {}. Global and per-file finding caps can reduce these counts. The
scanner attributes each package version to one representative manifest, so this
is not an inventory of every affected manifest or proof of advisory coverage.
This summary does not change findings, gates, triage, or manifest_hash.
summary.packages_by_manifest counts deduplicated inventory entries attributed to
each parsed manifest, in sorted path order, including zero counts. Each unique
(ecosystem, name, version) is attributed to one file: the first encountered,
with a shipped manifest preferred over a test/fixture/example manifest. These
counts sum to summary.packages_found; they are not raw per-file dependency
counts. An empty object means no manifest was parsed. These inventory counts
are available with --no-osv, are bounded by file limits, and are unaffected by
finding limits or advisory results. They do not change manifest_hash.
summary.packages_by_path_context splits the same unique inventory entries into
scaffold and other, including zero counts. Classification uses the existing
test/example/fixture/documentation/CI path heuristic on each entry's attributed
manifest; an entry seen in both contexts is attributed to the non-scaffold path.
Distinct versions and ecosystems count separately. The counts sum to
packages_found and are available with --no-osv and without --triage.
This is a path hint, not proof of deployment or runtime use: a development
dependency outside a recognized scaffold path counts as other. File limits
bound the inventory; finding limits do not. Findings and manifest_hash are
unchanged by this summary.
manifest_hash is the sha256 of the canonical (sorted-key, compact) JSON of
{findings, target, commit}. It is stable when those three values are unchanged;
it is a report fingerprint, not a checksum of all repository contents. Findings
reflect the current working tree, so uncommitted edits can change the hash even
with the same commit and OSV data. Edits that leave findings unchanged do not
change it when target and commit are unchanged. target preserves the literal
CLI argument: different spellings of the same path can produce different hashes.
To recompute it from a full JSON report, use Python's ASCII-escaped JSON strings
(ensure_ascii=True), sorted object keys, and compact separators, then hash the
UTF-8 bytes without a trailing newline. Array order is preserved. Non-ASCII
characters stay escaped in this hash input even with --unicode-json; report
indentation and key-order flags do not change it. For example, with a full report
saved as report.json:
import hashlib
import json
with open("report.json", encoding="utf-8") as report_file:
report = json.load(report_file)
payload = {key: report[key] for key in ("findings", "target", "commit")}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"),
ensure_ascii=True)
assert hashlib.sha256(canonical.encode("utf-8")).hexdigest() == report["manifest_hash"]
This recipe requires the full findings array; --summary-only output omits it.
Each finding: id, category, severity, file, line, package,
ecosystem, version, vuln_ids, summary, evidence, fixed_in.
For dependency findings, evidence displays only the first five advisory IDs in
lookup-result order, while vuln_ids retains the full list sorted by ID. The
finding summary includes the advisory count and only the first advisory's
summary, limited to 120 characters. These display fields are not a complete
advisory list or a description of the most severe advisory. Advisories omitted
from evidence still contribute to severity, fix metadata, advisory counts,
triage and severity gates. Use vuln_ids to review all reported advisories for
an emitted finding; file/finding limits and lookup failures can still limit
report coverage.
Findings are sorted by severity (critical, high, medium, low, unknown), then
category, file, line, package and summary. Missing lines sort as zero; missing
file/package values sort as empty strings. id is assigned after sorting
(F-001…). These IDs identify findings within one report, including keys in
triage when enabled; they are not persistent issue identifiers. Adding or
removing a finding earlier in the sort order can renumber an unchanged finding.
Do not join findings across scans using id alone.
For a dependency-vuln finding, file names one representative manifest for
the unique (ecosystem, name, version) entry, not every manifest containing it.
The scanner prefers a path outside its test/fixture/example/benchmark path
heuristic; among paths with the same classification it retains the first in
sorted relative-path order. Different versions remain separate entries. Thus
affected-file counts can be smaller than the number of manifests containing an
affected dependency, even without truncation. summary.lockfiles_parsed lists
processed manifests, but does not map each package to all its occurrences.
summary.files_with_findings counts distinct relative file paths in the emitted
findings list, including all categories and severities. Multiple findings in one
file count once. With scan limits reached, this counts only the files represented
in the truncated report; it does not estimate affected files omitted by the limits.
summary.files_with_findings_by_path_context splits those distinct retained
file counts into scaffold and other, including zero counts. It uses the same
path-component heuristic as summary.findings_by_path_context (for example,
tests/, examples/, and docs/); filename-only hints such as src/test_app.py
do not make a path scaffold here. Repeated findings, categories, severities, and
line numbers in one file count once; findings with no line number still count.
The two buckets sum to summary.files_with_findings. This describes retained
finding paths, not all affected manifests or a judgment that a file is safe.
Counts remain available with --summary-only, with or without --triage.
summary.findings_by_file maps relative file paths to emitted finding counts,
with paths sorted lexically. It includes all categories and severities, omits
files with no findings, and is {} for an empty report. Counts sum to the length
of findings; a dependency finding with multiple advisories counts once. Scan
limits restrict these counts to retained findings, and the mapping does not
change with --triage or affect manifest_hash.
summary.files_with_multiple_findings counts relative file paths with at least
two emitted findings across all categories and severities. Findings on the same
line, and findings without a line number, each contribute to that file's count.
Each qualifying file counts once; an empty report yields zero. Scan limits
restrict this count to retained findings. It remains available with
--summary-only; triage does not filter it.
summary.finding_lines_by_category counts distinct retained (file, line)
locations separately for dependency-vuln, secret, and config, including zero
counts for categories without located findings. Findings with line: null are
excluded. Multiple findings in one category at the same location count once;
the same line number in different files counts separately. A location shared by
categories counts in each, so the category counts can sum to more than
summary.finding_lines. These are review-location counts after finding caps,
not source-line coverage or evidence that a category is safe. The counts remain
available with --summary-only, with or without --triage.
summary.findings_without_line counts emitted findings whose line is null,
such as dependency findings and the missing Dockerfile USER warning. It counts
findings, not files or advisory IDs: two dependency findings in one manifest
count twice. Empty reports and reports containing only located findings yield
zero. Only findings retained after caps count; this field remains available with
--summary-only. It does not change findings, triage, gates or manifest_hash.
summary.finding_lines counts distinct (file, line) locations across all
emitted findings with a non-null line number. Multiple rules or categories on
one line count once; equal line numbers in different files count separately.
Findings without a line number, including dependency findings and the missing
Dockerfile USER check, are excluded. Empty or entirely unlocated reports yield
zero. This counts retained finding locations after limits, not lines scanned,
all affected files, or source coverage. Triage does not filter the count;
findings, severity gates and manifest_hash are unchanged.
summary.finding_lines_by_severity counts distinct retained (file, line)
locations separately for critical, high, medium, low, and unknown,
including zero counts. Findings without a line number are excluded. Multiple
findings at the same location and severity count once; the same location at two
severities counts in both buckets, so their sum can exceed finding_lines.
These are raw finding severities; triage does not filter or reclassify the counts.
summary.finding_lines_by_file breaks that count down by relative file path,
with paths sorted lexically. Each value counts distinct retained non-null line
numbers across all rules and categories in that file. Paths with only unlocated
findings are omitted; an empty or entirely unlocated report yields {}. Values
sum to summary.finding_lines, including when --summary-only omits the findings.
summary.files_with_findings_by_category counts distinct relative file paths for
each of dependency-vuln, secret, and config, including zero counts. Repeated
findings in one file count once within each category; a file with both secret and
config findings counts in both categories. These counts can therefore sum to more
than files_with_findings. They count only emitted findings when scan limits
truncate results and do not affect manifest_hash.
summary.by_category counts emitted findings for dependency-vuln, secret, and
config, including zero counts. Its values sum to the length of findings, even
when scan limits truncate the report. A dependency finding may represent several
advisories; the count is not the number of advisories. Zero dependency findings
does not certify coverage (see summary.osv_enabled above). These counts do not
affect manifest_hash.
summary.by_category_severity breaks each category count down into critical,
high, medium, low, and unknown, including zeros. For example,
summary.by_category_severity.secret.high counts emitted high-severity secret
findings. Each row sums to its summary.by_category count, and each severity
column sums to its summary.by_severity count. These are raw severities, unchanged
by --triage, and count only emitted findings when limits truncate the report.
This summary does not affect manifest_hash or the --fail-on exit gate.
summary.dependency_findings_by_ecosystem counts emitted dependency findings by
ecosystem, in sorted ecosystem order. Only ecosystems with retained findings
appear; the counts sum to summary.by_category["dependency-vuln"]. Multiple
advisory IDs on one finding count once. Counts reflect both finding caps, whereas
summary.vulnerable_packages_by_ecosystem counts affected inventory entries even
when their findings are omitted. No emitted dependency findings yields {};
this does not certify coverage (review OSV status, errors and truncation). Triage
and severity gates do not filter these counts. This field does not affect
manifest_hash.
summary.dependency_findings_by_manifest counts emitted dependency findings per
representative manifest path, with paths sorted lexicographically. Each finding
counts once even when it lists multiple advisory IDs; other finding categories
do not contribute. A package/version shared by multiple manifests is attributed
only to its selected representative path, not every manifest containing it.
Paths without retained dependency findings are omitted; an empty result is {}.
The counts sum to summary.by_category["dependency-vuln"] and reflect finding
limits. They do not certify advisory coverage; review OSV status, errors and
truncation. Triage, severity gates and manifest_hash are unchanged.
summary.dependency_findings_with_fixes counts emitted dependency findings with
at least one reported fixed_in version. Each package/version finding counts
once, even with multiple advisories or fixed versions. This counts reported fix
metadata, not verified upgrade recommendations: a listed version need not fix
every advisory in the finding. Empty, offline, and no-fix reports return zero;
zero does not certify coverage or prove no fix exists. Finding limits can omit
dependencies from this count. The field does not affect gates or manifest_hash.
summary.dependency_findings_without_fixes counts emitted dependency findings
whose fixed_in list is empty. It counts findings, not advisory IDs; a finding
with any reported fix is excluded even if its other advisories have no fix
metadata. Together with dependency_findings_with_fixes, it sums to
summary.by_category["dependency-vuln"]. Missing fix metadata does not prove that
no fixed release exists. Empty, offline, and no-advisory reports return zero;
finding limits can omit results. This count is unchanged by --triage and does
not affect findings, severity gates or manifest_hash.
summary.secret_hits_by_rule groups emitted secret findings by their existing
human-readable summary labels, in alphabetical label order. Its counts sum to
summary.secret_hits; no secret findings produces {}. Repeated hits count
separately, including when provider and generic rules both match one value.
Low-severity hinted hits are included, and --triage does not change the counts.
These labels are not stable rule IDs or credential validation; no matched values
are included. Scan limits restrict the counts to emitted findings. This field
does not affect manifest_hash or the severity gate.
summary.config_issues_by_file counts emitted configuration findings by relative
file path, with paths in alphabetical order. Multiple findings in one file count
separately, including file-level findings without a line number. Paths with no
configuration findings are omitted; no configuration findings produces {}.
Counts sum to summary.config_issues and reflect only findings retained within
scan limits, not complete configuration coverage.
summary.config_issues_by_rule groups emitted configuration findings by their
existing human-readable summary labels, in alphabetical order. These labels
are not stable rule IDs. Counts sum to summary.config_issues; no configuration
findings produces {}. Multiple findings in one file count separately, while
checks that emit once per file still count once. File and finding limits can
omit results, so these counts do not certify configuration coverage. They contain
no credential values and do not affect findings, severity gates or manifest_hash.
summary.highest_severity is the highest severity in the emitted findings,
ordered critical, high, medium, low, then unknown. It is unknown when
all findings are unrated and JSON null when no findings were emitted. It uses
raw severity, unchanged by --triage; unknown never triggers --fail-on.
Scan limits can omit more severe findings, and an empty report does not certify
coverage. Check summary.truncated, summary.osv_enabled, notes and errors.
This summary field does not affect manifest_hash.
summary.unique_advisories counts distinct advisory ID strings in emitted
dependency-vuln findings. A shared ID counts once across packages and versions;
different IDs count separately even if they are aliases for the same issue.
This differs from summary.vulnerable_packages, which counts affected inventory
entries before finding limits. File and finding limits bound the advisory count.
Zero means no advisory IDs were emitted, including with --no-osv; it does not
certify coverage or successful lookups. Check errors, notes and truncation flags.
This summary field does not affect manifest_hash.
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
