- 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.
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:
package.jsonexists, AND- One or more of:
bun.lockexists (text lockfile)bun.lockbexists (binary lockfile — triggers unsupported message)bunfig.tomlexists (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 |
Symlink Safety
Bun's isolated linker creates symlink-heavy structures. Safety requirements:
- Prefix Containment: Only follow symlinks that resolve within root path
- Cycle Detection: Maintain visited inode/realpath set
- Path Recording: Record both logical path (symlink) and real path (target)
- 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: Paginationname: 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):
- Emit remediation finding with severity
Info - Provide migration command:
bun install --save-text-lockfile - 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
- Sorted Output: Packages ordered by
(name, version)tuple - Stable IDs: Component keys computed as
sha256(analyzerId + purl) - Reproducible Evidence: Evidence ordered by
(kind, source, locator) - No Timestamps: Evidence does not include file modification times
- Canonical Paths: All paths normalized (forward slashes, no trailing slash)
Open Decisions
- Dev Dependency Default: Currently
include_dev: truefor lockfile-only scans — confirm with Policy Guild - Workspace Handling: Whether to emit separate inventory per workspace or merged — await monorepo fixture results
- PURL Qualifier: Using
?package_manager=bunvs 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