Dev Encyclopedia
ArticlesToolsContactAbout

Get notified when new content drops

No spam. Just new articles, tools, and updates straight to your inbox.

Dev Encyclopedia

A reference for builders

Dev.to
Discord
WhatsApp Channel
daily.dev
Hashnode
X

Content

  • Articles
  • Tools
  • About
  • Contact

Connect

  • support@devencyclopedia.com
  • RSS Feed

Legal

  • Privacy Policy
  • Terms of Service
  • Disclaimer

© 2026 Dev Encyclopedia

Back to top ↑
  1. Home
  2. /Blog
  3. /Bumblebee Tutorial: Scan Your Dev Machine for Supply Chain Risks
security24 min read

Bumblebee Tutorial: Scan Your Dev Machine for Supply Chain Risks

How to install and use Bumblebee, Perplexity's open-source scanner for npm, MCP configs, and extensions. Real commands, scan profiles, and incident response setup.

Zeeshan Tofiq
Zeeshan Tofiq
June 21, 2026
On this page

On this page

  • What Bumblebee Scans
  • Installing Bumblebee
  • The Three Scan Profiles
  • Running Your First Scan
  • Triaging Scan Findings
  • A Triage Order That Actually Works
  • Handling False Positives
  • Lockfiles and Transitive Dependencies
  • Using an Exposure Catalog for Incident Response
  • Incident Response for a Confirmed Compromise
  • Checking MCP Configs
  • Scheduling Regular Scans
  • Wiring the Scan Into CI
  • What Bumblebee Doesn't Do
  • What Any Scanner Fundamentally Cannot Catch
  • Frequently Asked Questions

Every time a supply chain attack hits, security teams ask: which dev machines have the compromised package installed? SBOMs cover production builds. EDR covers running processes. Neither checks the actual files sitting on a developer's laptop.

Perplexity open-sourced Bumblebee (May 22, 2026) to fill that gap: a small, read-only Go binary that inventories npm, pnpm, Yarn, Bun, PyPI, Go modules, RubyGems, Composer packages, plus MCP configs and editor/browser extensions. No install scripts, no network calls during scanning, no write operations.

DepScan checks a single package.json in the browser; Bumblebee checks your entire machine across 8 ecosystems. The same supply chain principles apply locally, not just in CI.

💡 TL;DR

Clone, build with Go, run bumblebee scan --profile baseline > inventory.ndjson. Pipe to jq or your SIEM. When a compromise drops, point an exposure catalog at it for instant fleet-wide triage.

What Bumblebee Scans

CategoryWhat it checksExample paths
npm/pnpm/Yarn/BunLockfiles and node_modules~/.npm, ~/code/**/package-lock.json
PyPIpip installs and virtualenvs~/.local/lib/python*, **/venv/
Go modulesgo.sum and module cache~/go/pkg/mod
RubyGemsGem installs~/.gem, **/Gemfile.lock
ComposerVendor directories**/composer.lock
MCP configsAI agent server configsmcp.json, claude_desktop_config.json, .claude.json
Editor extensionsVS Code, Cursor, Windsurf~/.vscode/extensions, ~/.cursor/extensions
Browser extensionsChromium and FirefoxChrome/Default/Extensions, .mozilla/firefox

All scans are read-only. Bumblebee never executes a package manager or runs install scripts, which sidesteps attacks that hide in preinstall/postinstall hooks.

Installing Bumblebee

  1. 1

    Clone and Build (requires Go 1.25+)

    bash
    git clone https://github.com/pplx-oss/bumblebee.git
    cd bumblebee
    go build -o bumblebee ./cmd/bumblebee
  2. 2

    Verify the Binary

    bash
    ./bumblebee --version

    You should see the version string and build commit. If not, confirm your Go version with go version (must be 1.25 or later).

  3. 3

    (Optional) Move to PATH

    bash
    sudo mv ./bumblebee /usr/local/bin/

ℹ Info

No install script that pipes to a shell. You build from source, which is intentional for a security tool: you can read the code before trusting the binary.

The Three Scan Profiles

ProfileWhat it scansBest for
baselineCommon global/user package roots, language toolchains, extensions, MCP configsDaily cron, routine inventory
projectTargeted sweep of specific dev directories (~/code, ~/src)Auditing work projects on demand
deepOperator-supplied roots including full home directoryActive incident response

