Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ New catalogs land under `threat_intel/`. Before submitting:
```sh
python3 -c "import json, jsonschema; \
jsonschema.validate(json.load(open('threat_intel/your-catalog.json')), \
json.load(open('docs/schema/v0.1.0/exposure-catalog.schema.json')))"
json.load(open('docs/schema/v0.2.0/exposure-catalog.schema.json')))"
```

- Include a `_comment` field at the catalog root with the methodology
Expand All @@ -54,10 +54,10 @@ Catalogs can also be generated offline from OSV data with

## Schema changes

Any change to `docs/schema/v0.1.0/*.json` or the wire format that breaks
existing consumers is a breaking change. Land it as a new version
directory (`docs/schema/v0.2.0/`) and bump `model.SchemaVersion`
together; do not edit a published schema in place.
Any change to a published `docs/schema/<version>/*.json` or the wire
format that breaks existing consumers is a breaking change. Land it as a
new version directory (e.g. `docs/schema/v0.3.0/`) and bump
`model.SchemaVersion` together; do not edit a published schema in place.

## Security issues

Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Package record:
{
"record_type": "package",
"record_id": "package:...",
"schema_version": "0.1.0",
"schema_version": "0.2.0",
"scanner_name": "bumblebee",
"scanner_version": "v0.1.1",
"run_id": "9b1f0c2e4d5a6b7c8d9e0f1a2b3c4d5e",
Expand Down Expand Up @@ -212,7 +212,7 @@ Finding record (exposure-catalog match):
{
"record_type": "finding",
"record_id": "finding:...",
"schema_version": "0.1.0",
"schema_version": "0.2.0",
"scanner_name": "bumblebee",
"scanner_version": "v0.1.1",
"run_id": "3a8c7d1e9f0b2a4c6d8e0f1a2b3c4d5e",
Expand Down Expand Up @@ -251,11 +251,12 @@ guidance: [docs/state-model.md](docs/state-model.md#record-identity-record_id).

## Exposure Catalog Format

Minimal JSON, exact `(ecosystem, name, version)` matching only:
Minimal JSON, exact `(ecosystem, name, version)` matching. An entry may
declare `"versions": ["*"]` to match every version of the package:

```json
{
"schema_version": "0.1.0",
"schema_version": "0.2.0",
"entries": [
{
"id": "advisory-2026-0042",
Expand All @@ -270,10 +271,11 @@ Minimal JSON, exact `(ecosystem, name, version)` matching only:
```

The catalog must be a JSON object with `schema_version` and `entries`
keys. Bare top-level arrays are rejected. Unsupported future
`schema_version` values are rejected. Multiple catalog files can be
loaded together by pointing `--exposure-catalog` at a directory; see
the flag description above.
keys. Bare top-level arrays are rejected. `schema_version` `0.1.0`
catalogs are still accepted (they cannot use `"*"`); unsupported future
values are rejected. Multiple catalog files can be loaded together by
pointing `--exposure-catalog` at a directory; see the flag description
above.

### Sample exposure catalogs

Expand Down
2 changes: 1 addition & 1 deletion cmd/bumblebee/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func registerScanFlags(fs *flag.FlagSet, o *scanOpts) {
fs.IntVar(&o.concurrency, "concurrency", 4, "number of concurrent file parsers")

fs.StringVar(&o.exposureCatalog, "exposure-catalog", "",
"path to a JSON exposure catalog file, or a directory containing one or more *.json catalogs (merged non-recursively). Matches emit record_type=finding alongside packages. v0.1 matches by exact (ecosystem, normalized_name, version). The catalog is package-presence criteria only; it is NOT an EDR IOC feed.")
"path to a JSON exposure catalog file, or a directory containing one or more *.json catalogs (merged non-recursively). Matches emit record_type=finding alongside packages. Matching is by exact (ecosystem, normalized_name, version); an entry with versions [\"*\"] matches any version. The catalog is package-presence criteria only; it is NOT an EDR IOC feed.")
fs.Int64Var(&o.maxCatalogSize, "max-catalog-size", 64*1024*1024,
"max bytes to read from any single exposure catalog file (0 = unbounded). Applied per file when --exposure-catalog is a directory.")
fs.BoolVar(&o.findingsOnly, "findings-only", false,
Expand Down
2 changes: 1 addition & 1 deletion cmd/bumblebee/selftest/catalog.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"schema_version": "0.1.0",
"schema_version": "0.2.0",
"_comment": "Synthetic exposure catalog used by `bumblebee selftest`. Entries reference fixture packages that do not exist on any real registry; the names are deliberately obvious so this catalog is never confused with production threat intel.",
"entries": [
{
Expand Down
24 changes: 24 additions & 0 deletions docs/schema/v0.2.0/diagnostic-record.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/perplexityai/bumblebee/docs/schema/v0.2.0/diagnostic-record.schema.json",
"title": "bumblebee diagnostic record",
"type": "object",
"additionalProperties": false,
"required": [
"record_type",
"record_id",
"run_id",
"time",
"level",
"message"
],
"properties": {
"record_type": { "const": "diagnostic" },
"record_id": { "type": "string", "pattern": "^diagnostic:[0-9a-f]{64}$" },
"run_id": { "type": "string" },
"time": { "type": "string", "format": "date-time" },
"level": { "enum": ["debug", "info", "warn", "error"] },
"path": { "type": "string" },
"message": { "type": "string" }
}
}
38 changes: 38 additions & 0 deletions docs/schema/v0.2.0/exposure-catalog.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/perplexityai/bumblebee/docs/schema/v0.2.0/exposure-catalog.schema.json",
"title": "bumblebee exposure catalog",
"type": "object",
"additionalProperties": true,
"required": ["schema_version", "entries"],
"properties": {
"schema_version": { "const": "0.2.0" },
"entries": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"required": ["id", "ecosystem", "package", "versions"],
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"ecosystem": {
"enum": ["npm", "pypi", "go", "rubygems", "packagist", "mcp", "editor-extension", "browser-extension", "homebrew"]
},
"package": { "type": "string" },
"versions": {
"oneOf": [
{ "const": ["*"] },
{
"type": "array",
"minItems": 1,
"items": { "type": "string", "not": { "const": "*" } }
}
]
},
"severity": { "type": "string" }
}
}
}
}
}
54 changes: 54 additions & 0 deletions docs/schema/v0.2.0/finding-record.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/perplexityai/bumblebee/docs/schema/v0.2.0/finding-record.schema.json",
"title": "bumblebee finding record",
"type": "object",
"additionalProperties": false,
"required": [
"record_type",
"record_id",
"schema_version",
"scanner_name",
"scanner_version",
"run_id",
"scan_time",
"endpoint",
"profile",
"finding_type",
"catalog_id",
"ecosystem",
"package_name",
"normalized_name",
"version",
"source_type",
"source_file",
"confidence"
],
"properties": {
"record_type": { "const": "finding" },
"record_id": { "type": "string", "pattern": "^finding:[0-9a-f]{64}$" },
"schema_version": { "const": "0.2.0" },
"scanner_name": { "const": "bumblebee" },
"scanner_version": { "type": "string" },
"run_id": { "type": "string" },
"scan_time": { "type": "string", "format": "date-time" },
"endpoint": { "$ref": "package-record.schema.json#/$defs/endpoint" },
"profile": { "enum": ["baseline", "project", "deep"] },
"finding_type": { "const": "package_exposure" },
"severity": { "type": "string" },
"catalog_id": { "type": "string" },
"catalog_name": { "type": "string" },
"ecosystem": {
"enum": ["npm", "pypi", "go", "rubygems", "packagist", "mcp", "editor-extension", "browser-extension"]
},
"package_name": { "type": "string" },
"normalized_name": { "type": "string" },
"version": { "type": "string" },
"root_kind": { "type": "string" },
"project_path": { "type": "string" },
"source_type": { "type": "string" },
"source_file": { "type": "string" },
"confidence": { "enum": ["high", "medium", "low"] },
"evidence": { "type": "string" }
}
}
73 changes: 73 additions & 0 deletions docs/schema/v0.2.0/package-record.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/perplexityai/bumblebee/docs/schema/v0.2.0/package-record.schema.json",
"title": "bumblebee package record",
"type": "object",
"additionalProperties": false,
"required": [
"record_type",
"record_id",
"schema_version",
"scanner_name",
"scanner_version",
"run_id",
"scan_time",
"endpoint",
"profile",
"ecosystem",
"package_name",
"normalized_name",
"version",
"source_type",
"source_file",
"has_lifecycle_scripts",
"confidence"
],
"properties": {
"record_type": { "const": "package" },
"record_id": { "type": "string", "pattern": "^package:[0-9a-f]{64}$" },
"schema_version": { "const": "0.2.0" },
"scanner_name": { "const": "bumblebee" },
"scanner_version": { "type": "string" },
"run_id": { "type": "string" },
"scan_time": { "type": "string", "format": "date-time" },
"endpoint": { "$ref": "#/$defs/endpoint" },
"profile": { "enum": ["baseline", "project", "deep"] },
"ecosystem": {
"enum": ["npm", "pypi", "go", "rubygems", "packagist", "mcp", "editor-extension", "browser-extension", "homebrew"]
},
"package_name": { "type": "string" },
"normalized_name": { "type": "string" },
"version": { "type": "string" },
"project_path": { "type": "string" },
"root_kind": { "type": "string" },
"install_scope": { "type": "string" },
"package_manager": { "type": "string" },
"source_type": { "type": "string" },
"source_file": { "type": "string" },
"direct_dependency": { "type": "boolean" },
"has_lifecycle_scripts": { "type": "boolean" },
"lifecycle_scripts": {
"type": "array",
"items": { "type": "string" }
},
"confidence": { "enum": ["high", "medium", "low"] },
"requested_spec": { "type": "string" },
"server_name": { "type": "string" }
},
"$defs": {
"endpoint": {
"type": "object",
"additionalProperties": false,
"required": ["hostname", "os", "arch", "username", "uid"],
"properties": {
"hostname": { "type": "string" },
"os": { "type": "string" },
"arch": { "type": "string" },
"username": { "type": "string" },
"uid": { "type": "string" },
"device_id": { "type": "string" }
}
}
}
}
69 changes: 69 additions & 0 deletions docs/schema/v0.2.0/scan-summary.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/perplexityai/bumblebee/docs/schema/v0.2.0/scan-summary.schema.json",
"title": "bumblebee scan summary",
"type": "object",
"additionalProperties": false,
"required": [
"record_type",
"record_id",
"schema_version",
"scanner_name",
"scanner_version",
"run_id",
"scan_time",
"end_time",
"endpoint",
"profile",
"status",
"package_records_emitted",
"findings_emitted",
"duplicates",
"diagnostics_count",
"files_considered",
"timed_out",
"duration_ms"
],
"properties": {
"record_type": { "const": "scan_summary" },
"record_id": { "type": "string", "pattern": "^scan_summary:[0-9a-f]{64}$" },
"schema_version": { "const": "0.2.0" },
"scanner_name": { "const": "bumblebee" },
"scanner_version": { "type": "string" },
"run_id": { "type": "string" },
"scan_time": { "type": "string", "format": "date-time" },
"end_time": { "type": "string", "format": "date-time" },
"endpoint": { "$ref": "package-record.schema.json#/$defs/endpoint" },
"profile": { "enum": ["baseline", "project", "deep"] },
"status": { "enum": ["complete", "partial", "error"] },
"roots": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["path", "kind"],
"properties": {
"path": { "type": "string" },
"kind": { "type": "string" }
}
}
},
"counts": {
"type": "object",
"additionalProperties": { "type": "integer" }
},
"package_records_emitted": { "type": "integer", "minimum": 0 },
"package_records_suppressed": { "type": "integer", "minimum": 0 },
"findings_emitted": { "type": "integer", "minimum": 0 },
"duplicates": { "type": "integer", "minimum": 0 },
"diagnostics_count": { "type": "integer", "minimum": 0 },
"files_considered": { "type": "integer", "minimum": 0 },
"timed_out": { "type": "boolean" },
"duration_ms": { "type": "integer", "minimum": 0 },
"http_batches_attempted": { "type": "integer", "minimum": 0 },
"http_batches_succeeded": { "type": "integer", "minimum": 0 },
"http_batches_failed": { "type": "integer", "minimum": 0 },
"http_last_status": { "type": "integer", "minimum": 0 },
"error": { "type": "string" }
}
}
Loading
Loading