Repository navigation
[APPS-2947] Fix: fail the build on connection IDs read from objects that calls or aliases write to - #619
Draft
tyffical wants to merge 1 commit into
Conversation
This was referenced Oct 23, 2026
…s or aliases
The connection ID extractor doesn't read a member of an object that a
known mutator writes to: Object.assign, defineProperty, defineProperties
and setPrototypeOf, and Reflect.set (its receiver, when one is given),
defineProperty, deleteProperty and setPrototypeOf, including one named
through a const-aliased computed key such as `Object[method]`. The same
holds when the object is written through an alias, such as `const gh =
CONN.gh`, `gh = CONN.gh`, `const gh = CONN?.gh || {}`, `cur ||= {}`, a
member of a rest copy (including one destructured from it), or a
`const`, `let` or destructured alias its module exports by name. Such a
connectionId takes the unsupported-connectionId path instead of
reporting a stale ID. Action options with a toJSON or then key, or a
__proto__ key other than a shorthand or null one, are rejected, since
each can change the connectionId the call sends. Each extraction walks
the module graph for writes at most once.
An object that is only read, logged, copied (including a write to a rest
copy's own properties) or used to call a method still resolves, as does
one whose alias is rebound without being written, or one passed to a
local function that shadows Object or Reflect. Writes through `this` in
the object's own methods, through a function, container or awaited value
the object is passed to, through a default or namespace import of it, or
through an aliased call to a mutator are not tracked; the server's
allowlist still refuses the resulting connection ID.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tyffical
force-pushed
the
tiffany.trinh/apps-2947-connection-ids-fail-closed
branch
from
October 23, 2026 15:59
e64de90 to
23be3cd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
connectionIdvalues, and they feed the server's allowlist and the Live Preview consent prompt.connectionIdis read from, the static read reports the object's original value, not the one the call sends.connectionIderror instead of reporting a stale ID.Architecture
flowchart LR S["connectionId: CONN.github"] --> R["Resolve CONN.github statically"] W["Writes to CONN or an alias of it: Object.assign, Reflect.set, gh = CONN.github; gh.x = ..."] --> M["Mutated-object set, one walk per extraction"] M --> R R -- "CONN not written" --> OK["Report the literal"] R -- "CONN written" --> ERR["Fail the build: mutated object binding"] O["Options key toJSON, then or __proto__"] --> ERR2["Fail the build: could hide a connectionId"]++/--,deleteorfor…in/for…ofwrite to the object; this adds mutator calls, including a template-literal or const-aliased method key, and the aliases a write can go through, across module exports.Changes
7 changes across module-graph.ts, static-string-resolution.ts, extract-connection-ids-from-module-graph.ts, ambient-global-access.ts, type-guards.ts and tests
packages/plugins/apps/src/backend/ast-parsing/module-graph.tspackages/plugins/apps/src/backend/ast-parsing/static-string-resolution.tsconnectionIdpackages/plugins/apps/src/backend/ast-parsing/extract-connection-ids-from-module-graph.tstoJSONorthenkey, or a__proto__key that isn't shorthand ornullpackages/plugins/apps/src/backend/ast-parsing/extract-connection-ids-from-module-graph.tsstaticMemberNameso mutator calls resolve method keys the way the ambient-global checks dopackages/plugins/apps/src/backend/ast-parsing/ambient-global-access.tsisNodeOfTypefor nodes typed against eslint-scope's copy ofestreepackages/plugins/apps/src/backend/ast-parsing/type-guards.tspackages/plugins/apps/src/backend/ast-parsing/extract-connection-ids-from-module-graph.test.tsQA Instructions
qa-fc-build.cjsbelow runs a realvite buildof a one-function app per case, on this branch and on its parent.Production-build harness source and captured output
qa-fc-build.cjsandnode_modules/@datadog/action-catalog, copied frompackages/tests/src/_jest/fixtures/apps_backend_project/node_modules; the script writesapp/anddist/beside them.Blast Radius
vite build, so a build that passes today can now fail.connectionIdis read from an object that a mutator call or alias writes to, or the call's options carry atoJSON,thenor non-inert__proto__key.connectionIdbuilds.Out of Scope / Follow-ups
9 items deferred
Reflect.set's receiver, a non-constmethod key (Object[m]), or(Object?.assign)(…)(0, Object.assign)(…),.call/.apply,const { assign } = Object,globalThis.Object.*,__defineGetter__delete CONN?.x, an object-spread copy,Object.prototype,this, a function, return, container or awaited value, a default or namespace import, or a block-scoped exportedvarevalwrites, e.g.eval("CONN.gh.id = 'x'"), which the extractor can't see, so it reports the stale IDlet cur = CONN.gh; cur = {...}; cur.x = 1never writesCONN, but the build failsmodule-graph.tsgetMutatedCallTargetsandmatchBulkCopyCallmatchObject/Reflectstatic calls separately${isMember}:${isPastImport}string and re-resolves an import's definition per writemodule-graph.tsuses, and cache definitions per importmodule-graph.tsexportongetModuleExportNameDocumentation
🤖 Generated with Claude Code