PostBomly CLIby Ahmed ElMallah

How to find out why a dependency is in your project

Use Bomly CLI to trace why a package is in your dependency graph, then add license, vulnerability, and Scorecard context for review.

To find out why a dependency is in your project, run bomly explain <package> from the repo root. Bomly resolves the dependency graph, finds the package, and prints the path or paths that introduced it. Add --enrich when you also want license and vulnerability metadata, and opt into --matchers +scorecard when you want OpenSSF Scorecard context for packages that resolve to GitHub repositories.

This is useful for the small mystery every dependency reviewer knows: a package appears in a lockfile, nobody remembers choosing it, and the answer is somewhere in the graph. It may be direct, transitive, pulled in through multiple routes, or present only because another tool dependency brought it along.

Start with the package name

From the root of a Go project, ask why github.com/spf13/cobra is present:

bomly explain github.com/spf13/cobra --enrich --matchers +scorecard -q

The command resolves the local project, traces dependency paths, and enriches the explained package. --enrich runs Bomly's enabled matchers for external package metadata. --matchers +scorecard opts into the Scorecard matcher specifically; it is not part of the default enrichment set because it adds one lookup per unique GitHub repository.

In Bomly CLI 0.16.0, that command reports:

ecosystem  golang
manager    gomod
package    github.com/spf13/cobra@v1.10.2
scope      runtime
direct     yes
licenses
  Apache-2.0 [external-depsdev]
introduced by:
  github.com/bomly-dev/bomly-cli
  ├─ github.com/anchore/clio@v0.1.1
  │  └─ github.com/spf13/cobra@v1.10.2 (transitive)
  ├─ github.com/anchore/grype@v0.115.0
  │  └─ github.com/anchore/clio@v0.1.1
  │     └─ github.com/spf13/cobra@v1.10.2 (transitive)
  ├─ github.com/anchore/syft@v1.46.0
  │  └─ github.com/anchore/clio@v0.1.1
  │     └─ github.com/spf13/cobra@v1.10.2 (transitive)
  └─ github.com/spf13/cobra@v1.10.2 (direct)

The real output includes nine paths. The important part is the shape: Cobra is a direct dependency, but it is also present through other tools in the graph. That answers a different question than "is it listed in go.mod?" It tells you what else depends on it, which is usually the more useful review question.

Read the impact section

The Markdown output is handy when you want a review artifact:

bomly explain github.com/spf13/cobra \
  --enrich \
  --matchers +scorecard \
  --format markdown -q

The same run summarizes the review context:

Impact Assessment

- Vulnerabilities: 0
- Policy findings: none
- Licenses: 1
- Project posture: 6.5/10  github.com/spf13/cobra
  (updated 2026-06-29, scorecard v5.5.1-0.20260519155427-916bfc57fa74)

That is often enough for a local review. You can see the Apache-2.0 license from deps.dev metadata, that no vulnerability matches were found in this run, and that Scorecard has a result for the upstream GitHub repository.

For automation, use JSON and select the fields you need:

bomly explain github.com/spf13/cobra \
  --enrich \
  --matchers +scorecard \
  --json -q |
  jq '.targets[0].dependency | {
    name,
    version,
    licenses: [.licenses[] | {value}],
    vulnerabilities: [.vulnerabilities[]? | {id, severity}],
    scorecard: {
      repository: .scorecard.repository,
      aggregateScore: .scorecard.aggregateScore,
      runDate: .scorecard.runDate
    }
  }'

That gives you the same data in a shape a script can consume:

{
  "name": "github.com/spf13/cobra",
  "version": "v1.10.2",
  "licenses": [{ "value": "Apache-2.0" }],
  "vulnerabilities": [],
  "scorecard": {
    "repository": "github.com/spf13/cobra",
    "aggregateScore": 6.5,
    "runDate": "2026-06-29T00:00:00Z"
  }
}

When the package has advisories

If enrichment finds known advisories, they appear in the same report. For example, this public fixture pins an older golang.org/x/text. Add --analyze when you want Bomly to run reachability analysis too:

bomly explain golang.org/x/text \
  --url https://github.com/bomly-dev/example-go-gomod \
  --ref v1.0.0 \
  --enrich \
  --analyze \
  --format markdown -q

The impact section includes the vulnerable package version, license, advisory IDs, and the reachability result:

Impact Assessment

- Vulnerabilities: 4
- Policy findings: none
- Licenses: 1
- Reachability: 4 analyzed (4 unreachable)

| Severity | ID | Reachability | Fixed In |
| --- | --- | --- | --- |
| HIGH | GHSA-69ch-w2m2-3vjp | unreachable (package) | 0.3.8 |
| HIGH | GHSA-ppp9-7jff-5vj2 | unreachable (package) | 0.3.7 |
| HIGH | GO-2021-0113 | unreachable (package) | 0.3.7 |
| HIGH | GO-2022-1059 | unreachable (package) | 0.3.8 |

Reachability is still experimental. Symbol-level reachability is currently supported for Go when the advisory and govulncheck data support it; other ecosystems are currently less precise. Treat reachability as triage context, not as proof that a package is safe or unsafe. If a vulnerable package is present, decide whether to update, investigate further, or document why it is acceptable in your project.

What explain is good for

Use bomly explain when you need to answer a narrow question:

  • Why is this package here?
  • Is it direct or transitive?
  • Which parent packages introduced it?
  • What license and vulnerability context is attached to this package?
  • What reachability context is available for the finding?
  • Does the upstream GitHub repository have Scorecard data?

Use bomly scan when you want the whole graph, bomly diff when you want to review what changed between two states, and --json or Markdown output when another tool needs to consume the result. The nice part is that you do not have to start with the whole graph. You can start with the one package that made you ask, "why is this here?"