⚠ Warning

baseline and project refuse bare-home roots. Only deep will walk them, since scanning an entire home directory is significantly more invasive and slower.

Running Your First Scan

  1. 1

    Run a Baseline Scan

    bash
    bumblebee scan --profile baseline > inventory.ndjson

    Output is NDJSON (one JSON record per line). Diagnostics go to stderr, inventory goes to stdout. This makes piping clean.

  2. 2

    Filter Output with jq

    Pull only npm packages:

    bash
    cat inventory.ndjson | jq -r 'select(.ecosystem == "npm") | .package'

    List all discovered MCP config file paths:

    bash
    cat inventory.ndjson | jq -r 'select(.ecosystem == "mcp") | .path'
  3. 3

    Count Packages by Ecosystem

    bash
    cat inventory.ndjson | jq -r '.ecosystem' | sort | uniq -c | sort -rn

    This gives you a quick breakdown of how many packages each ecosystem contributes to your machine's footprint. A typical active dev machine shows 500+ npm packages, a handful of Go modules, and 10-30 editor extensions.

Triaging Scan Findings

The first baseline scan on a working developer machine is a shock. Thousands of records, most of them transitive npm packages you have never heard of, plus every editor extension you installed once and forgot about. That is not a bug in the tool. An inventory is supposed to be exhaustive.

The mistake teams make here is trying to read the output. You do not read an inventory, you query it. Bumblebee's job ends at producing accurate NDJSON; deciding what matters is a separate step, and it is the step where most rollouts stall.

Before writing any query, look at exactly one record so you know what fields you actually have to work with:

bash
head -1 inventory.ndjson | jq .

Do this every time you upgrade the binary. Field names in a young tool are the least stable part of its interface, and a jq filter that silently returns nothing because a key was renamed is worse than no check at all, because it looks like a clean result.

A Triage Order That Actually Works

Work outward from the things that can execute code with the least warning, not alphabetically and not by ecosystem size. In rough priority order:

  1. MCP server configs. These grant an AI agent the ability to run commands and read your project context. The blast radius is the largest and the population is the smallest, usually under a dozen entries, so this is the highest value per minute of review.
  2. Browser and editor extensions. They run with broad permissions, update automatically without a lockfile, and nobody reviews their diffs. A handful of entries, high impact each.
  3. Global package installs. Anything installed with npm i -g, pipx, or go install sits on your PATH and gets invoked by shell aliases and build scripts. These bypass every per-project control you have.
  4. Direct project dependencies. Packages you or a teammate deliberately added. Moderate count, and you can usually explain why each one is there.
  5. Transitive dependencies. The largest bucket by far, and the one you triage last, because you triage it with a catalog query rather than with your eyes.

If you invert that order and start with npm packages because there are the most of them, you will burn a day and never reach the MCP configs, which are the part of a modern dev machine that changed most recently and got reviewed least.

Handling False Positives

An inventory scanner produces a different class of false positive than a malware scanner. It is not telling you something is malicious, it is telling you something exists. The false positive appears one layer up, when you or your catalog decides that an existing thing is suspicious.

These are the patterns that reliably generate noise on a real machine:

The five noise sources that show up on almost every first scan.
What you seeWhy it looks alarmingWhy it usually is not
The same package listed many times at different versionsLooks like a version conflict or a shadowed installNested node_modules and pnpm's content-addressed store legitimately hold multiple versions side by side
Packages under a directory you have not touched in a yearLooks like an unmanaged, unpatched installUsually an archived project or a stale clone; real, but low risk until someone runs its build
A vulnerable version that a catalog flagsReads as a live exposureA vulnerable version present on disk is not the same as a vulnerable version reachable at runtime; confirm the path before escalating
Extensions you do not recogniseLooks like something installed itselfMost editors auto-install extension dependencies and pack extensions; check the publisher before assuming compromise
Hits inside test fixtures or sample projectsSame package name as a real advisory hitFixture directories often pin deliberately old versions; scope your paths rather than deleting the fixture

The practical fix for all five is scoping. Use --profile project against your actual working directories for routine checks and save --profile deep for the moments when completeness genuinely beats signal-to-noise, which is to say incidents.

