Files
git.stella-ops.org/docs/modules/scanner/prep/bun-analyzer-design.md
StellaOps Bot f6c22854a4
Some checks failed
AOC Guard CI / aoc-verify (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
mock-dev-release / package-mock-release (push) Has been cancelled
feat(api): Add Policy Registry API specification
- Introduced OpenAPI specification for the StellaOps Policy Registry API, covering endpoints for verification policies, policy packs, snapshots, violations, overrides, sealed mode operations, and advisory staleness tracking.
- Defined schemas, parameters, and responses for comprehensive API documentation.

chore(scanner): Add global usings for scanner analyzers

- Created GlobalUsings.cs to simplify namespace usage across analyzer libraries.

feat(scanner): Implement Surface Service Collection Extensions

- Added SurfaceServiceCollectionExtensions for dependency injection registration of surface analysis services.
- Included methods for adding surface analysis, surface collectors, and entry point collectors to the service collection.
2025-12-06 20:52:23 +02:00

8.6 KiB

Bun Analyzer Design — PREP-SCANNER-BUN-001-DESIGN-DOC

Status: Draft (2025-12-06) Owners: Bun Analyzer Guild · Scanner Guild Scope: Bun package manager analyzer for npm-ecosystem vulnerability scanning in container filesystems.

Overview

The Bun analyzer extracts npm-ecosystem package inventory from Bun-managed JavaScript/TypeScript projects. Bun uses npm-compatible package.json and produces packages in node_modules, making it similar to the Node analyzer but with distinct lockfile formats and installation structures.

Supported Artifacts

Lockfile Formats

Format Extension Status Notes
Text lockfile bun.lock Supported JSONC format with package metadata
Binary lockfile bun.lockb Unsupported Undocumented binary format; emit migration guidance

Installation Structures

Structure Discovery Pattern Notes
Default (hoisted) node_modules/**/package.json Standard flat structure
Isolated linker node_modules/.bun/**/package.json Symlink-heavy, requires safe traversal

Discovery Heuristics

Project Root Detection

A directory is considered a Bun project root when:

  1. package.json exists, AND
  2. One or more of:
    • bun.lock exists (text lockfile)
    • bun.lockb exists (binary lockfile — triggers unsupported message)
    • bunfig.toml exists (Bun configuration)
    • node_modules/.bun/ exists (isolated linker marker)

Input Classification

BunInputNormalizer classifies each root:
├── InstalledPath: node_modules exists → traverse installed packages
├── LockfilePath: bun.lock only (no node_modules) → parse lockfile
└── Unsupported: bun.lockb only → emit remediation finding

Lockfile Schema (bun.lock)

The bun.lock text format is a JSONC variant (JSON with comments and trailing commas):

{
  "lockfileVersion": 1,
  "workspaces": {
    "": {
      "name": "my-app",
      "dependencies": {
        "lodash": "^4.17.21"
      }
    }
  },
  "packages": {
    "lodash@4.17.21": {
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57...",
      "dependencies": {}
    }
  }
}

Extracted Fields

Field Source Usage
name packages key (before @version) PURL name component
version packages key (after @) PURL version component
resolved packages[].resolved Evidence locator
integrity packages[].integrity Evidence hash (sha512/sha256)

Evidence Model

LanguageComponentEvidence Structure

record BunPackageEvidence(
    LanguageEvidenceKind Kind,     // File | Metadata | Lockfile
    string Source,                  // "node_modules" | "bun.lock"
    string Locator,                 // File path or registry URL
    string? Content,                // package.json content (if File)
    string? Sha256);                // Content hash

Evidence Collection Matrix

Source Kind Locator Content Hash
node_modules/**/package.json File Relative path JSON content sha256 of content
bun.lock Lockfile bun.lock:packages[name@version] null null
Registry resolution Metadata resolved URL null integrity value

PURL Generation

Bun packages are npm packages with bun package manager qualifier:

pkg:npm/<name>@<version>?package_manager=bun

Scoped Package Encoding

Raw Name Encoded PURL
lodash pkg:npm/lodash@4.17.21?package_manager=bun
@types/node pkg:npm/%40types/node@20.10.0?package_manager=bun
@org/pkg pkg:npm/%40org/pkg@1.0.0?package_manager=bun

Bun's isolated linker creates symlink-heavy structures. Safety requirements:

  1. Prefix Containment: Only follow symlinks that resolve within root path
  2. Cycle Detection: Maintain visited inode/realpath set
  3. Path Recording: Record both logical path (symlink) and real path (target)
  4. Depth Limit: Cap symlink depth at 32 levels (configurable)
record SymlinkSafetyContext(
    string RootPrefix,
    HashSet<(long Inode, long Device)> VisitedInodes,
    int MaxDepth = 32);

Performance Guards

Guard Default Rationale
max-files-per-root 50,000 Prevent full image traversal
max-symlink-depth 32 Avoid infinite loops in malformed structures
prefix-pruning enabled Skip paths outside expected locations
timeout-per-root 60s Bound analysis time per project

CLI Contract

stellaops-cli bun inspect

Display Bun package inventory for local root or scan ID:

# Local analysis
stellaops-cli bun inspect /path/to/project

# Remote scan lookup
stellaops-cli bun inspect --scan-id abc123

Output formats: --output json|table|ndjson

stellaops-cli bun resolve

Resolve Bun packages by scan ID, digest, or image reference:

stellaops-cli bun resolve --scan-id abc123 --package lodash
stellaops-cli bun resolve --digest sha256:abc... --format json

WebService Contract

GET /api/scans/{scanId}/bun-packages

Returns inventory of Bun packages for a completed scan.

Query parameters:

  • page, pageSize: Pagination
  • name: Filter by package name (prefix match)
  • scope: Filter by npm scope

Response schema:

{
  "scanId": "abc123",
  "analyzer": "bun",
  "packages": [
    {
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21?package_manager=bun",
      "evidence": [
        {
          "kind": "File",
          "source": "node_modules",
          "locator": "node_modules/lodash/package.json",
          "sha256": "abc..."
        }
      ]
    }
  ],
  "total": 150,
  "page": 1,
  "pageSize": 50
}

Unsupported Artifact Handling

Binary Lockfile (bun.lockb)

When only bun.lockb is present (no bun.lock or node_modules):

  1. Emit remediation finding with severity Info
  2. Provide migration command: bun install --save-text-lockfile
  3. Skip package enumeration (no false negatives from partial binary parse)
record BunLockbUnsupportedFinding(
    string Path,
    string RemediationCommand = "bun install --save-text-lockfile",
    string Reason = "Binary lockfile format is undocumented and unstable");

Test Fixtures

Fixture Purpose Validation
hoisted-install Standard Bun install with node_modules + bun.lock Installed inventory path
isolated-linker bun install --linker isolated structure .bun/ traversal
lockfile-only No node_modules, only bun.lock Lockfile inventory, dev/prod filtering
binary-lockfile-only Only bun.lockb present Unsupported remediation message
monorepo-workspaces Multiple package.json under single lock Workspace member handling
symlink-cycles Malformed structure with cycles Cycle detection, no infinite loops

Configuration

Environment Variables

Variable Default Description
STELLAOPS_BUN_MAX_FILES 50000 Max files per root
STELLAOPS_BUN_MAX_SYMLINK_DEPTH 32 Max symlink traversal depth
STELLAOPS_BUN_INCLUDE_DEV true Include dev dependencies
STELLAOPS_BUN_TIMEOUT_SECONDS 60 Per-root analysis timeout

appsettings.json

{
  "Scanner": {
    "Analyzers": {
      "Bun": {
        "MaxFilesPerRoot": 50000,
        "MaxSymlinkDepth": 32,
        "IncludeDevDependencies": true,
        "TimeoutSeconds": 60
      }
    }
  }
}

Determinism Requirements

  1. Sorted Output: Packages ordered by (name, version) tuple
  2. Stable IDs: Component keys computed as sha256(analyzerId + purl)
  3. Reproducible Evidence: Evidence ordered by (kind, source, locator)
  4. No Timestamps: Evidence does not include file modification times
  5. Canonical Paths: All paths normalized (forward slashes, no trailing slash)

Open Decisions

  1. Dev Dependency Default: Currently include_dev: true for lockfile-only scans — confirm with Policy Guild
  2. Workspace Handling: Whether to emit separate inventory per workspace or merged — await monorepo fixture results
  3. PURL Qualifier: Using ?package_manager=bun vs no qualifier — coordinate with Concelier linkset resolution

Handoff

This document serves as the PREP artifact for PREP-SCANNER-BUN-001-DESIGN-DOC. Update upon:

  • Policy Guild confirmation of dev dependency defaults
  • Concelier Guild decision on PURL qualifier handling
  • Fixture suite completion revealing edge cases