Files
git.stella-ops.org/docs/modules/scanner/analyzers-bun.md
StellaOps Bot 6e45066e37
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
up
2025-12-13 09:37:15 +02:00

4.3 KiB

Bun Analyzer (Scanner)

What it does

  • Inventories npm-ecosystem dependencies from Bun-managed projects without executing bun.
  • Supports installed inventory (node_modules/**/package.json), lockfile-only inventory (bun.lock), and declared-only fallback from package.json.
  • Enriches output with deterministic scope signals (dev, optional, peer, scopeUnknown), patch attribution, and bounded sha256 evidence.

Inputs and precedence

  1. Installed inventory (node_modules/ present): traverse installed packages and emit components from installed package.json (uses bun.lock for resolved/integrity + scope enrichment when present).
  2. Lockfile-only (bun.lock present, no install): parse bun.lock and emit components from lock entries.
  3. Declared-only fallback (project markers present but no bun.lock/install): emit explicit-key components from package.json dependency sections.
  4. Unsupported (bun.lockb only): emit a remediation record explaining how to produce bun.lock.

Project discovery (including container roots)

The analyzer discovers Bun project roots under:

  • The analysis root (context.RootPath)
  • Common OCI unpack layouts: layers/*, .layers/*, and layer* (direct children)

Discovery is bounded and deterministic:

  • Sorted directory enumeration
  • Explicit depth and root caps
  • Never recurses into node_modules/

Identity rules (PURL vs explicit key)

Concrete versions emit a PURL:

  • purl = pkg:npm/<name>@<version>
  • Concrete versions follow the Node-style guardrail (no ranges/tags/paths embedded as a "version"; see Internal/BunVersionSpec.IsConcreteNpmVersion).

Non-concrete versions emit an explicit key:

  • componentKey = explicit::<analyzerId>::npm::<name>::sha256:<digest>
  • purl = null, version = null
  • Used for declared-only dependencies and any lock/installed records whose version is not concrete (e.g., workspace:*, link:../..., file:../...).

Explicit-key digest input (canonical, UTF-8):

npm\n<name>\n<spec>\n<originLocator>

Generated via LanguageExplicitKey.Create(...) and aligned with docs/modules/scanner/language-analyzers-contract.md.

Evidence and locators

All evidence locators are relative and use / separators.

File evidence

  • Installed packages: node_modules/.../package.json
  • Hashing: sha256 is computed for package.json only when size is within 1 MiB; when skipped, metadata includes:
    • packageJson.hashSkipped=true
    • packageJson.hashSkipReason=<missing|unauthorized|io|size>...

Lockfile entry evidence

  • Locator format: <lockfileRelativePath>:packages[<name>@<version>]
    • Example: bun.lock:packages[lodash@4.17.21]
  • Hashing: sha256 is computed for bun.lock only when size is within 50 MiB; when skipped, metadata includes:
    • bunLock.hashSkipped=true
    • bunLock.hashSkipReason=<missing|unauthorized|io|size>...

Scope semantics (dev/optional/peer)

Scope is derived deterministically from the bun.lock dependency graph rooted at package.json declarations:

  • dev=true only when dev reachability is provable.
  • optional=true and peer=true are preserved when present in lock data or derived from declared scopes.
  • If the graph cannot disambiguate (multiple candidates/specifier mismatch), the record is marked:
    • scopeUnknown=true
    • dev=false (do not guess)

includeDev=false filters only packages proven to be dev-only; unknown-scope packages are kept but marked scopeUnknown=true.

Patches and workspaces

  • Workspace patterns come from root package.json (workspaces).
  • Patch attribution supports Bun's patchedDependencies and patch directories.
  • Patch keys preserve version specificity (name@version) and patch paths are emitted as deterministic project-relative paths.
  • Patch matching precedence: name@version first; then name-only only when unambiguous.

Known limitations

  • bun.lockb (binary lockfile) is not parsed; a remediation record is emitted instead.
  • The analyzer does not execute bun and does not fetch registries; offline-only behavior is enforced.

References

  • Sprint: docs/implplan/SPRINT_0407_0001_0001_scanner_bun_detection_gaps.md
  • Cross-analyzer contract: docs/modules/scanner/language-analyzers-contract.md
  • Design notes: docs/modules/scanner/prep/bun-analyzer-design.md
  • Gotchas: docs/modules/scanner/bun-analyzer-gotchas.md