fix: le descripteur d-un membre repond ce que le lire repond
Trouve en rejouant l-adversaire sur le correctif precedent, et mesure : `Object.getOwnPropertyDescriptor(resultat, "data")` rendait la collection BRUTE, et `(..., "subscription")` le handle que `get` refuse. Cet appel ne passe pas par le piege `get` - ni l-etalement, ni `Object.entries`, ni `JSON.stringify`, ni une inspection console. Une deuxieme porte sur la meme piece, ouverte par un piege que personne n-avait ecrit. Le meme trou existait sur la vue de collection pour la trappe dunder. La forme ordonnee dit combien d-objets elle porte a QUATRE endroits : `length`, `Object.keys`, `in`, et le descripteur de chaque index. Un seul etait filtre, donc le compte de ce que le porteur ne peut pas lire ressortait par les trois autres, et le descripteur rendait les objets eux-memes. Les quatre pieges appellent maintenant une seule fonction `read`, pas trois copies de la regle : une frontiere faite de `switch` paralleles est une frontiere qui derive. Et l-index nomme une position dans ce qui est LISIBLE, donc la forme ordonnee sert son porteur au lieu de refuser.
This commit is contained in:
@@ -262,7 +262,36 @@ test("useShape: an ordered result does not publish the unfiltered count", async
|
||||
expect((hook.raw() as BaseType[]).length).toBe(3);
|
||||
expect((data as { length: number }).length).toBe(1);
|
||||
expect([...(data as Iterable<BaseType>)].map((o) => o["@graph"])).toEqual([hers]);
|
||||
// An index hands back a raw item and there is no projection of it this file would not
|
||||
// have to invent, so it is refused rather than guessed at.
|
||||
expect(() => (data as Record<number, unknown>)[0]).toThrow(/not filtered/i);
|
||||
// An index names a position in the READABLE items, so the holder reads their own by
|
||||
// index and the positions past them do not exist at all. `Object.keys` and `in` have to
|
||||
// agree with `length`, or the count comes back out through the other three.
|
||||
expect((data as Record<number, BaseType>)[0]!["@graph"]).toBe(hers);
|
||||
expect((data as Record<number, BaseType>)[1]).toBeUndefined();
|
||||
expect(Object.keys(data as object)).toEqual(["0"]);
|
||||
expect(1 in (data as object)).toBe(false);
|
||||
expect(JSON.parse(JSON.stringify(data)) as BaseType[]).toHaveLength(1);
|
||||
});
|
||||
|
||||
// REGRESSION, found by re-running the adversary on the first fix (2026-09-24).
|
||||
// `Object.getOwnPropertyDescriptor` reads a property WITHOUT the `get` trap, so a view
|
||||
// that filtered `get` alone handed the raw value straight back to whoever asked for the
|
||||
// descriptor - including `subscription`, the one member `get` refuses outright. Everything
|
||||
// built on it goes the same way: a spread, `Object.entries`, a console inspection.
|
||||
test("useShape: the descriptor of a member answers exactly what reading it answers", async () => {
|
||||
const hook = makeUseShapeHook();
|
||||
inject(hook);
|
||||
const hers = await documentOf("alice");
|
||||
const his = await documentOf("bob");
|
||||
hook.items.push(noteIn(hers, "a1"), noteIn(his, "b1"));
|
||||
|
||||
setCurrentUser("bob");
|
||||
const result: ShapeResult = useShape(NoteShape, EVERYTHING);
|
||||
|
||||
const described = Object.getOwnPropertyDescriptor(result, "data");
|
||||
expect(described?.value).toBe(result.data);
|
||||
expect(described?.value).not.toBe(hook.raw());
|
||||
expect([...(described?.value as Iterable<BaseType>)].map((o) => o["@graph"])).toEqual([his]);
|
||||
expect(() => Object.getOwnPropertyDescriptor(result, "subscription")).toThrow(
|
||||
/RdfOrmSubscription/,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user