bumblebee README

The project's README from github.com/perplexityai/bumblebee. As one markdown file: README.md.

bumblebee

Bumblebee is a read-only inventory collector for package, extension, and developer-tool metadata on macOS and Linux developer endpoints.

It answers a narrow supply-chain response question: when an advisory names a package, extension, or version, which developer machines show a match in their on-disk metadata right now?

SBOMs help answer what shipped, and EDR helps answer what ran or touched the network, but supply-chain response often needs a different view: messy local state across lockfiles, package-manager metadata, extension manifests, and supported developer-tool configs.

Bumblebee turns that scattered on-disk state into structured NDJSON component records and, when given an exposure catalog, flags exact matches for fast, read-only exposure checks when responders already know what they are looking for.

Scope

Coverage

Family Emitted ecosystem Sources
npm npm package-lock.json, npm-shrinkwrap.json, node_modules/.package-lock.json, node_modules/<pkg>/package.json
pnpm npm pnpm-lock.yaml, .pnpm/.../package.json
Yarn npm yarn.lock (Classic + Berry)
Bun npm bun.lock; bun.lockb presence as diagnostic
PyPI pypi *.dist-info/METADATA, INSTALLER, direct_url.json, *.egg-info/PKG-INFO
Go modules go go.sum, go.mod
RubyGems rubygems Gemfile.lock, installed *.gemspec
Composer packagist composer.lock, vendor/composer/installed.json
MCP mcp JSON host configs: mcp.json, .mcp.json, claude_desktop_config.json, mcp_config.json, mcp_settings.json, cline_mcp_settings.json, plus ~/.gemini/settings.json (Gemini CLI / Code Assist) and ~/.claude.json (Claude Code user- and project-scoped mcpServers). Non-JSON configs (Codex config.toml, Continue YAML) are not parsed in v0.1.
Agent skills agent-skill skills.sh / vercel-labs/skills lock files: global ~/.agents/.skill-lock.json (or $XDG_STATE_HOME/skills/.skill-lock.json) and project-local skills-lock.json. Loose SKILL.md directories without a lock file are not enumerated.
Editor extensions editor-extension VS Code, Cursor, Windsurf, VSCodium manifests
Browser extensions browser-extension Chromium-family (manifest.json) and Firefox (extensions.json) per profile
Homebrew homebrew Formula INSTALL_RECEIPT.json files and cask .metadata install markers

Per-ecosystem detail: docs/inventory-sources.md.

Install

Requires Go 1.25+. Zero non-stdlib dependencies.

# Install the latest tagged release into $GOBIN.
go install github.com/perplexityai/bumblebee/cmd/bumblebee@latest

# Or pin a specific tag.
go install github.com/perplexityai/bumblebee/cmd/[email protected]

To build from a checkout:

go build -o bumblebee ./cmd/bumblebee
go test ./...

Stamp an explicit version at build time:

go build -ldflags "-X main.Version=v0.1.1" -o bumblebee ./cmd/bumblebee

bumblebee version prints the version plus the VCS revision, build time, and Go runtime — so a record emitted in production can be traced back to a specific build. Version precedence: -ldflags override, module version recorded by go install, then the in-tree default tracked in VERSION.

Self-test

After installing, run a built-in end-to-end check against embedded fixtures:

bumblebee selftest
# selftest OK (2 findings in 1ms)

The fixtures live inside the binary, use deliberately fake package names ([email protected]), and make no network calls. A non-zero exit means the local install can no longer detect what it should — a fast pre-deployment smoke test for fleet rollouts.

Profiles

Bumblebee is a one-shot scanner: each invocation performs a single scan and exits. Cadence is the runner's responsibility (cron, launchd, systemd, MDM, etc.). Each record carries profile and a per-root root_kind so receivers can keep populations separate.

Profile Scans Use for
baseline Common global/user package roots, language toolchains, editor extensions, browser extensions, and MCP configs. Recurring lightweight inventory via an external runner.
project Configured development directories, such as ~/code, ~/src, or ~/work. Recurring inventory for known project workspaces.
deep Explicit --root paths, including broad roots like $HOME. On-demand incident or campaign checks, usually with --ecosystem, --exposure-catalog, and --findings-only.

baseline and project refuse bare-home roots; only deep walks them.

Quick start

# Baseline global inventory.
bumblebee scan --profile baseline > inventory.ndjson

# Daily project sweep with explicit roots.
bumblebee scan --profile project \
  --root "$HOME/code" \
  --root "$HOME/Developer"

# Limit a run to selected emitted ecosystems.
bumblebee scan --profile baseline \
  --ecosystem npm,pypi \
  --ecosystem go

# On-demand exposure scan against a published advisory.
bumblebee scan --profile deep \
  --root "$HOME" \
  --exposure-catalog ./catalog.json \
  --max-duration 10m

Preview the resolved roots without scanning:

bumblebee roots --profile baseline
# prints "<root_kind>\t<path>" lines

--root is a filesystem path to scan; repeatable, required for deep, optional for the other profiles. --ecosystem is repeatable and comma-separated. --exposure-catalog accepts a JSON file or a directory of *.json catalogs (merged non-recursively, all files must share schema_version). --findings-only requires --exposure-catalog and suppresses package records while keeping findings. bumblebee scan --help lists every flag.

Output

Records are NDJSON, one per line. Diagnostics go to stderr as NDJSON. Each run ends with a scan_summary record; receivers use it to decide whether to promote a run to current state. See docs/transport.md for HTTPS/file output and docs/state-model.md for the receiver-side current-state model.

Package record:

