# 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/@` - 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::::npm::::sha256:` - `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\n\n ``` 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=...` ### Lockfile entry evidence - Locator format: `:packages[@]` - 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=...` ## 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`