Skip to content

PrivateName leak in decorators #24

@rbuckton

Description

@rbuckton

The current semantics for the PrivateName Object can result in decorators that leak shared custom PrivateName values, breaking "hard privacy". For example:

// a.js
const sharedPrivateName = new PrivateName("shared");
export function dec(desc) {
  desc.elements.push({ 
    kind: "field", 
    placement: "own", 
    key: sharedPrivateName 
  });
  desc.elements.push({
    kind: "method",
    key: "setPrivateData",
    placement: "prototype",
    descriptor: {
      value: function(data) {
        sharedPrivateName.set(this, data);
      }
    }
  });
  return desc;
}

// b.js
import { dec } from "./a.js";

// apply the decorator, adds `sharedPrivateName` to `C`
@dec
class C {
}

// leak the private name
const desc = { kind: "class", elements: [] };
dec(desc);
const leakedPrivateName = desc.elements[0].key;

// demonstrate leak
const obj = new C();
obj.setPrivateData("private data"); // method attached by decorator

const privateData = leakedPrivateName.get(obj);
privateData; // "private data";

One possible approach to avoid this is to break PrivateName into two parts: a property "mutator" and an opaque "key". For example:

interface PrivateName {
  key;
  get(obj);
  set(obj, value);
}

Then the decorator above could be rewritten:

// a.js
const sharedPrivateName = new PrivateName("shared");
export function dec(desc) {
  desc.elements.push({ 
    kind: "field", 
    placement: "own", 
    key: sharedPrivateName.key // only send opaque key
  });
  desc.elements.push({
    kind: "method",
    key: "setPrivateData",
    placement: "prototype",
    descriptor: {
      value: function(data) {
        sharedPrivateName.set(this, data); // uses mutator
      }
    }
  });
  return desc;
}

While this still results in a key that could be added to any object, it prevents an untrusted third party from reading or writing to that state.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions