You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Project the package summary into scan and diff package collections and focused explain dependencies. Reuse enriched vulnerabilities for compact MCP remediation with optional audit overlays, show the summary in component details, and document the read-only contract.
Copy file name to clipboardExpand all lines: dev-docs/ARCHITECTURE.md
+17-1Lines changed: 17 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@ Stage summary:
65
65
1. Runtime preparation builds the filtered registry and execution plan.
66
66
2. Subproject discovery finds supported package-manager roots for the target. By default only the execution-target root is inspected; `--recursive` walks nested directories (bounded by `--max-depth`, `--exclude`, and built-in ignore rules) and plans one subproject per directory-and-package-manager pair, with workspace-expanding managers pruned below ancestors that already cover them.
67
67
3. Detection resolves a dependency graph per package manager and then consolidates the per-subproject graphs into the single graph and package registry the rest of the pipeline uses. When `--scope` is set, the requested scope is part of the detector request so build-tool detectors can narrow command execution where the package manager supports it; all detector results pass through the shared SDK scope filter, and consolidation is the tail of this stage rather than a separate step.
68
-
4. Matchers enrich packages with additional metadata such as licenses, EOL status, and vulnerability records.
68
+
4. Matchers enrich packages with additional metadata such as licenses, EOL status, and vulnerability records. After matcher results are consolidated, the engine derives a small package remediation summary from the fix evidence already on each vulnerable package.
69
69
5. Analyzers run when `--analyze` is set. They consume the matched graph and annotate `sdk.Vulnerability.Reachability` (on the PURL-keyed registry package) with status (reachable/unreachable/unknown), tier (symbol/module/package/none), and call paths. Failures degrade to `Status=unknown` rather than aborting the pipeline. See [`../docs/REACHABILITY.md`](../docs/REACHABILITY.md) for ecosystem coverage and tier semantics.
70
70
6. Auditors evaluate policy against the enriched graph + registry pair and create reference-style findings (`PackageRef` + `VulnerabilityID`) when `--audit` is enabled. As the final part of that same audit stage, neutral policy-status resolvers may change only `Finding.PolicyStatus`; they never remove or rewrite finding evidence. The built-in `vulnerability`, `license`, and `package` auditors cover advisory thresholds, SPDX policy, and denied or suspicious packages respectively.
71
71
7. Users combine `--enrich --audit` when they want external matcher data to feed policy evaluation in the same run.
@@ -133,6 +133,22 @@ same behavior without owning global identity policy. Baseline construction
133
133
repeats the identity normalization defensively for legacy or independently
134
134
constructed registries.
135
135
136
+
### Decision: package remediation is derived enrichment
137
+
138
+
After vulnerability consolidation, the engine derives
139
+
`sdk.Package.Remediation` from each package's vulnerability fix evidence. The
140
+
summary reports whether the evidence is complete, partial, unavailable, or
141
+
unknown. A recommended version is present only when every vulnerability has a
142
+
usable version and those versions can be compared.
143
+
144
+
This remains part of matching rather than becoming a pipeline stage.
145
+
Detectors, matchers, and protocol-v1 plugins do not own the result, and the
146
+
engine overwrites any incoming value. The derivation is read-only: it makes no
147
+
network calls, does not inspect manifests or dependency relationships, and
148
+
does not apply changes. JSON keeps the summary on the PURL-keyed package.
149
+
MCP uses the same summary when it can safely name one group-level recommended
150
+
version, while preserving its existing bounded relationship and policy view.
151
+
136
152
Pipeline plumbing: `engine.PipelineResult` exposes `Graph`, `Registry`, `Findings`, and `RiskScores`. The registry is built right after consolidation (`consolidation.BuildPackageRegistry`) and threaded through match/analyze/audit requests; output helpers (`BuildScanResponse`, `WriteSARIF`, `FindingsFromScan`, `PackagesFromGraph`) all accept `*sdk.PackageRegistry` and re-enrich their projections by resolving `PackageRef` and `VulnerabilityID`. See [`MODELS.md`](MODELS.md) for the full schema reference.
| Detection |`sdk.Dependency`| per-manifest `sdk.Graph`|`Dependency.ID` (stable within manifest) | One node per dependency instance; carries scope, locations, edges |
10
-
| Matching |`sdk.Package`|`sdk.PackageRegistry`|`Package.PURL` (canonical) | One artifact per unique PURL; carries licenses, vulnerabilities, scorecard, EOL |
10
+
| Matching |`sdk.Package`|`sdk.PackageRegistry`|`Package.PURL` (canonical) | One artifact per unique PURL; carries licenses, vulnerabilities, remediation, scorecard, EOL |
11
11
| Audit |`sdk.Finding`|`engine.PipelineResult.Findings`|`Finding.ID` + `Finding.PackageRef` + `Finding.VulnerabilityID`| Reference-style policy outcome with no inlined vuln fields |
12
12
13
13
Vulnerabilities themselves are OSV-aligned `sdk.Vulnerability` records owned by the registry; analyzers annotate them in place with reachability.
@@ -123,6 +123,7 @@ type Package struct {
123
123
Digests []Digest
124
124
Licenses []PackageLicense
125
125
Vulnerabilities []Vulnerability// OSV-aligned
126
+
Remediation *PackageRemediation // derived from vulnerability fix evidence
126
127
Scorecard *PackageScorecard
127
128
EOL *PackageEOL
128
129
Copyrightstring
@@ -142,6 +143,23 @@ Registry API (`sdk/registry.go`):
142
143
143
144
Built by `consolidation.BuildPackageRegistry(consolidated)` right after the consolidation stage; threaded through match/analyze/audit and into the output layer via `PipelineResult.Registry`.
144
145
146
+
`Package.Remediation` is a small summary derived by the engine after all
147
+
matcher results have been consolidated. It is absent when a package has no
148
+
vulnerabilities:
149
+
150
+
-`complete` means every vulnerability has usable fix evidence and
151
+
`RecommendedVersion` is the lowest package version known to address all of
152
+
them.
153
+
-`partial` means some fix evidence exists but it does not support one complete
154
+
recommendation.
155
+
-`unavailable` means every vulnerability explicitly reports no fix or
156
+
won't-fix.
157
+
-`unknown` means evidence is missing or contradictory.
158
+
159
+
The summary is derived data, not matcher policy. The engine replaces any
160
+
incoming value after matching. Derivation does not inspect manifests or the
161
+
dependency graph, make network calls, run commands, or write files.
Copy file name to clipboardExpand all lines: docs/ARCHITECTURE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,7 +33,7 @@ flowchart TD
33
33
34
34
1.**Discover** — Bomly inspects the target root and finds every supported package-manager root (a `go.mod`, a `package-lock.json`, a `pom.xml`, and so on). With `--recursive` it also walks nested directories, discovering independent subprojects in a monorepo while workspace-aware managers (npm workspaces, Maven reactors, …) keep expanding their own modules from the root. See [Scan targets](SCAN_TARGETS.md#recursive-discovery----recursive).
35
35
2.**Detect** — For each root, a [detector](DETECTORS.md) reads the lockfile, manifest, or SBOM and resolves a dependency graph. Per-subproject graphs are then *consolidated* into one graph and one deduplicated package set for the rest of the run. `--scope` narrows the graph to runtime or development dependencies here.
36
-
3.**Match** — When you pass `--enrich`, [matchers](MATCHERS.md) add data to published registry packages: known vulnerabilities, licenses, end-of-life status, and project health scores. Project roots, workspace members, and local/file/Git/URL artifacts remain in the graph and reports but are not queried as if they were registry releases.
36
+
3.**Match** — When you pass `--enrich`, [matchers](MATCHERS.md) add data to published registry packages: known vulnerabilities, licenses, end-of-life status, and project health scores. Bomly then summarizes the available vulnerability fix evidence on each affected package. Project roots, workspace members, and local/file/Git/URL artifacts remain in the graph and reports but are not queried as if they were registry releases.
37
37
4.**Analyze** — When you pass `--analyze`, [reachability](REACHABILITY.md) analysis runs on top of the matched data to flag whether a vulnerability is actually reachable from your code.
38
38
5.**Audit** — When you pass `--audit`, [auditors](AUDITORS.md) evaluate policy (severity thresholds, license rules, denied packages) against the enriched data and produce findings. As part of this same step, configured policy-status rules may mark a finding non-gating without removing it. Combine `--enrich --audit` to gate on fresh external data in one run.
39
39
6.**Render** — Bomly emits the result as text, JSON, SARIF, or an SBOM. See [Output formats](OUTPUT_FORMATS.md) and [SBOM formats](SBOM.md).
@@ -45,7 +45,7 @@ flowchart TD
45
45
Bomly keeps three kinds of data separate, which is why the same fact never appears twice in the output:
46
46
47
47
-**Dependencies** are detection-time graph nodes. Each is one instance of a dependency in a manifest, carrying its scope, where it was found, and its edges to other dependencies. A dependency points at a package by its PURL but does not itself hold license or vulnerability data.
48
-
-**Packages** are deduplicated artifacts keyed by [PURL](GLOSSARY.md). There is one package per unique PURL across the whole scan, and it owns the enrichment: licenses, vulnerabilities, scorecard, and EOL. If 50 dependencies all reference `react@18.2.0`, they share one package — and one set of CVEs.
48
+
-**Packages** are deduplicated artifacts keyed by [PURL](GLOSSARY.md). There is one package per unique PURL across the whole scan, and it owns the enrichment: licenses, vulnerabilities, remediation status, scorecard, and EOL. If 50 dependencies all reference `react@18.2.0`, they share one package — and one set of CVEs.
49
49
-**Findings** are reference-style audit results. A finding names a policy outcome and points back at a package (and, for a vulnerability, at a specific advisory) rather than copying that data inline.
External matchers are published as plugins — see the [ClearlyDefined License Matcher](https://github.com/bomly-dev/bomly-plugin-clearlydefined-matcher) and [EOL Lifecycle Matcher](https://github.com/bomly-dev/bomly-plugin-eol-matcher) for worked examples, and [PLUGINS.md](PLUGINS.md) to install and enable them.
44
44
45
+
## Package remediation summary
46
+
47
+
After all vulnerability matcher results are combined, Bomly summarizes the fix
48
+
evidence for each affected package. The summary reports whether a complete
49
+
recommended version is available, only part of the needed evidence is
50
+
available, every advisory reports no fix, or the evidence is unclear.
51
+
52
+
This is read-only enrichment data. It does not inspect or change manifests and
53
+
does not run a package manager. JSON places the summary on `packages[]`; the
54
+
interactive view shows it in a selected component's details.
Copy file name to clipboardExpand all lines: docs/MCP.md
+10-6Lines changed: 10 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,9 +95,9 @@ Bomly registers four MCP tools.
95
95
96
96
| Tool | Use it for | Required arguments | Optional arguments | Enrichment behavior |
97
97
| --- | --- | --- | --- | --- |
98
-
|`bomly_scan`| Scan a path, Git URL, container image, or SBOM and get a compact, remediation-grouped summary of what needs fixing. | None |`path`, `image`, `url`, `ref`, `enrich`, `audit`, `analyze`, policy arguments, `ecosystems`, `scope`, `recursive`, `max_depth`, `exclude`|Calls external matchers only when `enrich`is true. Pass `enrich`and `audit`together for a security review. |
99
-
|`bomly_explain`|Drill into one package: dependency paths, full advisory detail, and concrete fix context. |`package`|`path`, `enrich`, `audit`, `analyze`, policy arguments, `recursive`, `max_depth`, `exclude`|Calls external matchers only when `enrich` is true. |
100
-
|`bomly_diff`|Branch-aware security delta between two Git refs, container tags/digests, or SBOM files: what head fixes, introduces, and leaves open after merge. |`base`, `head`|`path`, `image`, `sbom`, `enrich`, `audit`, `analyze`, policy arguments, `recursive`, `max_depth`, `exclude`|Calls external matchers only when `enrich` is true. |
98
+
|`bomly_scan`| Scan a path, Git URL, container image, or SBOM and return a compact dependency summary. | None |`path`, `image`, `url`, `ref`, `enrich`, `audit`, `analyze`, policy arguments, `ecosystems`, `scope`, `recursive`, `max_depth`, `exclude`|`enrich`adds vulnerabilities and remediation. `audit`adds policy results. |
99
+
|`bomly_explain`|Show why one package is present, with full advisory details when enriched. |`package`|`path`, `enrich`, `audit`, `analyze`, policy arguments, `recursive`, `max_depth`, `exclude`|`enrich` adds vulnerabilities and remediation. `audit` adds policy results. |
|`bomly_plugins`| List built-in and installed external plugins with their enabled state. | None | None | Does not enrich package data. |
102
102
103
103
MCP coverage matches Bomly CLI coverage: `bomly_scan`, `bomly_explain`, and `bomly_diff` use the same detector, matcher, auditor, and analyzer registry as the CLI. See [Support Matrix](SUPPORT_MATRIX.md) for the current ecosystem and package-manager list.
@@ -116,7 +116,7 @@ MCP tool results land in an agent's context window, so they use a compact respon
116
116
`bomly_scan` returns:
117
117
118
118
-**`summary`** — manifest/package counts, `subprojects` and `modules` counts for scans that span nested projects or workspace/reactor members (omitted for flat scans), vulnerable vs clean packages, findings by severity, and whether enrich/audit ran. Clean packages are counted, never listed. The full hierarchy is derived from `manifests[].subproject` + `path` in the complete CLI JSON document.
119
-
-**`remediations`** — ranked groups, each one concrete change and every finding it closes: the direct dependency to change (`target_package`, full identity with org/scope and PURL), the manifest to edit, the version to move to, and an `action` (`direct-bump`, `transitive-override`, `lockfile-refresh`, `no-fix-upstream`, `policy-review`). Transitive cases carry package-manager-specific `override_advice` (npm `overrides`, pnpm `pnpm.overrides` / `pnpm-workspace.yaml`, yarn `resolutions`, Maven `dependencyManagement`, Gradle constraints, `go get` + `go mod tidy`, and so on). Groups are ranked by known-exploited (KEV) first, then severity, EPSS, and fixability.
119
+
-**`remediations`** — ranked groups built from enriched vulnerabilities. Each group identifies the affected package, its relationship to the project, and the manifest when known. `recommended_version` appears only when one affected package has a complete package-level recommendation. When audit also runs, matching policy status is added without hiding enriched vulnerabilities that did not produce a finding.
120
120
-**`informational`** — warning and policy-only findings, separated from actionable work.
-**One package**: call `bomly_explain` with `enrich` (and `audit` for remediation context). Its response carries the package's full advisory records — descriptions, references, CVSS, affected ranges — bounded to that package.
128
+
-**One package**: call `bomly_explain` with `enrich`. Its response carries the package's full advisory records and remediation context, bounded to that package. Add `audit` when you also need policy results.
129
129
-**The complete document**: run the CLI (`bomly scan --format json -o <file>`). The MCP server intentionally never returns the full scan document; it does not fit tool-result limits on real projects.
130
130
131
-
`bomly_diff` returns the same finding shape bucketed into a `security_delta` — `introduced` (new on head), `resolved` (closed when head merges), `persisted` (still open after merge) — keyed by advisory id independent of version bumps, plus remediation groups for everything still open. By default `base` and `head` are Git refs; set `image` to diff two container tags/digests, or `sbom: true` to diff two SBOM file paths (SPDX or CycloneDX), mirroring the CLI's `--image` and `--sbom` diff modes.
131
+
With `enrich`, `bomly_diff` returns remediation groups for vulnerable packages
132
+
on the head side. With `audit`, it also returns findings bucketed into a
133
+
`security_delta`: `introduced`, `resolved`, and `persisted`. By default `base`
134
+
and `head` are Git refs; set `image` to compare two container tags or digests,
0 commit comments