Resist the urge to build a permanent suppression list early. Suppressions written in the first week encode assumptions you have not tested yet, and they are the single most common reason a scanner goes quiet right before it was needed.

⚠ Never suppress by package name alone

If you must suppress, suppress a specific package at a specific version under a specific path. A name-only suppression follows that package into every future version, including the compromised one you installed the suppression to help you find.

Lockfiles and Transitive Dependencies

Almost every serious npm compromise in recent years reached its victims transitively. Nobody deliberately installed the malicious package; they installed something four levels above it. That is why an inventory built from lockfiles and installed trees tells you more than a list of your direct dependencies ever will.

The distinction matters enough to be explicit about it, because the two files answer completely different questions:

package.jsonLockfile (package-lock, pnpm-lock, yarn.lock)
What it recordsYour intent, as version rangesThe exact resolved versions of every package in the tree
Transitive coverageNone, only what you asked for directlyComplete, every level of the tree
Answers 'am I exposed?'No, a range like ^1.4.0 could resolve to a clean or compromised versionYes, it names the version that was actually resolved
Changes whenA human edits itAny install, upgrade, or bot PR touches the tree
Reviewed in pull requestsAlmost alwaysAlmost never, which is exactly the problem

A scanner reading lockfiles and installed trees closes that gap without asking anyone to review a 4,000 line diff. Once you have the inventory, the follow-up question is always the same: who pulled this in?

Bumblebee tells you a package is present and where. Your package manager tells you why it is present. Use them together:

bash
# npm: show the full dependency path to a package
npm ls <package-name> --all

# pnpm: explain why a package is in the tree
pnpm why <package-name>

# yarn (berry): same question
yarn why <package-name>

If the answer is a single direct dependency, you can usually resolve it by bumping that one package. If the answer is nine different paths through your tooling, you are looking at an override or a resolution pin instead.

One inventory-level check worth running regularly is a duplicate-version count. A package resolving to many versions across your projects means an advisory against any one of them requires per-project work rather than a single upgrade:

bash
cat inventory.ndjson \
  | jq -r 'select(.ecosystem == "npm") | .package' \
  | sort | uniq -c | sort -rn | head -20

ℹ Lockfiles do not protect you from a compromised version

A lockfile pins a version, it does not vouch for it. If the pinned version is the malicious one, the lockfile faithfully reproduces the compromise on every machine and in every CI run. Pinning improves reproducibility, not safety, which is the same trap covered in our breakdown of npm postinstall attacks.

Using an Exposure Catalog for Incident Response

When a new supply chain compromise drops, the first question is: are we affected? Bumblebee's exposure catalog feature turns that into a 5-second answer instead of a manual grep across every developer machine.

Create a JSON file describing the compromised packages:

json — advisory.json
{
  "schema_version": "1.0",
  "entries": [
    {
      "id": "ADV-2026-0042",
      "name": "colors-hijack",
      "ecosystem": "npm",
      "package": "colors",
      "versions": [">=1.4.1 <1.4.3"],
      "severity": "critical"
    },
    {
      "id": "ADV-2026-0043",
      "name": "faker-hijack",
      "ecosystem": "npm",
      "package": "faker",
      "versions": [">=6.6.6"],
      "severity": "high"
    }
  ]
}

Then run a targeted deep scan against it:

bash
bumblebee scan --profile deep --ecosystem npm --exposure-catalog advisory.json --findings-only

The --findings-only flag means you only see records that match the catalog. No match, no output. This turns "is anyone running the compromised version?" into a trivially scriptable check you can run across your fleet.

Incident Response for a Confirmed Compromise

A catalog hit is the start of the work, not the end of it. Once you have confirmed that a compromised package version is genuinely present on a machine, the clock is running on credential theft, and the order you do things in matters.

