Skip to content

[Bug]: A member named __proto__ is silently dropped from a decorator's object value #11743

Description

The compiler marshals an object value before it passes that value to a
decorator, and it adds each member by assignment. An assignment to __proto__
sets the prototype instead of adding a member, so that member never reaches the
decorator and no diagnostic reports it.

The reproduction emits this, with no warning and no error:

x-c-control: # the control, an ordinary member name
  normal:
    polluted: true
  ok: 1
x-b-string: # `__proto__` holding a string
  ok: 1
x-a-object: # `__proto__` holding an object
  ok: 1

The control keeps both members. Only the two cases that name a member
__proto__ lose one, so the name decides it — not the nesting, and not the
emitter.

The document cannot show the second effect. When the lost member holds an
object or an array, that value becomes the prototype of the marshalled object.
Measured in a decorator of my own, and still present in 1.15.0:

own=["ok"]  prototypeOwnNames=["polluted"]  value.polluted === true

polluted is never a member of that object. It is a name the .tsp author
chose, so a decorator that reads any name it did not declare can read the
author's data.

Every decorator taking an object value is affected.

The assignment is objectValueToJs, still the same on main
today:

function objectValueToJs(type: ObjectValue): Record<string, unknown> {
  const result: Record<string, unknown> = {};
  for (const [key, value] of type.properties) {
    result[key] = marshalTypeForJs(value.value, undefined);
  }
  return result;
}

The other direction in the same file already handles the name.
unmarshalJsToValue collects members into a Map, which
takes __proto__ as an ordinary key. So the two directions disagree about it.

Suggested fix: build the object with Object.create(null), or add each
member with Object.defineProperty.

I keep the case as a failing test
here,
so it turns green if this changes.

Playground Link

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcompiler:coreIssues for @typespec/compiler

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions