Bumblebee Documentation

repository·main·Indexed 26 days ago

https://github.com/perplexityai/bumblebee

Bumblebee is a read-only inventory collector for macOS and Linux developer endpoints designed to assist in supply-chain security response. It identifies installed packages, extensions, and developer tools across various ecosystems by scanning filesystem roots and emitting results in NDJSON format. The tool includes three scan profiles (baseline, project, and deep) and can match inventory against JSON exposure catalogs to identify malicious packages. It also provides a utility, osvcatalog, to convert OSV snapshots into compatible offline catalogs.

Tokens
10.7K
Snippets
16
Records
51
Agent score
85%

What's inside bumblebee

  1. Understand Bumblebee ecosystem and source toolchain mapping

    main

    Bumblebee uses OSV-aligned ecosystem identifiers (e.g., npm, pypi, go, rubygems, packagist).

    Note that while pnpm, Yarn, and Bun use different lockfiles, they all install from the npm registry. Therefore, their records emit ecosystem=npm. To distinguish between them, Bumblebee uses the package_manager (npm, pnpm, yarn, or bun) and source_type (e.g., pnpm-lockfile, yarn-lockfile) fields.

    When using the --ecosystem CLI flag, use the OSV-aligned values; --ecosystem npm will cover all four npm-based package managers.

  2. Handle macOS TCC and Full Disk Access

    main

    The bumblebee walker is read-only and automatically skips sensitive credential directories (e.g., .ssh, .aws, .config/gcloud) and .env files. However, paths under ~/Library/, ~/Documents/, ~/Desktop/, and ~/Downloads/ are protected by macOS TCC.

    To allow scanning of TCC-protected paths (required for deep profiles):

    • Grant the bumblebee binary Full Disk Access via MDM (using a Privacy Preferences Policy Control payload).
    • Note: A LaunchDaemon running as root still requires Full Disk Access to reach TCC-protected paths.

    Alternative:

    • Restrict your --root to development trees only and accept that TCC-protected paths will be out of scope. The walker will emit a debug-level diagnostic for unreadable paths but will not abort the scan.
  3. Deploy bumblebee on macOS using launchd

    main

    Since bumblebee is a one-shot binary without a built-in scheduler, use macOS launchd to automate scans. Deployment typically follows one of three profile patterns:

    • baseline: Scans global/user package-manager and toolchain roots. Recommended interval: every 6 hours (StartInterval 21600) plus RunAtLoad.
    • project: Scans configured developer/project roots. Recommended interval: daily (StartInterval 86400) or every 12 hours (43200).
    • deep: Performs an exposure scan over operator-supplied roots. Typically run on-demand during security campaigns. Use --findings-only for low-cost streams.
  4. Analyze Editor Extensions (VS Code, Cursor, etc.)

    main

    Bumblebee scans for extensions in popular editor environments by reading <extensions-root>/<publisher>.<name>-<version>[-<platform>]/package.json.

    Supported Editor Roots:

    • VS Code: ~/.vscode/extensions, ~/.vscode-server/extensions, ~/.vscode-insiders/extensions
    • Cursor: ~/.cursor/extensions, ~/.cursor-server/extensions
    • Windsurf: ~/.windsurf/extensions, ~/.windsurf-server/extensions
    • VSCodium: ~/.vscodium/extensions

    Captured Fields:

    • package_name: The full publisher.name identifier.
    • version: The extension version.
    • package_manager: Inferred from the path (e.g., vscode, cursor, windsurf, vscodium).

    Note on Vendored Dependencies: If an extension contains a node_modules/ tree or lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml), Bumblebee will dispatch the generic npm/pnpm/yarn/bun parsers. This results in both an editor-extension record for the extension itself AND ecosystem=npm records for the vendored dependencies. This is intentional to capture potential supply-chain exposures bundled within extensions.

  5. Generate catalogs from OSV using osvcatalog

    main

    The tools/osvcatalog tool converts a local OSV (Open Source Vulnerabilities) snapshot into an offline Bumblebee catalog. It only emits malicious-package records (those with MAL- IDs or aliases) with a severity: "critical". Bumblebee does not query osv.dev during scans; it uses these pre-generated files.

    Input Shapes

    1. OSSF malicious-packages repo (Recommended): Best for covering all ecosystems in a single tree.
    2. OSV per-ecosystem dump: Best for a single ecosystem provided as a zip archive.

    Ecosystem Mapping

    When generating catalogs, OSV ecosystems map to Bumblebee as follows:

    • npm, PyPI $\rightarrow$ pypi
    • Go $\rightarrow$ go
    • RubyGems $\rightarrow$ rubygems
    • Packagist $\rightarrow$ packagist
    • VSCode $\rightarrow$ editor-extension

    Output Details

    • Records with an introduced: "0" event (declaring all versions affected) are emitted with "versions": ["*"].
    • Records with only bounded ranges and no enumerated affected[].versions are skipped.
    • The output includes _comment records detailing scope, per-ecosystem counts, skip-reason breakdowns, and optional -source provenance labels.
    # Using OSSF malicious-packages (Recommended)
    git clone --filter=blob:none --sparse --depth=1 \
      https://github.com/ossf/malicious-packages.git mp
    git -C mp sparse-checkout set osv/malicious
    go run ./tools/osvcatalog \
      -source "https://github.com/ossf/malicious-packages@$(git -C mp rev-parse HEAD)" \
      -o threat_intel/osv-malicious.json mp/osv/malicious/
    
    # Using OSV per-ecosystem dump (e.g., npm)
    curl -fsSLO https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip
    go run ./tools/osvcatalog -o threat_intel/osv-npm-malicious.json npm/all.zip
  6. Verify installation with selftest

    main

    After installation, run the built-in selftest command to perform an end-to-end check against embedded fixtures. This is a fast, network-free smoke test to ensure the binary can correctly detect known fake packages (e.g., bumblebee-selftest-evil@0.0.0). A non-zero exit code indicates a local installation issue.

    bumblebee selftest
  7. Choose between LaunchDaemon and LaunchAgent for macOS deployment

    main

    Select the appropriate launchd mechanism based on your required scope:

    • LaunchDaemon (/Library/LaunchDaemons/): Runs as root. Use this to scan every user on the machine by passing the --all-users flag. Note that endpoint.username will reflect the scanner identity (root) rather than the user being scanned; use project_path or source_file for user attribution.
    • LaunchAgent (/Library/LaunchAgents/): Runs per logged-in user under their own UID. This is the safer default as it maintains per-user scope and user-owned TCC prompts.
  8. Configure Homebrew coverage with --root

    main

    Bumblebee reads Homebrew Formulae via <prefix>/Cellar/<formula>/<version>/INSTALL_RECEIPT.json and Casks via <prefix>/Caskroom/<token>/.metadata/<version>/<timestamp>/Casks/<token>.{internal.json,json,rb}.

    By default, the baseline profile covers Apple Silicon macOS (/opt/homebrew), Intel macOS (/usr/local), and Linuxbrew. Bumblebee does not infer custom Homebrew prefixes from environment variables. If you use a custom Homebrew installation, you must provide the Cellar and Caskroom paths using the --root flag to ensure coverage.

  9. Verify a bumblebee deployment on macOS

    main

    Follow these steps to verify that your launchd deployment is working correctly:

    1. Load the plist:
      • For a LaunchAgent: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.bumblebee.baseline.plist
      • For a LaunchDaemon: sudo launchctl bootstrap system /Library/LaunchDaemons/...plist
    2. Trigger a manual run: launchctl kickstart -k gui/$(id -u)/com.example.bumblebee.baseline
    3. Check logs: Monitor stderr for the scan complete: info diagnostic.
    4. Confirm receipt: Verify the receiver has processed a record_type=scan_summary line with status=complete for the specific run_id. This marker should be used to promote the run to the current state in your pipeline.
  10. Analyze Browser Extensions in Inventory

    main

    Bumblebee scans Chromium-family (Chrome, Brave, Edge, etc.) and Firefox-family (Firefox, LibreWolf, etc.) browser extensions.

    • Chromium-family: Reads <profile>/Extensions/<extension_id>/<version>/manifest.json. It resolves localized names using _locales/<default_locale>/messages.json (falling back to en).
    • Firefox-family: Reads <profile>/extensions.json. It emits one record per add-on with type=extension and skips themes/system add-ons.

    Key Data Fields:

    • source_type: Always browser-extension.
    • root_kind: Always browser_extension_root.
    • package_manager: Either chromium-extension or firefox-extension (does not specify the brand).
    • normalized_name: Contains the extension ID.
    • source_file: Contains the per-browser profile path, which can be used to recover the specific browser brand.

    Scanning Scope:

    • Baseline: Includes curated roots like Default and Profile 1..9 for Chromium and Firefox profile parents. To scan other profiles, use the --root flag.
    • Deep Scan (--profile deep): On macOS, the scanner is restricted to specific path shapes to avoid TCC-protected files (like Cookies or Login Data). On macOS, deep scans only pick up extensions if the specific Extensions/ directory is passed via --root. On Linux, it descends into ~/.config/<browser>/<profile>/ but only opens matched manifest files.
    # All browser-extension records
    jq 'select(.record_type == "package" and .source_type == "browser-extension")' inventory.ndjson
    
    # One row per (install mechanism, extension id, version) — the browser brand
    # the browser brand lives in source_file, since package_manager is chromium-extension/firefox-extension
    jq -r 'select(.source_type == "browser-extension")
           | [.package_manager, .normalized_name, .version, .package_name, .source_file] | @tsv' inventory.ndjson
  11. Choose a Bumblebee scan profile

    main

    Bumblebee provides three scan profiles that determine which files and directories are walked:

    ProfileDescription
    baselineScans global system/user locations: Homebrew (Cellar/Caskroom), Python (/Library/Python, ~/.local/lib/python*, pyenv), language version managers (asdf, nvm, rbenv, rvm), Cargo (~/.cargo), Go (~/go), editor extensions, MCP configs, agent-skill lock locations, and browser extensions. It does not scan project trees.
    projectScans configured developer/project roots (e.g., ~/code, ~/src, ~/Projects) and applies all ecosystem parsers within those trees.
    deepScans operator-supplied roots (typically a bare home directory). Recommended to use with --exposure-catalog to emit record_type=finding records.

    Note: source_type values remain consistent across all profiles; only the visited file set changes.

  12. Configure bumblebee output sinks

    main

    Bumblebee writes NDJSON records to one of three supported sinks. Note that the collector does not spool, retry in the background, or maintain an on-disk queue; it runs once and exits.

    1. stdout: Best for local testing or remote execution. Diagnostics are sent to stderr as NDJSON.
    2. file: Use --output file and --output-file PATH. This is ideal if the endpoint already has a log shipper. Use --append to add to an existing file.
    3. http: Use --output http and --http-url URL. This is the preferred method when no existing log pipeline exists.