The single most important thing to internalise: uninstalling the package does not undo what it already did. If a malicious install script executed, it ran with your user's permissions, and it had access to everything your user has access to. Treat removal as cleanup, not containment.

  1. 1

    Preserve the evidence before you change anything

    Save the scan output that produced the hit, with a timestamp. If you delete the package first, you lose the only record of what version was where, and you will need that record for the postmortem and possibly for a customer notification.

    bash
    bumblebee scan --profile deep --exposure-catalog advisory.json --findings-only \
      > "findings-$(hostname)-$(date +%Y%m%dT%H%M%S).ndjson"
  2. 2

    Rotate credentials that the machine could reach

    This comes before cleanup, because it is the part that stops ongoing damage. Assume anything readable from the developer's home directory is gone.

    • npm, PyPI, RubyGems, and container registry publish tokens
    • Cloud provider access keys, including anything cached by a CLI in the home directory
    • SSH keys and any signing keys used for commits or releases
    • Personal access tokens for GitHub, GitLab, and CI providers
    • Session cookies and browser-stored credentials if browser extensions were in scope
    • Environment variables checked into local .env files, which are usually the fastest thing for a payload to read
  3. 3

    Determine the exposure window

    Find out when the compromised version was installed, not when the advisory was published. Lockfile history in git is usually the most reliable source, because it records the exact resolved version and the commit date it landed.

    bash
    git log -p --follow -- package-lock.json | grep -n "<package-name>" | head -40

    Everything that ran between that date and now is inside the window: every CI job, every local build, every developer who pulled the branch.

  4. 4

    Widen the scan to the rest of the fleet

    One confirmed machine almost never means one affected machine. Push the same catalog to every endpoint and collect findings centrally, because a partial answer here produces a false all-clear that is worse than no answer.

    bash
    bumblebee scan --profile deep --ecosystem npm \
      --exposure-catalog advisory.json --findings-only \
      | tee "/var/log/bumblebee/ir-$(hostname).ndjson"
  5. 5

    Remove the package and rebuild from a clean lockfile

    Delete the installed tree rather than upgrading in place, because an upgrade leaves whatever the old version wrote on disk. Then reinstall with scripts disabled so you are not re-running install hooks during recovery.

    bash
    rm -rf node_modules
    npm ci --ignore-scripts

    Also clear the package manager caches. A poisoned tarball sitting in a local cache will happily reinstall itself into your clean tree.

  6. 6

    Re-scan to confirm the machine is clean

    Run the same catalog again and require empty output. --findings-only makes this a clean pass/fail signal: no output means no match. Record the clean result alongside the original finding so the timeline is complete.

Once the immediate response is done, work through the follow-up items while the details are still fresh. These are the ones teams routinely skip and later wish they had not:

  • Audit registry publish history for every rotated token, in case the attacker published under your identity
  • Check CI logs across the exposure window for outbound requests to unfamiliar domains during install steps
  • Review git history for commits or tags you cannot attribute to a real person
  • Confirm no long-lived secrets were baked into build artifacts released during the window
  • Add the compromised package and version range to your permanent catalog so re-introduction is caught automatically
  • Write the timeline down while you still remember it, including what the scan found and when

CI runners deserve the same treatment as laptops here. They hold the most valuable credentials on the network and they install dependencies far more often than any human does, which is why hardening your GitHub Actions workflows belongs in the same remediation pass.

Checking MCP Configs

MCP (Model Context Protocol) configs define which external servers your AI coding tools connect to. After the ContextCrush incident in March 2026, this became a real attack surface: a malicious MCP server can exfiltrate code context, inject prompts, or pivot into your development environment.

Bumblebee checks these config files: mcp.json, .mcp.json, claude_desktop_config.json, mcp_config.json, mcp_settings.json, cline_mcp_settings.json, Gemini CLI/Code Assist settings, and ~/.claude.json.

bash
bumblebee scan --profile baseline --ecosystem mcp

This outputs every MCP server config found on the machine, with the server URL and transport type for each entry. You can then cross-reference against known compromised servers or your organization's allowlist.

For a quick browser-based check of a single config file without installing anything, use the MCPConfigCheck tool.

Scheduling Regular Scans

A one-off scan is useful during an incident. A daily scan turns Bumblebee into continuous inventory. Here's a crontab entry that runs at 2 AM every day:

bash — crontab -e
0 2 * * * /usr/local/bin/bumblebee scan --profile baseline > /var/log/bumblebee/inventory-$(date +\%Y-\%m-\%d).ndjson 2>&1

On macOS, launchd is more reliable than cron for scheduled tasks. Here's a plist that achieves the same thing:

