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

283 lines
8.6 KiB
Markdown

# 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):
```jsonc
{
"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
```csharp
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:
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)
```csharp
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:
```bash
# 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:
```bash
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:
```json
{
"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)
```csharp
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
```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