fix(web): let a malformed _meta.ui stop the carrier lookup
uiMeta returned undefined for both "this bag declares no ui" and "its ui
is unusable", so a content item declaring `ui: null` fell through and was
granted whatever a BROADER carrier asked for — wrong precedence, wrong
direction to fail in. It now distinguishes the two: no `ui` key means
keep looking, a declared-but-malformed one decides for "no hints".
Also pin the full three-way precedence with a case carrying all three
carriers at once (no prior case would have caught the first two operands
being swapped), and state why a permission's object value need not be
empty: `{}` is the spec's extension point, so rejecting a populated one
would drop grants from servers written against a newer spec.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
e2a88d49dd
commit
53943c2ef8
2 changed files with 117 additions and 29 deletions
|
|
@ -107,6 +107,15 @@ describe("approveSandboxPermissions", () => {
|
|||
warn.mockRestore();
|
||||
});
|
||||
|
||||
it("accepts a non-empty object, since `{}` is the spec's extension point", () => {
|
||||
// The key's presence is the request; a field the host doesn't know can't
|
||||
// narrow it, and rejecting it would drop grants from servers written
|
||||
// against a newer spec. The value is normalized to `{}` on the way out.
|
||||
expect(approveSandboxPermissions({ camera: { enabled: false } })).toEqual({
|
||||
camera: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("drops `false` without warning, and ignores unknown keys", () => {
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -106,13 +106,21 @@ const PERMISSION_KEYS = exhaustivePermissionKeys([
|
|||
* the `unknown` parameter: this is the boundary that decides what the shape is,
|
||||
* not a consumer of an already-validated one.
|
||||
*
|
||||
* A key counts as requested only when it carries the spec's empty-object marker
|
||||
* (`camera: {}` — {@link McpUiResourcePermissions} defines every permission that
|
||||
* way). Everything else fails closed: `true`, `false`, any string, `null`, a
|
||||
* number, an array, and any key outside the four the proxy knows. `true` in
|
||||
* particular looks like a harmless shorthand, but honoring an undocumented one
|
||||
* means a server typo turns an iframe permission ON, which is the wrong
|
||||
* direction to guess in.
|
||||
* A key counts as requested only when its value is an **object** — the marker
|
||||
* {@link McpUiResourcePermissions} defines for every permission is `{}`, and
|
||||
* presence of that object is the request. Everything else fails closed:
|
||||
* `true`, `false`, any string, `null`, a number, an array, and any key outside
|
||||
* the four the proxy knows. `true` in particular looks like a harmless
|
||||
* shorthand, but honoring an undocumented one means a server typo turns an
|
||||
* iframe permission ON, which is the wrong direction to guess in.
|
||||
*
|
||||
* The object is not required to be *empty*. `{}` is a type with no fields yet,
|
||||
* not a promise there will never be any — it is precisely the spec's extension
|
||||
* point, so rejecting `{ camera: { … } }` would fail closed the day a
|
||||
* permission gains an option, silently dropping grants from servers written
|
||||
* against the newer spec. The value is normalized to `{}` on the way out
|
||||
* either way, so an unrecognized field can't reach the proxy; what it cannot
|
||||
* do is *narrow* a grant the key's presence already asked for.
|
||||
*
|
||||
* Returns undefined when nothing survives, so the notification and the
|
||||
* `hostCapabilities.sandbox` echo carry no permissions at all rather than an
|
||||
|
|
|
|||
Loading…
Reference in a new issue