[BUG] Agent installed an end-of-life dependency with 37 published CVEs after a failed registry lookup, with no vulnerability check

Status Open
Reported on v2.1.270
Maintainer reply None cached
Activity 1 comment · opened Oct 13, 2026

Summary

The core problem is that an agent installed software with known, published vulnerabilities, and nothing checked.

On 14 September 2026 a Claude Code session installed Electron 36.9.5. The version was out of support, and Electron's advisory feed lists 37 vulnerabilities against it, 11 rated High by GitHub. Electron had published all 37 before the install, the newest 16 days earlier. GitHub's advisory database, which npm audit and similar tools read, held 32 of them by then; the other 5 were added on 29 September. So a database lookup at install time would have returned 32 of the 37, and Electron's own advisory page would have returned all of them.

The version came from the agent's memory. On 13 September it wrote "electron": "^36.0.0" into a new package.json because the sandbox could not reach the npm registry. The creating commit says the versions were "written from memory with no registry access" and are "a proposal, not a verified resolution." Electron 36 had been out of support since October 2025, and Electron 44 had been stable since 25 August 2026.

This is not a report of exploitation. It is a report of an unsafe fallback and a missing vulnerability check.

The highest-severity advisories

These are the 11 rated High by GitHub, highest first. A CVSS score reflects what an attacker gains if the conditions are met, and the last column gives the conditions, taken from each advisory. Four of the eleven, marked ‡, are among the 5 added to GitHub's database on 29 September; Electron had published them on 29 August.

| CVE | GitHub CVSS | What it allows | Affects an app only if |
|---|---|---|---|
| CVE-2026-102676 ‡ | 8.3 | A <webview> can turn on Node.js in its Web Workers even when the embedding app disabled it, so guest content gets more privilege than the app allowed. | It enables <webview> with an unsandboxed embedder |
| CVE-2026-102674 ‡ | 8.2 | Windows opened from a sandboxed top-level document skip its sandbox, so untrusted content can open a window with the app's full origin. | It renders untrusted content in a sandboxed top-level document that allows popups |
| CVE-2026-102673 ‡ | 8.2 | Popups from a sandboxed iframe skip its sandbox and get the embedding app's cookies, storage and same-origin scripting. | It embeds untrusted content in iframes sandboxed with allow-scripts allow-popups |
| CVE-2026-34774 | 8.1 | Use-after-free in an offscreen child window's paint callback, which may crash the app or corrupt memory. | It uses offscreen rendering and permits child windows |
| CVE-2026-34769 | 7.7 | An undocumented commandLineSwitches setting appends arbitrary switches to the renderer command line, which can disable sandboxing or web security. | It builds webPreferences from untrusted input |
| CVE-2026-34771 | 7.5 | Use-after-free in fullscreen, pointer-lock and keyboard-lock permission callbacks, which may crash the app or corrupt memory. | It registers an asynchronous permission request handler |
| CVE-2026-70601 | 7.5 | Context isolation bypass: untrusted web content reaches the preload world and every capability it has, and Node.js access if the renderer is unsandboxed. | It exposes Promise-returning functions through contextBridge in windows that load untrusted content |
| CVE-2026-70604 | 7.4 | A custom scheme registered without corsEnabled lets a remote page read its responses cross-origin. | It serves sensitive data from such a scheme and loads untrusted content |
| CVE-2026-102675 ‡ | 7.4 | The same cross-origin read through file and HTTP protocol handlers; it completes the fix for CVE-2026-70604. | It registers such a scheme and loads untrusted content |
| CVE-2026-70608 | 7.2 | A sandboxed iframe without allow-popups can still open a window with no user interaction. | It embeds untrusted content in sandboxed iframes and relies on allow-popups being absent |
| CVE-2026-34770 | 7.0 | Use-after-free in powerMonitor after garbage collection, triggered by a session change or shutdown; not directly renderer-controllable. | It uses powerMonitor events |

Why these rate High: the sandbox and isolation flaws carry a changed scope in the CVSS vector, meaning content that should stay confined can reach resources beyond its boundary, and most carry a high confidentiality impact because the escaped content gets the app's origin or capabilities. CVE-2026-70608 is the exception, with low confidentiality and integrity impact, and it rates 7.2 because it is reachable over the network with low complexity, and needs no privileges or user interaction. The memory-corruption flaws rate High on the worst case, full loss of confidentiality, integrity and availability, tempered by high attack complexity. The 36 line receives no patches. Fixes for the seven April and July High advisories land between 38.8.6 and 39.8.10, and the four September ones are fixed in 41.10.4 to 41.10.6.

None of the 37 is in CISA's known-exploited list, and the highest EPSS score is 0.56%. One scanner scored CVE-2026-34775 at 9.8, while GitHub rates it Medium at 6.8, because it only affects apps that enable nodeIntegrationInWorker. This app does not, and I use GitHub's scores throughout. I read the app's source against the conditions above, and it meets few of them, with medium confidence because that reading came from a text search.