xml — ~/Library/LaunchAgents/com.bumblebee.scan.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.bumblebee.scan</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/bumblebee</string>
    <string>scan</string>
    <string>--profile</string>
    <string>baseline</string>
  </array>
  <key>StandardOutPath</key>
  <string>/var/log/bumblebee/inventory.ndjson</string>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Hour</key>
    <integer>2</integer>
    <key>Minute</key>
    <integer>0</integer>
  </dict>
</dict>
</plist>

For security teams managing a fleet, forward these NDJSON files to a central log aggregator (Splunk, Elastic, or even a simple S3 bucket). When an advisory drops, you query your existing inventory instead of scanning every machine in real time.

Wiring the Scan Into CI

A scheduled scan tells you about a problem the next morning. A scan in CI tells you before the code merges, which is the difference between a fix and an incident. The pattern is the same one you already used for incident response: a catalog plus --findings-only, evaluated as pass or fail.

Rather than depending on a specific exit code, treat any output at all as a failure. --findings-only prints nothing when nothing matches, so a line count is a reliable gate that will not break if the tool's exit-code behaviour changes between versions:

bash — scripts/supply-chain-gate.sh
#!/usr/bin/env bash
set -euo pipefail

CATALOG="${1:-security/advisories.json}"
OUT="$(mktemp)"

bumblebee scan \
  --profile project \
  --exposure-catalog "$CATALOG" \
  --findings-only > "$OUT"

if [ -s "$OUT" ]; then
  echo "Supply chain gate FAILED: known-bad packages present"
  cat "$OUT"
  exit 1
fi

echo "Supply chain gate passed: no catalog matches"

The runner needs the dependency tree on disk before the scan runs, so install first. Install with scripts disabled, because you do not want a lifecycle hook from an unreviewed package executing on a runner that holds your deploy credentials.

Check bumblebee scan --help on the version you pinned before wiring this up. Profiles differ in whether they infer roots or expect you to name them, and a CI job that scans the wrong directory reports a clean result forever.

yaml — .github/workflows/supply-chain.yml
name: Supply chain gate

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Install dependencies without running scripts
        run: npm ci --ignore-scripts

      - uses: actions/setup-go@v5
        with:
          go-version: "1.25"

      - name: Build Bumblebee from a pinned commit
        run: |
          git clone https://github.com/pplx-oss/bumblebee.git /tmp/bumblebee
          git -C /tmp/bumblebee checkout "${{ vars.BUMBLEBEE_COMMIT }}"
          go build -C /tmp/bumblebee -o /tmp/bin/bumblebee ./cmd/bumblebee
          echo "/tmp/bin" >> "$GITHUB_PATH"

      - name: Run supply chain gate
        run: bash scripts/supply-chain-gate.sh security/advisories.json

Pinning the commit rather than tracking the default branch matters more than it looks. A security tool you rebuild from HEAD on every run is itself an unpinned dependency with the ability to read your entire workspace, which is the exact category of risk you installed it to manage.

Keep the catalog in the repository next to the workflow. Adding a compromised package to a JSON file in a pull request is a reviewable, auditable action, and it means the gate strengthens through the same process as the rest of your code.

Match the profile to the context; running deep in a pull request gate is the fastest way to get the gate disabled.
Where it runsProfileWhy
Pull requestprojectFast, scoped to the checkout, keeps the feedback loop tight enough that people do not route around it
Nightly scheduled jobbaselineCatches packages that entered through global installs or tooling rather than the repo
Developer laptop, on demandprojectSame gate a developer can run locally before pushing, so CI is never the first place they hear about it
Active incidentdeepCompleteness beats speed, and the noise is acceptable because someone is reading every line

⚠ A gate that fails constantly is a gate that gets removed

Start the CI check in report-only mode: run it, print the findings, but do not fail the build. Watch it for a week. When it produces zero false failures across a normal week of merges, flip it to blocking. A gate that cries wolf on day one will be commented out by day three, and nobody will remember to turn it back on.

