Follow-up named by Zaldaryon while approving #376, deliberately left out of that PR so the beta.8 promotion could move.
assertWindowOpenHandlerDenies in tests/security-boundaries.test.ts accepts return { action: "deny", ...override }. At runtime a spread written after action overrides it, so a handler of that shape can grant while the contract stays green. It is not a shape that appears by accident the way a conditional return does, which is why it did not hold up the merge, but it is a one-line hole in a test whose whole purpose is to have no holes.
Fix: in the object-literal check, reject any SpreadAssignment that appears after the action property (a spread before it is harmless, action wins). Add the rejected fixture { ...override, action: "deny" } accepted and { action: "deny", ...override } rejected, and a mutant on the real handler that appends a spread. Same file, same style as the #376 helpers.
Follow-up named by Zaldaryon while approving #376, deliberately left out of that PR so the beta.8 promotion could move.
assertWindowOpenHandlerDeniesintests/security-boundaries.test.tsacceptsreturn { action: "deny", ...override }. At runtime a spread written afteractionoverrides it, so a handler of that shape can grant while the contract stays green. It is not a shape that appears by accident the way a conditional return does, which is why it did not hold up the merge, but it is a one-line hole in a test whose whole purpose is to have no holes.Fix: in the object-literal check, reject any
SpreadAssignmentthat appears after theactionproperty (a spread before it is harmless,actionwins). Add the rejected fixture{ ...override, action: "deny" }accepted and{ action: "deny", ...override }rejected, and a mutant on the real handler that appends a spread. Same file, same style as the #376 helpers.