{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "keep-as-periodic-hunt",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "ChainDrop is a high-velocity supply chain attack targeting the root of developer trust; identifying its presence on workstations and runners is essential to prevent large-scale credential theft."
      },
      "name": "ChainDrop: NPM Worm Endpoint and CI Runner Activity",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1195.002",
        "attack.t1059.003",
        "attack.t1105",
        "attack.t1552.001",
        "attack.t1555",
        "attack.t1003.001"
      ],
      "series": {
        "slug": "chaindrop-inside-a-self-propagating-npm-worm",
        "index": 1,
        "title": "ChainDrop: Inside a Self-Propagating npm Worm",
        "total": 2
      },
      "related": [
        {
          "hunt": "chaindrop-persistence-and-propagation",
          "reason": "This hunt identifies the initial execution and harvest; persistence in VS Code and Claude Code, and automated propagation, are handled in the follow-on hunt.",
          "relation": "out-of-scope-alternative"
        }
      ],
      "targets": {
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "analysis": "A simple detection rule on the npm package version is easily bypassed by the worm's propagation. This hunt pivots between software inventory, process runtime prevalence (Bun), and subsequent behavioral indicators like file harvest and proc-fs scraping to find the worm even after packages rotate.",
      "coverage": [
        {
          "stage": "npm-supply-chain-compromise",
          "steps": [
            "affected-npm-packages"
          ],
          "status": "covered"
        },
        {
          "stage": "npm-lifecycle-hook-execution",
          "steps": [
            "dropper-execution"
          ],
          "status": "covered"
        },
        {
          "stage": "portable-runtime-dropper",
          "steps": [
            "bun-prevalence"
          ],
          "status": "covered"
        },
        {
          "stage": "credential-and-environment-sweep",
          "steps": [
            "credential-sweep"
          ],
          "status": "covered"
        },
        {
          "stage": "ci-runner-memory-access",
          "steps": [
            "memory-scraping"
          ],
          "status": "covered"
        },
        {
          "stage": "developer-tooling-persistence",
          "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "blockchain-c2-routing",
          "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "automated-package-propagation",
          "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
          "status": "out_of_scope"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "Trojanized npm Package Installation",
            "slug": "npm-supply-chain-compromise",
            "tactic": "initial-access",
            "techniques": [
              "T1195.002"
            ],
            "observables": [
              "registry.npmjs.org",
              "keyv",
              "cacheable-request"
            ]
          },
          {
            "name": "NPM Preinstall Hook Execution",
            "slug": "npm-lifecycle-hook-execution",
            "tactic": "execution",
            "techniques": [
              "T1059.003"
            ],
            "observables": [
              "preinstall",
              "node setup.mjs"
            ]
          },
          {
            "name": "Bun Runtime Dropper and Payload Execution",
            "slug": "portable-runtime-dropper",
            "tactic": "execution",
            "techniques": [
              "T1105"
            ],
            "observables": [
              "setup.mjs",
              "math_init.js",
              "Bun 1.3.13",
              "Oven GitHub repository",
              "_NODE_RUNTIME_INIT=1"
            ]
          },
          {
            "name": "Filesystem and Environment Credential Harvest",
            "slug": "credential-and-environment-sweep",
            "tactic": "credential-access",
            "techniques": [
              "T1552.001",
              "T1555"
            ],
            "observables": [
              ".env",
              ".git-credentials",
              ".netrc",
              "SSH keys",
              "npm tokens",
              "GitHub tokens",
              "Kubernetes service-account tokens"
            ]
          },
          {
            "name": "CI Runner Process Memory Scraping",
            "slug": "ci-runner-memory-access",
            "tactic": "credential-access",
            "techniques": [
              "T1003.001"
            ],
            "observables": [
              "Runner.Worker",
              "/proc/pid/maps",
              "/proc/pid/mem",
              "OIDC tokens"
            ]
          },
          {
            "name": "Persistence via VS Code and Claude Code Hooks",
            "slug": "developer-tooling-persistence",
            "tactic": "persistence",
            "techniques": [
              "T1546"
            ],
            "observables": [
              ".vscode/tasks.json",
              ".claude/settings.json",
              "Environment Setup",
              "SessionStart",
              ".claude/math_init.js",
              "com.user.gh-token-monitor",
              "gh-token-monitor.service"
            ]
          },
          {
            "name": "Ethereum Smart Contract C2 Resolution",
            "slug": "blockchain-c2-routing",
            "tactic": "command-and-control",
            "techniques": [
              "T1102"
            ],
            "observables": [
              "Ethereum transaction",
              "blockchain-based C2 resolution"
            ]
          },
          {
            "name": "Worm Propagation via Package Republishing",
            "slug": "automated-package-propagation",
            "tactic": "lateral-movement",
            "techniques": [
              "T1534",
              "T1574.006"
            ],
            "observables": [
              "Shai-Hulud: Here We Go Again",
              ".github/workflows/codeql_analysis.yml",
              "npm:registry.npmjs.org",
              "release-drafter.yml",
              "/opensearch-js"
            ]
          }
        ],
        "summary": "ChainDrop is a self-propagating npm worm that infects developer environments via trojanized package lifecycle hooks to harvest cloud credentials, SSH keys, and CI runner secrets. It persists through cross-linked VS Code and AI tool configurations and uses Ethereum smart contracts for C2 routing to spread by republishing infected versions of legitimate packages."
      },
      "severity": "high",
      "rationale": "Prioritize development workstations and CI/CD runner environments where npm packages are frequently installed and updated.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An intruder has infected an npm package and triggered a preinstall hook that uses the Bun runtime to harvest credentials from the filesystem and CI runner process memory.",
      "parameters": {
        "scope_hosts": {
          "type": "list[host]",
          "default": [],
          "description": "Optional list of hostnames to focus the hunt on; leave empty for fleet-wide."
        },
        "lookback_days": {
          "type": "number",
          "default": "14",
          "description": "Days of history to examine."
        },
        "infected_packages": {
          "from": {
            "ref": "unit42-chaindrop",
            "kind": "article",
            "observed": "2026-08-06"
          },
          "type": "list[string]",
          "default": [
            "keyv",
            "cacheable-request"
          ],
          "description": "NPM packages known to have been trojanized by ChainDrop."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://unit42.paloaltonetworks.com/chaindrop-npm-worm-analysis/",
          "gates": [
            "dry-run",
            "lint",
            "critic"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://unit42.paloaltonetworks.com/chaindrop-npm-worm-analysis/",
          "name": "ChainDrop: Inside a Self-Propagating npm Worm"
        }
      ],
      "blind_spots": [
        {
          "id": "limited-process-memory-visibility",
          "risk": "Without memory-access logs, we rely on the command-line presence of procfs strings, which the adversary could obfuscate or perform via a binary that does not use a shell helper.",
          "stage": "ci-runner-memory-access",
          "question": "Whether the Runner.Worker memory was successfully read by a non-standard process.",
          "requires": "EDR memory-access telemetry or Sysmon for Linux with Event ID 10"
        },
        {
          "id": "short-lived-runners",
          "risk": "Ephemeral CI runners may not persist logs to a central sink in time for the hunt to see the hook execution if the job finishes quickly.",
          "stage": "npm-lifecycle-hook-execution",
          "question": "If the worm executed on a runner that was destroyed before logs were shipped.",
          "requires": "Real-time process logs from ephemeral containers"
        }
      ]
    },
    "name": "ChainDrop: NPM Worm Endpoint and CI Runner Activity",
    "description": "ChainDrop is a self-propagating worm that spreads by trojanizing npm packages and hijacking the preinstall lifecycle hook. It uses a legitimate portable runtime, Bun, as an execution vehicle to evade standard Node.js instrumentation. Once running, the worm sweeps the filesystem for developer credentials and scrapes GitHub Actions runner memory for OIDC tokens and runner secrets. This hunt focuses on the initial execution of the worm on developer endpoints and CI runners. It identifies affected hosts via software inventory, detects the presence of the Bun-based dropper, and corroborates the activity by looking for unauthorized access to sensitive credential files and Linux process memory associated with CI agents."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "series": {
          "slug": "chaindrop-inside-a-self-propagating-npm-worm",
          "index": 1,
          "title": "ChainDrop: Inside a Self-Propagating npm Worm",
          "total": 2
        },
        "coverage": [
          {
            "stage": "npm-supply-chain-compromise",
            "steps": [
              "affected-npm-packages"
            ],
            "status": "covered"
          },
          {
            "stage": "npm-lifecycle-hook-execution",
            "steps": [
              "dropper-execution"
            ],
            "status": "covered"
          },
          {
            "stage": "portable-runtime-dropper",
            "steps": [
              "bun-prevalence"
            ],
            "status": "covered"
          },
          {
            "stage": "credential-and-environment-sweep",
            "steps": [
              "credential-sweep"
            ],
            "status": "covered"
          },
          {
            "stage": "ci-runner-memory-access",
            "steps": [
              "memory-scraping"
            ],
            "status": "covered"
          },
          {
            "stage": "developer-tooling-persistence",
            "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "blockchain-c2-routing",
            "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "automated-package-propagation",
            "reason": "Belongs to another part of the 'ChainDrop: Inside a Self-Propagating npm Worm' series.",
            "status": "out_of_scope"
          }
        ],
        "rationale": "An intruder has infected an npm package and triggered a preinstall hook that uses the Bun runtime to harvest credentials from the filesystem and CI runner process memory.",
        "blind_spots": [
          {
            "id": "limited-process-memory-visibility",
            "risk": "Without memory-access logs, we rely on the command-line presence of procfs strings, which the adversary could obfuscate or perform via a binary that does not use a shell helper.",
            "stage": "ci-runner-memory-access",
            "question": "Whether the Runner.Worker memory was successfully read by a non-standard process.",
            "requires": "EDR memory-access telemetry or Sysmon for Linux with Event ID 10"
          },
          {
            "id": "short-lived-runners",
            "risk": "Ephemeral CI runners may not persist logs to a central sink in time for the hunt to see the hook execution if the job finishes quickly.",
            "stage": "npm-lifecycle-hook-execution",
            "question": "If the worm executed on a runner that was destroyed before logs were shipped.",
            "requires": "Real-time process logs from ephemeral containers"
          }
        ],
        "scoping_notes": "Prioritize development workstations and CI/CD runner environments where npm packages are frequently installed and updated.",
        "beyond_detection": "A simple detection rule on the npm package version is easily bypassed by the worm's propagation. This hunt pivots between software inventory, process runtime prevalence (Bun), and subsequent behavioral indicators like file harvest and proc-fs scraping to find the worm even after packages rotate."
      }
    },
    {
      "id": "affected-npm-packages",
      "type": "query",
      "label": "Affected npm packages in inventory",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "endpoint",
        "content": "SELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE package_type = 'npm' AND instr(',' || '{{infected_packages}}' || ',', ',' || package_name || ',') > 0",
        "surface": "hb_software_inventory",
        "description": "Identify hosts that have the trojanized npm packages installed in their development environments.",
        "expected_signal": "A list of hostnames and paths where the vulnerable packages are installed. Silence indicates the packages are not present in the reported inventory."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Affected npm packages in inventory",
        "reads": [
          "device_hostname",
          "package_name",
          "package_version",
          "install_path"
        ],
        "source": "hb_software_inventory",
        "target": "endpoint",
        "content": "SELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE package_type = 'npm' AND instr(',' || '{{infected_packages}}' || ',', ',' || package_name || ',') > 0",
        "silence": "not_evidence_of_absence",
        "expected": "A list of hostnames and paths where the vulnerable packages are installed. Silence indicates the packages are not present in the reported inventory.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "dropper-execution",
      "type": "query",
      "label": "Dropper and lifecycle hook execution",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "endpoint",
        "content": "SELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(process_cmd_line) LIKE '%setup.mjs%' OR LOWER(process_cmd_line) LIKE '%math_init.js%' OR LOWER(process_cmd_line) LIKE '%_node_runtime_init%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_process_activity",
        "description": "Detect the execution of setup.mjs or the specific environment variable used by the worm to prevent recursion.",
        "expected_signal": "Process command lines referencing the dropper file or the worm's internal state variable. This confirms the malicious hook fired."
      },
      "parents": [
        {
          "id": "affected-npm-packages"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "Dropper and lifecycle hook execution",
        "reads": [
          "device_hostname",
          "process_cmd_line",
          "user_name",
          "time"
        ],
        "source": "hb_process_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(process_cmd_line) LIKE '%setup.mjs%' OR LOWER(process_cmd_line) LIKE '%math_init.js%' OR LOWER(process_cmd_line) LIKE '%_node_runtime_init%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Process command lines referencing the dropper file or the worm's internal state variable. This confirms the malicious hook fired.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "bun-prevalence",
      "type": "query",
      "label": "Rare Bun runtime execution",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "endpoint",
        "content": "SELECT LOWER(process_name) AS process_name, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE LOWER(process_name) LIKE '%bun%' AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_name HAVING host_count <= 3",
        "surface": "hb_process_activity",
        "description": "Identify hosts running the Bun runtime, which the worm uses as a portable dropper to evade standard Node instrumentation.",
        "expected_signal": "Anomalous execution of the Bun binary. Since it is less common than Node.js in many environments, stack-counting helps find the beachhead."
      },
      "parents": [
        {
          "id": "affected-npm-packages"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Rare Bun runtime execution",
        "reads": [
          "process_name",
          "device_hostname",
          "time"
        ],
        "source": "hb_process_activity",
        "target": "endpoint",
        "content": "SELECT LOWER(process_name) AS process_name, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE LOWER(process_name) LIKE '%bun%' AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_name HAVING host_count <= 3",
        "silence": "not_evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "Anomalous execution of the Bun binary. Since it is less common than Node.js in many environments, stack-counting helps find the beachhead.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "process_name"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "triage-initial",
      "type": "analytic",
      "label": "Evaluate initial execution",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint"
        ],
        "context": [
          "affected-npm-packages",
          "dropper-execution",
          "bun-prevalence"
        ],
        "objective": "Determine if the setup.mjs dropper or Bun runtime was triggered on hosts that also contain the trojanized npm packages.",
        "description": "Assess whether the inventory finds and process patterns together indicate a ChainDrop dropper execution.",
        "max_iterations": 3,
        "expected_signal": "An assessment of which hosts moved from installation to execution.",
        "success_criteria": "A list of hosts with confirmed execution, citing command lines and package inventory."
      },
      "parents": [
        {
          "id": "dropper-execution",
          "kind": "merge"
        },
        {
          "id": "bun-prevalence",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "credential-sweep",
      "type": "query",
      "label": "Credential file harvest",
      "config": {
        "dsl": "sqlite",
        "role": "triage",
        "source": "endpoint",
        "content": "SELECT device_hostname, file_path, process_name, time FROM hb_file_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(file_path) LIKE '%.env%' OR LOWER(file_path) LIKE '%.npmrc%' OR LOWER(file_path) LIKE '%.git-credentials%' OR LOWER(file_path) LIKE '%/.ssh/%') AND (LOWER(process_name) LIKE '%node%' OR LOWER(process_name) LIKE '%bun%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_file_activity",
        "description": "Identify instances where Node or Bun processes access sensitive files like .env, .npmrc, or SSH keys.",
        "expected_signal": "File activity showing the worm's runtime accessing credential stores. Silence suggests no broad sweep occurred or was captured."
      },
      "parents": [
        {
          "id": "triage-initial"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "triage",
        "label": "Credential file harvest",
        "reads": [
          "device_hostname",
          "file_path",
          "process_name",
          "time"
        ],
        "source": "hb_file_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, file_path, process_name, time FROM hb_file_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(file_path) LIKE '%.env%' OR LOWER(file_path) LIKE '%.npmrc%' OR LOWER(file_path) LIKE '%.git-credentials%' OR LOWER(file_path) LIKE '%/.ssh/%') AND (LOWER(process_name) LIKE '%node%' OR LOWER(process_name) LIKE '%bun%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "File activity showing the worm's runtime accessing credential stores. Silence suggests no broad sweep occurred or was captured.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "memory-scraping",
      "type": "query",
      "label": "CI Runner process memory scraping",
      "config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "source": "endpoint",
        "content": "SELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(process_cmd_line) LIKE '%runner.worker%' AND (LOWER(process_cmd_line) LIKE '%/proc/%/maps%' OR LOWER(process_cmd_line) LIKE '%/proc/%/mem%')) AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_process_activity",
        "description": "Detect the worm accessing the memory of the GitHub Actions Runner worker to steal OIDC tokens.",
        "expected_signal": "Command lines showing Python or Shell helpers reading the Linux proc filesystem for the GitHub runner process. This is a high-confidence indicator of ChainDrop impact on CI infrastructure."
      },
      "parents": [
        {
          "id": "triage-initial"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "label": "CI Runner process memory scraping",
        "reads": [
          "device_hostname",
          "process_cmd_line",
          "user_name",
          "time"
        ],
        "source": "hb_process_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, process_cmd_line, user_name, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (LOWER(process_cmd_line) LIKE '%runner.worker%' AND (LOWER(process_cmd_line) LIKE '%/proc/%/maps%' OR LOWER(process_cmd_line) LIKE '%/proc/%/mem%')) AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Command lines showing Python or Shell helpers reading the Linux proc filesystem for the GitHub runner process. This is a high-confidence indicator of ChainDrop impact on CI infrastructure.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "final-assessment",
      "type": "analytic",
      "label": "Confirm credential theft and impact",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint"
        ],
        "context": [
          "triage-initial",
          "credential-sweep",
          "memory-scraping"
        ],
        "objective": "Analyze the file access and memory scraping activity in the context of the initial dropper execution to verify if ChainDrop successfully harvested credentials.",
        "description": "Combine the early execution context with the later harvesting patterns to confirm a full intrusion.",
        "max_iterations": 5,
        "expected_signal": "A final verdict on compromised hosts.",
        "success_criteria": "A per-host verdict of malicious if the chain from infected package to file or memory harvest is complete."
      },
      "parents": [
        {
          "id": "credential-sweep",
          "kind": "merge"
        },
        {
          "id": "memory-scraping",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-verdict",
      "type": "checkpoint",
      "label": "Route on verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the final-assessment verdict is malicious for at least one host",
        "condition": "the final-assessment verdict is malicious for at least one host",
        "blind_spot": "limited-process-memory-visibility",
        "confidence": "high",
        "description": "Determine whether to contain the host based on the severity of the findings.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "final-assessment"
        }
      ]
    },
    {
      "id": "isolate-and-remediate",
      "type": "action",
      "label": "Isolate and remediate host",
      "config": {
        "target": "endpoint",
        "description": "Stop further exfiltration and remove the worm's artifacts.",
        "instructions": "Isolate the endpoint, terminate the Bun and Node processes identified by the agent, and delete the malicious files: .claude/math_init.js, .claude/settings.json, .claude/setup.mjs, .vscode/setup.mjs, and .vscode/tasks.json.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "analyst-review",
      "type": "task",
      "label": "Analyst review",
      "config": {
        "assignee": "analyst",
        "description": "Verify the findings and initiate a full incident response if the worm's activity is confirmed.",
        "instructions": "Review the cited evidence. Confirm if Bun was used to touch .env or SSH files. Inspect CI runner logs for matching setup.mjs activity. Rotate all developer tokens, npm credentials, and SSH keys discovered in the sweep."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "default"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_unavailable"
        },
        {
          "id": "isolate-and-remediate"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Close out",
      "config": {
        "assignee": "analyst",
        "description": "Finalize the hunt when no malicious activity is found.",
        "instructions": "Document the absence of ChainDrop activity in the current window and record any tuning notes for the Bun prevalence query."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_refutes"
        },
        {
          "id": "analyst-review"
        }
      ]
    }
  ]
}