Skip to main content
← Back to list
01Issue
BugIn ProgressSwamp CLIPublic
Assigneesstack72

Relationships

#2264 DeferredExpression durable bindings lose a binding named __proto__ (flaky property test)

Opened by skunk-ape · 9/17/2026

Problem

src/domain/expressions/deferred_expression_property_test.ts — "DeferredExpression: durable bindings round-trip and references keep scope identity" — fails intermittently. It is a fast-check property test, so it only fails when the generator happens to produce the offending case; most runs are green, which is why it reads as flaky rather than broken.

The case is a binding whose key is __proto__. The round-trip drops it:

Property failed after 92 tests
seed: 8136611
path: 91-0-0-0-0-0-0-0-0-1-0   (dashes substituted for colons; the real
                                separator is a colon, restore before use)
endOnFailure: true

Counterexample: [<uuid>, <uuid>, { ["__proto__"]: {} }]
Shrunk 10 time(s)

AssertionError: Values are not equal.
    [Diff] Actual / Expected
    {
      bindings: {
+       inputs: {
+         ['__proto__']: {},
+       },
-       inputs: {},
      },
      expression: "${{ env.HOME + inputs.suffix }}",
      id: <uuid>,
    }

Expected has the __proto__ key; actual has an empty object. Deno also prints the related hint elsewhere in the same run: "This program assigned to Object.prototype.proto, which Deno disables by default."

(The seed path above is written with dashes because the issue redactor reads a colon-separated digit run as an IP address and rewrites it.)

Cause

Almost certainly the standard __proto__ pitfall: assigning obj[key] = value with key === "__proto__" on a plain object mutates the prototype instead of creating an own property, so the binding vanishes from the reconstructed scope. Anywhere bindings are rebuilt key-by-key into an object literal is a candidate.

Suggested fix

Build binding maps with a null-prototype object (Object.create(null)) or a Map, or assign through Object.defineProperty, so a key named __proto__ is stored as data. Then pin the counterexample as an explicit non-property test case so it cannot regress silently.

Worth noting this is a prototype-pollution-shaped surface as well as a data-loss one, though the observable impact here is a lost binding rather than a reachable exploit.

Repro

deno test --allow-all src/domain/expressions/deferred_expression_property_test.ts

Re-run until it trips, or pin the seed and path above into the fc.assert options to hit it deterministically.

Context

Hit during pre-PR verification for #2254, where it failed one verify-build run and passed two others at the same commit. Unrelated to that change (which touches logging sinks, CLI argv parsing and the invite-link renderer). The nearest recent work in this area is 388ccad0, "fix(expressions): persist authored provenance with evaluated caches (swamp-club#2172)".

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 3 MOREVERIFICATION_STARTED

In Progress

9/18/2026, 8:23:47 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack729/18/2026, 7:57:49 PM

Sign in to post a ripple.