Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions docs/qa/platform-checklist/areas/access-security.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -1221,7 +1221,7 @@
"title": "OWD save gate: external ≤ internal on every object write, and a packaged object's OWD can only be tightened at runtime — Studio inline AND server-side",
"since": "v16",
"status": "active",
"revision": 2,
"revision": 3,
"priority": "P1",
"surface": "mixed",
"personas": [
Expand All@@ -1236,15 +1236,15 @@
],
"knownGaps": [
"runtime EVALUATION of externalSharingModel against external principals is liveness `planned` (#2696) — this item tests the AUTHORING/save gate (external ≤ internal at write; packaged tighten-only), NOT external-principal enforcement; do not tick an external-principal read/write result",
"R1 (owd_widening_forbidden) is only reachable via the OS_METADATA_WRITABLE=object escape hatch; on a STOCK deploy a runtime meta PUT to a packaged object is refused earlier by the writable-package gate (WRITABLE_PACKAGE_REQUIRED) — the run must record WHICH layer answered the 4xx"
"R1 (owd_widening_forbidden) fires on a STOCK deploy too — the escape hatch is NOT what reaches it. What selects the answering layer is the DIRECTION OF THE SUBMITTED BODY, not the deploy posture (measured on stock showcase, #9477): saveMetaItem runs the plugin-security authoring gate BEFORE the overlay/package doors on the host-config kernel the showcase boots (environmentId undefined, so protocol.ts's env-partitioned NOT_OVERRIDABLE branch is skipped), so a WIDENING body meets R1 first and always answers 403 owd_widening_forbidden — hatch open or closed, ?package= named or not. A body that does NOT widen passes R1 and only then meets the overlay door: 403 NOT_OVERRIDABLE (no ?package=) or 403 ITEM_LOCKED (?package= names the read-only base) on stock, and 2xx under OS_METADATA_WRITABLE=object. ⚠ WRITABLE_PACKAGE_REQUIRED was observed on NONE of those six legs — do not expect it here. The run must still record WHICH layer answered the 4xx, and must read the code from `declaredCode`: the dispatcher maps this refusal to `code: PERMISSION_DENIED` on the wire and carries the ledgered spelling in `declaredCode: owd_widening_forbidden`, so a check keyed on `code` alone MISSES it"
]
},
"steps": [
"boot showcase with the console; admin session",
"Studio inline (browser): open the object designer Settings for a writable/draft object; set sharingModel=private and externalSharingModel=public_read; screenshot — the panel must flag the D11 external-wider violation inline (owd-sharing.ts isExternalWider) and not offer a clean save",
"server reject: PUT /api/v1/meta/object/qa_owd_probe with sharingModel:'private' + externalSharingModel:'public_read' — capture the 4xx + error code. ⚠️ Drive this at the ACTIVE-state door, NOT ?mode=draft: the D11 refusal is raised by the runtime lint block (validateSecurityPosture, rule security-external-wider-than-internal), and draft saves deliberately defer it to the draft→active promotion gate (#4463 D1), so a ?mode=draft PUT is ADMITTED and the clause cannot be observed there",
"server admit: PUT the same object with externalSharingModel:'private' (≤ private) — capture the 2xx (the gate orders, it does not blanket-deny)",
"packaged tighten (R1): PUT /api/v1/meta/object/showcase_announcement widening sharingModel private→public_read_write (or externalSharingModelprivate→public_read) — capture the 4xx AND its code (WRITABLE_PACKAGE_REQUIRED on stock; owd_widening_forbidden under OS_METADATA_WRITABLE=object)",
"packaged tighten (R1): PUT /api/v1/meta/object/showcase_announcement widening its shipped sharingModel public_read→public_read_write (keep externalSharingModel:'private' so the D11 lint is not tripped instead) — capture the 4xx AND its code. Expect 403 with `declaredCode: owd_widening_forbidden` (wire `code` is PERMISSION_DENIED) on a STOCK deploy, no escape hatch needed. Control leg, to prove the probe reached R1 rather than a blanket deny: re-PUT the same body TIGHTENED (public_read→private) — R1 passes it and the next door answers 403 NOT_OVERRIDABLE (or ITEM_LOCKED with ?package=com.example.showcase), 2xx under OS_METADATA_WRITABLE=object",
"GET /api/v1/meta/object/showcase_announcement before and after the denied PUT — confirm byte-identical",
"cross-check the shipped declarations: GET /api/v1/meta/object/showcase_account and showcase_announcement — both satisfy external ≤ internal"
],
Expand All@@ -1268,9 +1268,9 @@
"evidence": "the PUT trace + read"
},
{
"clause": "a packaged object's OWD cannot be widened at runtime: the widening PUT on showcase_announcement answers 4xx with a ledgered code — WRITABLE_PACKAGE_REQUIRED on the stock deploy, or owd_widening_forbidden under the OS_METADATA_WRITABLE=object escape hatch — recorded with WHICH layer answered",
"clause": "a packaged object's OWD cannot be widened at runtime: the widening PUT on showcase_announcement answers 4xx with a ledgered code, recorded with WHICH layer answered. On a stock deploy that is 403 owd_widening_forbidden from the plugin-security authoring gate — the gate runs ahead of the overlay/package doors here, so the escape hatch is not what reaches it (#9477)",
"oracle": "api",
"verify": "PUT /api/v1/meta/object/showcase_announcement (widened) status >=400 and code ∈ {WRITABLE_PACKAGE_REQUIRED, owd_widening_forbidden}",
"verify": "PUT /api/v1/meta/object/showcase_announcement (widened) status >=400 and the ledgered code is owd_widening_forbidden. ⚠ read it from `declaredCode`, not `code`: the dispatcher answers `{code: 'PERMISSION_DENIED', declaredCode: 'owd_widening_forbidden'}` on the live REST route, so a check keyed on `code` alone misses the gate it is looking for (measured #9477; note the unit pin packages/rest/src/meta-object-owd-gate.test.ts reads the bare spelling off `code`, which is the in-process shape, not the wire shape)",
"evidence": "the PUT trace + the code + the answering-layer note"
},
{
Expand DownExpand Up@@ -1313,7 +1313,8 @@
"change": "new — OWD save gate both-sides: Studio inline external-wider warning + server object-posture-gate (R2 external≤internal on every write, R1 packaged tighten-only); external-principal evaluation carried as a knownGap (#2696 planned)",
"ref": "claude/platform-test-checklist-ocwugl"
},
{ "revision": 2, "date": "2026-08-18", "change": "corrected clauses 1-2 and steps 2-3 for the #8310 retirement (maintainer ruling 2026-08-13). The item tested R2 owd_external_wider / 403 at the ?mode=draft door; R2 is RETIRED from object-posture-gate.ts as a duplicate of the runtime lint door, whose refusal is 422 INVALID_METADATA rule security-external-wider-than-internal raised at the ACTIVE-state door — and draft saves deliberately defer to the draft-to-active promotion gate (#4463 D1), so the clause was unobservable where it was pointed (#9453 CF-2). ⚠️ #9453 CF-3 (that the R1 knownGap mispredicts which layer answers) was NOT applied: object-posture-gate.ts states verbatim that R1 applies only to overlay writes over an artifact-backed object and that 'the default deploy already 403s these before this gate runs' (if (!ctx.isArtifactBacked) return), which is what the knownGap and clause 4 already say", "ref": "#9386" }
{ "revision": 2, "date": "2026-08-18", "change": "corrected clauses 1-2 and steps 2-3 for the #8310 retirement (maintainer ruling 2026-08-13). The item tested R2 owd_external_wider / 403 at the ?mode=draft door; R2 is RETIRED from object-posture-gate.ts as a duplicate of the runtime lint door, whose refusal is 422 INVALID_METADATA rule security-external-wider-than-internal raised at the ACTIVE-state door — and draft saves deliberately defer to the draft-to-active promotion gate (#4463 D1), so the clause was unobservable where it was pointed (#9453 CF-2). ⚠️ #9453 CF-3 (that the R1 knownGap mispredicts which layer answers) was NOT applied: object-posture-gate.ts states verbatim that R1 applies only to overlay writes over an artifact-backed object and that 'the default deploy already 403s these before this gate runs' (if (!ctx.isArtifactBacked) return), which is what the knownGap and clause 4 already say", "ref": "#9386" },
{ "revision": 3, "date": "2026-08-19", "change": "the R1 knownGap, step 5 and clause 4 named the WRONG answering layer, and #9453 CF-3 was RIGHT — re-measured on a genuinely stock showcase boot and OVERTURNING the revision-2 note above (#9477). Proven stock from the server process's own /proc/<pid>/environ: zero METADATA_WRITABLE occurrences among 147 vars. Target artifact-backed, proven by GET ?layers=true returning a populated `code` layer (_packageId com.example.showcase) with `overlay: null`. A widening PUT then answered 403 declaredCode owd_widening_forbidden — reproduced twice, object byte-identical after. The mechanism the old text missed: saveMetaItem runs runAuthoringGate BEFORE the overlay/package doors on the host-config kernel the showcase boots (environmentId undefined, so protocol.ts's env-partitioned NOT_OVERRIDABLE branch never arms — the #7674 note in that file says the showcase is exactly that shape). So the deploy posture does not select the layer; the body's DIRECTION does. Control leg (same boot, same object, tightened body) passed R1 and answered 403 NOT_OVERRIDABLE, i.e. the door the knownGap called 'earlier' is in fact LATER. Six legs measured (widen/tighten x stock/hatch x ?package= present/absent) and WRITABLE_PACKAGE_REQUIRED appeared on none. ⛔ Not a product defect: every widening leg refused and the object never changed — object-posture-gate.ts behaved exactly as its executable guard (if (!ctx.isArtifactBacked) return) specifies; only the prose parenthetical in that file's header, and this item, mispredicted the door order", "ref": "#9477" }
]
},
{
Expand Down
Loading