What Bumblebee Doesn't Do

  • Windows support (macOS and Linux only currently)
  • Runtime behavior detection (it reads files, doesn't execute them)
  • SBOM replacement (covers dev endpoints, not production builds)
  • EDR replacement (reads on-disk state, doesn't monitor running processes)
  • Network analysis (doesn't check what packages do when executed)
  • Remediation (reports findings, doesn't auto-fix or quarantine)

Bumblebee fills the specific gap between SBOMs and EDR. It tells you what's on disk right now, not what ran or what shipped. Pair it with a VS Code extension audit for a more complete picture of your local attack surface.

What Any Scanner Fundamentally Cannot Catch

The previous section lists what this particular tool has not built yet. This section is different: these are limits that no inventory scanner can engineer its way past, and knowing them is what keeps a green scan from turning into false confidence.

An inventory scanner answers exactly one question well: what is on this disk right now, by name and version. Every question that depends on behaviour, intent, or timing is outside its reach by construction.

  • A compromise nobody has published yet. Catalogs are lists of known-bad things. The window between a malicious version going live and an advisory naming it is measured in days, and during that window every catalog-based check returns clean, correctly and uselessly.
  • Malicious code in a legitimately named package. If left-pad@1.3.0 is the version everyone expects and the tarball was swapped, the inventory shows a normal package at a normal version. Name and version matching cannot see inside the file.
  • Anything that already ran. A payload that executed during install and deleted itself leaves nothing on disk to inventory. Read-only file scanning is a snapshot of surviving state, not a record of history.
  • Payloads that live outside package directories. Malware that writes to a temp directory, installs a launch agent, or appends a line to your shell profile is not a package, so it is not in a package inventory.
  • The gap between the published tarball and the source repository. What ships to a registry is not guaranteed to match what is visible on GitHub. An inventory records what you installed, not whether what you installed matches what you reviewed.
  • Compromise of the machine doing the scanning. Any tool running as your user reports what the operating system tells it. If the host is already owned at a level that can lie to userspace, the scan output is just as compromised as everything else on the box.

None of that makes the scan worthless. It makes it one layer. The layer above it is behavioural (what the code does when it runs), the layer below it is provenance (whether the artifact you installed is the artifact that was built from the source you read), and a scanner sits squarely between the two without replacing either.

Provenance is where the biggest gains are available right now, because it is the layer most teams have nothing at all. Signed builds, published attestations, and integrity checks on anything you load from a third party all attack the problem earlier than a scan can. Subresource integrity is the cheapest version of this idea, and you can generate SRI hashes for third-party scripts in a minute.

The honest framing to carry into a security review: a clean Bumblebee scan means "none of the things we currently know are bad are sitting on this machine." That is genuinely valuable and it is not the same sentence as "this machine is clean." Attacks that hide in install-time lifecycle hooks or in a compromised CDN delivery path are specifically designed to be true and invisible at the same time.

🚫 Do not treat a clean scan as an all-clear during an active incident

During a live incident, the advisory is usually incomplete for the first 24 to 48 hours: package lists grow, version ranges widen, and new ecosystems get added. Re-run your catalog scan as the advisory is updated rather than trusting the result from the first hour. A clean scan against an incomplete catalog is the most common way teams conclude they were unaffected when they were not.

Frequently Asked Questions

What does Bumblebee scan for?

Bumblebee inventories installed packages across 8 ecosystems (npm, pnpm, Yarn, Bun, PyPI, Go modules, RubyGems, Composer), MCP configs, and editor/browser extensions. It's read-only and never executes package managers or install scripts.

Is Bumblebee safe to run?

Yes. It's read-only by design and never executes install scripts or package managers. The binary makes no network calls during scanning. It's Apache 2.0 licensed and fully source-available, so you can audit the code before building.

What is the difference between Bumblebee and an SBOM?

SBOMs inventory your production build artifacts (what ships to users). Bumblebee inventories your developer machine (what's installed locally). They complement each other: an SBOM tells you what's in the container you deployed, while Bumblebee tells you what's sitting on every engineer's laptop right now.

How do I check my MCP configs for known compromises?

Run the following command to list all MCP server configs found on your machine:

bash
bumblebee scan --profile baseline --ecosystem mcp

For a browser-based check without installing anything, use the MCPConfigCheck tool. It validates a single config file against known-bad server patterns.

Does Bumblebee work on Windows?

Not currently. Bumblebee supports macOS and Linux. Windows support is tracked in the GitHub issues but has no release date yet. If you need Windows coverage, you can run it inside WSL2 to scan packages visible from the Linux filesystem.

How do I cut down the noise from my first scan?

Scope before you suppress. A baseline scan on an active machine returns thousands of records by design, and most of the noise comes from stale clones, test fixtures, and duplicate transitive versions rather than from anything the tool got wrong.

  • Use `--profile project` for routine checks so you are looking at the directories you actually work in, and reserve --profile deep for incidents
  • Filter by ecosystem with --ecosystem when you are chasing a specific advisory, instead of reading the full inventory
  • Use an exposure catalog with `--findings-only` so the default output is empty and any output at all is meaningful
  • Triage by blast radius, starting with MCP configs and extensions rather than with the largest ecosystem

If you do end up writing suppressions, pin them to a package, a version, and a path. A suppression by package name alone will still be active on the day a compromised version of that package arrives.

Should I run this in CI or only on developer machines?

Both, for different reasons. CI catches a bad dependency before it merges, and developer machines catch everything CI never sees: global installs, editor extensions, browser extensions, and MCP servers, none of which appear in a repository checkout.

CI scanEndpoint scan
ScopeThe repository checkout and its installed treeThe whole machine across all ecosystems and tooling
CatchesA compromised dependency entering through a pull requestAnything already installed, however it arrived
MissesGlobal installs, extensions, MCP configsA bad dependency in a branch nobody has checked out locally
TimingBefore merge, blockingScheduled or on demand, reporting
Best profileprojectbaseline for routine runs, deep for incidents

If you can only do one to start with, do CI, because it prevents new exposure rather than just measuring existing exposure. Add endpoint scanning as soon as you can, since the extension and MCP surface is invisible to CI entirely.

If the scan comes back clean, am I actually safe?

A clean scan means something precise and narrower than it sounds: none of the packages your catalog currently lists as bad are present on this disk right now. It is not a statement about whether the code you have is safe.

Three gaps survive a clean result. A compromise published in the last few days may not be in any catalog yet. A malicious tarball published under a normal package name at a normal version looks identical to a clean one in an inventory. And a payload that ran during install and removed itself leaves nothing left to find.

Pair the scan with controls that work at different layers: disable install scripts by default, review lockfile diffs like real code, and verify provenance for anything you load from a third party. The source map exposure problem is a good reminder that what actually ships often diverges from what you think you published.

Zeeshan Tofiq

Zeeshan Tofiq

Full Stack Developer

Full stack developer with over 6 years of experience building production applications. Writes practical guides on JavaScript, TypeScript, React, Node.js, and cloud infrastructure. Focused on helping developers solve real problems with clean, maintainable code.

Enjoyed this article?

Get practical dev guides, tool updates, and new articles delivered straight to your inbox. No spam, unsubscribe anytime.

Related Articles

security

GitHub Actions Security: 7 Misconfigurations to Avoid

The 7 GitHub Actions misconfigurations behind real supply chain attacks: weak GITHUB_TOKEN scope, pull_request_target, unpinned actions, script injection.

Jun 12, 2026·11 min read
security

WordPress CDN Supply Chain Attack 2026: What Happened and How to Check Your Site

The OptinMonster, TrustPulse, and PushEngage supply chain attack (June 2026) hit 1.2M sites. Here's exactly how it worked, how to check if you were compromised, and how to recover.

Jun 21, 2026·16 min read
security

How to Audit Your VS Code Extensions for Security

The GitHub breach happened through a VS Code extension. Here's how to check what you have installed and reduce your exposure in 10 minutes.

May 30, 2026·16 min read

On this page

  • What Bumblebee Scans
  • Installing Bumblebee
  • The Three Scan Profiles
  • Running Your First Scan
  • Triaging Scan Findings
  • A Triage Order That Actually Works
  • Handling False Positives
  • Lockfiles and Transitive Dependencies
  • Using an Exposure Catalog for Incident Response
  • Incident Response for a Confirmed Compromise
  • Checking MCP Configs
  • Scheduling Regular Scans
  • Wiring the Scan Into CI
  • What Bumblebee Doesn't Do
  • What Any Scanner Fundamentally Cannot Catch
  • Frequently Asked Questions