Example package record
{
  "record_type": "package",
  "record_id": "package:...",
  "schema_version": "0.2.0",
  "scanner_name": "bumblebee",
  "scanner_version": "v0.1.1",
  "run_id": "9b1f0c2e4d5a6b7c8d9e0f1a2b3c4d5e",
  "scan_time": "2026-05-15T18:22:01.482Z",
  "endpoint": {
    "hostname": "alex-mbp",
    "os": "darwin",
    "arch": "arm64",
    "username": "alex",
    "uid": "501",
    "device_id": "MDM-7F4A2B"
  },
  "profile": "project",
  "ecosystem": "npm",
  "package_name": "@tanstack/query-core",
  "normalized_name": "@tanstack/query-core",
  "version": "5.59.20",
  "project_path": "/Users/alex/code/web-app",
  "root_kind": "project_root",
  "package_manager": "pnpm",
  "source_type": "pnpm-lockfile",
  "source_file": "/Users/alex/code/web-app/pnpm-lock.yaml",
  "has_lifecycle_scripts": false,
  "confidence": "high"
}

confidence:

Finding record (exposure-catalog match):

Example finding record
{
  "record_type": "finding",
  "record_id": "finding:...",
  "schema_version": "0.2.0",
  "scanner_name": "bumblebee",
  "scanner_version": "v0.1.1",
  "run_id": "3a8c7d1e9f0b2a4c6d8e0f1a2b3c4d5e",
  "scan_time": "2026-05-15T18:22:01.482Z",
  "endpoint": {
    "hostname": "alex-mbp",
    "os": "darwin",
    "arch": "arm64",
    "username": "alex",
    "uid": "501",
    "device_id": "MDM-7F4A2B"
  },
  "profile": "deep",
  "finding_type": "package_exposure",
  "severity": "critical",
  "catalog_id": "advisory-2026-0042",
  "catalog_name": "example-pkg 1.2.3 (compromised release)",
  "ecosystem": "npm",
  "package_name": "example-pkg",
  "normalized_name": "example-pkg",
  "version": "1.2.3",
  "root_kind": "deep_home_root",
  "project_path": "/Users/alex/code/web-app",
  "source_type": "pnpm-lockfile",
  "source_file": "/Users/alex/code/web-app/pnpm-lock.yaml",
  "confidence": "high",
  "evidence": "exact name+version match (version=1.2.3)"
}

record_id is a content-addressed hash of a canonical identity tuple per record type, stable across runs. Per-record-type field lists and dedupe guidance: docs/state-model.md.

Exposure Catalog Format

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

{
  "schema_version": "0.2.0",
  "entries": [
    {
      "id": "advisory-2026-0042",
      "name": "example-pkg 1.2.3 (compromised release)",
      "ecosystem": "npm",
      "package": "example-pkg",
      "versions": ["1.2.3"],
      "severity": "critical"
    }
  ]
}

The catalog must be a JSON object with schema_version and entries 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

The threat_intel/ directory holds maintained exposure catalogs built from public threat-intelligence reporting on recent supply-chain campaigns, assembled with Perplexity Computer and updated via PRs as new campaigns are reported. See threat_intel/README.md for the current catalog list and review guidance.

License

Apache License 2.0. See LICENSE.

bumblebee (perplexityai/bumblebee)

Searching Database…

What is this?Bumblebee is a read-only inventory collector that scans macOS and Linux developer machines for package, extension, and developer-tool metadata, so responders can quickly check if a machine matches a known supply-chain compromise advisory.
Is this a scanner that modifies my system or just reads files?It is strictly read-only: it parses lockfiles, package-manager metadata, extension manifests, and supported MCP JSON configs on disk. It never runs package manager commands like npm ls or pip show, and never reads source files.
How do I run a scan?Build the single static Go binary from a checkout, then run `bumblebee scan` with one of the baseline, project, or deep profiles; `--root` selects the deep profile's scan paths. You can preview resolved roots before actually scanning, and `bumblebee scan --help` lists all flags.
Do I need anything installed besides Go to build it?Just Go 1.25+; the binary has zero non-stdlib dependencies, so there's nothing else to install.
What file formats or configs does it NOT read?Non-JSON agent configs like Codex's config.toml and Continue's YAML files are not parsed in v0.1, and loose SKILL.md directories without a lock file aren't enumerated either.
Who actually uses this, and when?It's built for incident responders and security teams doing supply-chain exposure checks: baseline and project profiles run recurring lightweight inventory via cron/launchd/systemd, while deep is for on-demand incident or campaign investigations.
Which operating systems does it scan?macOS and Linux developer endpoints; baseline and project profiles refuse to walk bare-home roots, only the deep profile can.
Does it leak secrets from MCP configs it reads?MCP host configs can contain credentials in their env blocks, but Bumblebee parses only what it needs for server inventory and does not emit those secret values in its output records.
What ecosystems and package managers does it cover?npm, pnpm, Yarn, Bun, PyPI, Go modules, RubyGems, Composer, Homebrew, MCP server configs, agent-skill lock files, editor extensions (VS Code, Cursor, Windsurf, VSCodium), and Chromium/Firefox browser extensions.
How does it check if a machine is exposed to a known vulnerable package?You feed it an exposure catalog (JSON with schemaVersion and entries listing ecosystem, name, and version), and bumblebee flags exact matches against on-disk metadata; '--findings-only' suppresses normal package records and shows only matches.
How is scan output formatted for automation?Output is NDJSON, one JSON record per line, with diagnostics on stderr and a final scanSummary record per run so receivers can decide whether to promote the run to current state.
How do I verify my install works correctly before deploying it?Run the built-in self-test against embedded fixtures with deliberately fake package names and no network calls; a non-zero exit means the install can't properly detect what it should, which is a useful pre-deployment smoke test.
Size
0.2 MB
Stars
5.1K
Age
4mo
Commits · 90 Days