Observed

  • 13 Sep: the agent created the manifest with the remembered range and said so in the commit message.
  • 14 Sep: a session ran npm install, which resolved the range to 36.9.5.
  • 14 Sep: a ticket comment confirmed that all five dependency ranges resolved to installed versions. It did not check support status or advisories, and I found no recorded check of either. The broader ticket is now closed.
  • 14 Sep: a later commit recorded the lockfile. npm's install-scripts gate had been skipping Electron's postinstall step, and the commit message says npm install-scripts approve electron fixed it. That command records a version-pinned allowScripts entry.
  • 12 Oct: an unrelated dependency scan found the pin, 29 calendar days after the manifest was created.

Expected behavior

An agent should not silently promote an unverified dependency selection into an install or an install-script approval. If verification is unavailable, it should say so and obtain an explicit user decision before proceeding. Sketching an offline manifest is fine, provided unverified entries are clearly marked and not treated as approved. Once verification is possible, the agent should check support status and published advisories before installing.

Environment

  • Claude Code 2.1.270, from the project's session metadata on 13 to 15 September. The scan ran on 2.1.296.
  • Models named in the commit trailers: Claude Opus 5 and Claude Sonnet 5. The sessions' metadata also shows Claude Fable 5.
  • Permission mode: most recorded entries for the project's sessions are auto, with a few acceptEdits and one plan. I could not tie the install-script approval to a single session or tell whether the auto-mode classifier evaluated that command.
  • npm 11 (the commit message names npm 11's install-scripts gate). The Node version at the time is unknown. This machine now runs Node 26.11.1 and macOS 27.0.1.

Impact and limits

The app is local, offline and sandboxed, and most of the High-rated advisories need APIs it does not call. I have no evidence of exploitation. The wider risk is that an unverified or vulnerable choice flows straight into an install workflow that runs package code.

This is one machine and one historical incident. A clean-session reproduction is pending. Because the sessions ran in auto mode, I am describing the install-script approval as a trust decision made during the task, not as a bypass.

Related

#39421 proposed a pre-install supply-chain hook. Stale automation closed it in April without a recorded maintainer resolution of the proposal, and it is now locked. This issue is about the earlier step, what gets written into a manifest when lookup fails, and about install-script approval as a decision for a human.

Evidence

File 08 holds redacted commit messages and diffs, the lockfile entry, ticket comments, session metadata and a table of all 37 advisories with publication dates and two score sources. It contains the primary excerpts and the supporting table, and it does not include every raw scanner record.

What the documentation says

I read the documentation index and five pages: security, security guidance, Claude Security, permission modes and the hooks guide. I did not read the whole site or the Help Center.

  • The permission-modes page lists "installing dependencies declared in your lock files or manifests" as allowed by default in auto mode. A manifest the agent wrote itself, from memory, is therefore installed without any review of its versions.
  • The security-guidance and Claude Security plugins are optional installs. They review the source code Claude writes, and the security-guidance page assigns "supply-chain checks" to "your existing static analysis and dependency scanners" in CI, as something the plugin "does not attempt."
  • The security page says "You're responsible for reviewing proposed code and commands for safety before approval," and in auto mode a classifier reviews in the user's place.
  • The hooks guide says a PreToolUse deny blocks the tool in every permission mode. That is the mechanism I would use.
  • I found no page that documents a dependency, advisory or end-of-life check in the CLI, or that recommends a hook for vetting installs.

A mitigation I am adding, and why it should not be mine to add

I am writing a PreToolUse hook that denies package installs, and edits to dependency manifests and lockfiles, until the hook has run a live advisory and end-of-life lookup for each package version itself and written the result to a hash-frozen record that I commit. The hook does the lookup, not the agent, because a record the agent fills in could be filled in falsely. Matching install commands in Bash text is best effort, which the hooks guide says about its own filters, so the hook also watches manifest and lockfile edits.

That protects my projects only. Every Claude Code user who installs packages in auto mode has the same exposure, and each would have to find the hooks mechanism and build this. Users should not have to. The check belongs in the CLI as a built-in hook, and the safe behavior should not depend on the model remembering to do it.

Requested change

The outcome I am asking for is that a package version with a known Critical or High advisory, or a runtime past its end of life, is not installed unless the user makes an explicit decision. I do not think the model can be what guarantees this, because its knowledge ends at training and advisories keep arriving. For Electron 36.9.5, 5 of the 37 were published within the 28 days before the install. So the ask is a built-in check, with three parts:

  1. A default hook in the CLI that denies and fails closed. It runs a live advisory and end-of-life lookup before an install, and whenever an agent creates or edits a dependency declaration in a manifest or lockfile. That covers newly added packages, changed version ranges and changes to install-script approval policy.
  2. Auto mode's default allowance for installing declared dependencies should not apply to versions that fail that check.
  3. A clear way for the user to override, with the finding shown. A hard block on every finding would stall most installs, since a full scan of my machine returned over a thousand, so the default should block at Critical and High severity or an unsupported runtime, with every override recorded.

Base-prompt guidance that a failed lookup is reported and stops the task would still help, because it reduces how often the check fires. It should not be the control. Project instruction files are loaded every session but are context, not enforcement, so they cannot carry this either.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