Catching Miasma with Koban fleet rules
We expanded the built-in known malicious JavaScript package list and here are the exact YAML rules you can deploy today for the June 2026 waves.
- koban
- rules
- npm
- miasma
- supply-chain
The last two posts covered the June 1 Red Hat compromise and the Phantom Gyp binding.gyp follow-on. This one is the practical bit: what the rules look like and how to get them running on your Macs.
Koban rules run locally on the agent against the inventory it builds from disk. For JavaScript packages that means lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb) plus the metadata the collector pulls out of them. The agent diffs snapshots and evaluates rules on added, modified, removed, or present items. Findings sync to Fleet when you have enrollment and sync turned on.
What the defaults already do
Koban ships a rule called packages.known-malicious-javascript on the javascriptPackages surface. It matches package names against a list of prefixes and exact strings using fieldContainsAny. The rule fires on added, modified, and present so it catches both new resolutions and packages that were already present when the rule arrived.
We keep that list updated as campaigns are disclosed. For the Miasma activity we added the new indicators from the Red Hat wave and the June 3 binding.gyp spread:
- @vapi-ai/server-sdk
- ai-sdk-ollama
- autotel and awaitly (plus common family members)
- eslint-plugin-executable-stories
- node-env-resolver
- and the @redhat-cloud-services/ prefix that was already present
The rule comes in at suspicious severity with the title "Known malicious JavaScript package pattern" and a rationale that calls out the public supply-chain campaign.
If you run with the shipped defaults (most teams start here), you already get coverage for the names without extra work. The test in the backend confirms the values are present.
Additional rules worth adding
Name matching on known bad is the highest signal. You can layer a few more targeted rules for the campaign and for general install-hook hygiene.
Here is a compact bundle snippet you can paste into your Fleet org config or a test local koban.yaml. Bump the generation when you publish.
generation: "2026-06-06"
watch:
debounceMilliseconds: 800
pollIntervalSeconds: 300
sync:
maxBatchBytes: 524288
maxBatchEvents: 500
javascript:
enabled: true
rules:
# High confidence names from the June Miasma waves.
# These complement the built-in known-malicious rule.
- id: packages.miasma.vapi-wave
surface: javascriptPackages
triggers: [added, modified, present]
match: fieldContainsAny
field: name
values:
- "@vapi-ai/server-sdk"
- "ai-sdk-ollama"
severity: suspicious
title: Miasma June 2026 package
rationale: Name matches a package published during the Miasma binding.gyp / Phantom Gyp wave.
- id: packages.miasma.autotel-spread
surface: javascriptPackages
triggers: [added, modified, present]
match: fieldContainsAny
field: name
values:
- "autotel"
- "awaitly"
- "eslint-plugin-executable-stories"
- "node-env-resolver"
severity: suspicious
title: Miasma spread family package
rationale: Name matches a maintainer family hit by the self-propagating Miasma worm in early June 2026.
# General signal for packages that declare install-time execution.
# The first Miasma wave used preinstall. This will also fire for legitimate native and build tooling.
# Use it for review queues rather than immediate panic.
- id: packages.javascript.has-install-script
surface: javascriptPackages
triggers: [added, modified, present]
match: flagEquals
flag: hasInstallScript
expected: true
severity: notable
title: JavaScript package with install hook
rationale: Package metadata shows an install, postinstall, or prepare script. Check the resolved version and the lockfile diff.
# Example of a very narrow name rule for one high-risk package during an active window.
- id: packages.miasma.redhat-specific
surface: javascriptPackages
triggers: [added, modified, present]
match: fieldContainsAny
field: name
values: ["@redhat-cloud-services/frontend-components"]
severity: critical
title: Red Hat cloud services package from Miasma campaign
rationale: Specific package and namespace tied to the June 1 Red Hat GitHub account compromise.How to deploy
- Add or merge the rules into your org bundle YAML.
- Increment the generation field (date string works fine).
- Publish through Fleet. The bundle is validated before any Mac receives it.
- Enrolled agents pull the new JSON version on their next check-in.
- On a test Mac you can also drop the rules into ~/.config/koban/koban.yaml, restart the agent, and force a rescan or make a change.
The agent evaluates present rules against the current inventory on the next poll or snapshot, so you do not have to wait for new installs to see existing bad packages.
Tuning to keep noise down
- Start with the name-based rules at suspicious or critical. They are narrow.
- Put the hasInstallScript rule at notable and route it to a review queue rather than a high-urgency Slack channel.
- If you have a lot of native or build tooling that legitimately uses install scripts, you can scope the hasInstallScript rule to specific projects by using local config paths or simply accept the review volume.
- Combine with the untrusted-registry rule that is already in defaults if you want provenance signals alongside name IOCs.
What Koban will not catch by itself
Rules are intentionally limited to the closed vocabulary of fields and flags the collector provides. We do not have arbitrary file scanning or content regex in the rule engine. For the binding.gyp technique the name match was the reliable path because the packages avoided the hasInstallScript declaration.
Deeper file-level signals (presence of binding.gyp inside a resolved package directory, hash of the index.js payload, anomalous version jumps) would require agent enhancements. Those are on the roadmap for future surfaces. In the meantime the lockfile diff gives you the "this bad thing resolved here" event on the actual developer machine, which is often the earliest and most actionable signal.
Quick verification
After you publish:
- On an enrolled Mac, check the menubar agent shows it is using the latest generation.
- In Fleet, look at a device's current inventory or recent findings for one of the test names or a package with hasInstallScript.
- Trigger a manual change (edit a lockfile to include a test bad name, or install a package that has a script) and confirm the finding appears with the title and severity you expect.
If you want the full current defaults for reference, the backend serves them and the agent repo ships a koban.default.yaml that teams often start from before customizing.
The pattern that keeps working
Every wave in this family has taught the same lesson. Registry takedowns and provenance checks are necessary. They are not sufficient once a trusted publish path or a new execution hook appears. Continuous, local, diff-based visibility on the machines where developers actually run npm install turns the next incident from a surprise into a routine finding that you can act on in minutes instead of days.
We will keep the default lists fresh as new indicators come out. Layer the extra rules above when you have an active window or a particular family you want to watch more closely.
Questions or want a reviewed bundle for your org? Reach out. We are happy to look at what you have and suggest the smallest set that gives you the coverage you need without drowning the team in findings.
Further reading
- Unit 42: The npm Threat Landscape: Attack Surface and Mitigations (Updated June 2)
- StepSecurity: Miasma npm Supply Chain Attack: Self-Spreading Worm via Phantom Gyp
- StepSecurity: Multiple redhat-cloud-services npm Packages compromised
- Snyk: Node-gyp Supply Chain Compromise
- Red Hat: RHSB-2026-006 Supply chain compromise of @redhat-cloud-services npm packages