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
- 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.
283 lines
8.6 KiB
Markdown
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
|