From 008f06dac5107de14d8e39fff662aa855cfd0d95 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 17:03:36 +0000 Subject: [PATCH] docs(qa): close out D1 in the platform checklist + codify run-record home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D1 (saved-report schedule routes) was fixed in #6683, so this reconciles the checklist and the follow-up register with the fixed state: - dashboards.saved-report-ownership: flip the known-gap probe clause from an expected-FAIL probe to a positive deny-as-404 assertion (revision 1->2, history entry ref #6683); drop the expected-FAIL language in negative/source. - FOLLOW-UPS.md: mark D1 fixed in #6683 and remove the exploit detail. - runs/README.md: name the canonical home for run records — one "[sweep] vN release test sweep" tracking issue per release (resolves the open archival question in #6681). Docs only; check:platform-checklist green (178 items, 27 mapped / 2 waived). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01L8aEBrJVxRnA5XVRkeVft9 --- docs/qa/platform-checklist/FOLLOW-UPS.md | 4 ++-- .../platform-checklist/areas/dashboards.json | 18 ++++++++++++------ docs/qa/platform-checklist/runs/README.md | 19 ++++++++++++++++--- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/docs/qa/platform-checklist/FOLLOW-UPS.md b/docs/qa/platform-checklist/FOLLOW-UPS.md index 9bab64cdd2..12e285bd90 100644 --- a/docs/qa/platform-checklist/FOLLOW-UPS.md +++ b/docs/qa/platform-checklist/FOLLOW-UPS.md @@ -11,11 +11,11 @@ decide on, and docs that promise retired capabilities. These are real runtime/UI defects the gap hunters hit while grounding items. Each is captured inside the relevant checklist item as an **expected-fail probe** (so a run records the actual behavior instead of ticking green), but they are defects, not test -gaps. Security-sensitive ones were deliberately NOT filed publicly — your call. +gaps. The one security-sensitive finding (D1) has since been fixed in #6683. | # | defect | evidence | captured in | sensitivity | |---|---|---|---|---| -| D1 | **Saved-report schedule routes lack an owner check** — `report-service.ts` `unscheduleReport`/`listSchedules` ignore `_context`, so user B can delete user A's report schedule (cross-owner destructive access). The read/run/delete routes ARE owner-gated (deny-as-404); only the schedule routes leak. | packages/plugins/plugin-reports/src/report-service.ts (unscheduleReport/listSchedules) | dashboards.saved-report-ownership (known-gap probe clause) | **SECURITY — not filed publicly; awaiting your decision** | +| D1 | **Saved-report schedule routes now owner-gated** — the schedule delete/list routes were brought under the same parent-report owner check as the other report routes (deny-as-404). | packages/plugins/plugin-reports/src/report-service.ts | dashboards.saved-report-ownership (positive assertion since rev 2) | **FIXED in #6683** | | D2 | **AppManagementPage enable/disable/set-default/delete are client-only stubs** — the handlers call `toast.success()` with a `TODO: Replace with real API call` and issue no request; an admin sees "success" while nothing changes. | objectui apps/console/src/pages/system/AppManagementPage.tsx | platform-core.app-management-toggle (expected-fail probe) | UX-integrity — safe to file | | D3 | **`useGlobalUndo.executeOp` issues a bare `ds.update` with no `ifMatch`** — record undo can silently clobber a concurrent edit (no OCC guard on the undo path). | objectui react/src/hooks/useGlobalUndo.ts | records-forms.record-edit-undo (observe-and-flag clause) | correctness — safe to file | | D4 | **`SharedViewLink` builds dead `/share//?token=` URLs** — client-generated token, no matching console route (only `/s/:token`), no server persistence. Registered but unused. | objectui plugin-view/src/SharedViewLink.tsx | — (not an item; demo-grade) | low — file a cleanup issue | diff --git a/docs/qa/platform-checklist/areas/dashboards.json b/docs/qa/platform-checklist/areas/dashboards.json index 0ab6bf2173..23c2d65c33 100644 --- a/docs/qa/platform-checklist/areas/dashboards.json +++ b/docs/qa/platform-checklist/areas/dashboards.json @@ -768,7 +768,7 @@ "title": "Saved reports are owner-isolated: cross-owner read/run/delete deny as 404, schedules included", "since": "v15.1", "status": "active", - "revision": 1, + "revision": 2, "priority": "P1", "surface": "api", "personas": [ @@ -786,7 +786,7 @@ "as A: POST /api/v1/reports (201) and POST /api/v1/reports/:id/schedule; record both ids", "as B: GET /api/v1/reports/:idA, DELETE /api/v1/reports/:idA, POST /api/v1/reports/:idA/run — capture each", "as B: GET /api/v1/reports (list) with and without ?ownerId=A — capture row sets", - "as B: GET /api/v1/reports/:idA/schedules and DELETE /api/v1/reports/schedules/:scheduleIdA — capture each (see the known-gap clause)", + "as B: GET /api/v1/reports/:idA/schedules and DELETE /api/v1/reports/schedules/:scheduleIdA — capture each (both owner-gated since #6683)", "anonymous: GET /api/v1/reports", "as A afterwards: re-read the report and schedule to prove nothing was destroyed" ], @@ -810,9 +810,9 @@ "evidence": "the response" }, { - "clause": "KNOWN-GAP PROBE — schedule routes: unscheduleReport and listSchedules currently ignore the caller context (report-service.ts), so B deleting A's schedule succeeds today; the contract this item asserts is deny-as-404, so record the actual outcome and treat a 2xx as a FAIL with a privately-raised finding (do NOT file publicly without maintainer decision — cross-owner destructive access)", + "clause": "schedule routes are owner-gated too (#6683): B's GET /reports/:idA/schedules returns an empty list (never A's recipients/cron), and B's DELETE /reports/schedules/:scheduleIdA answers 404 REPORT_NOT_FOUND — never a cross-owner 2xx", "oracle": "api", - "verify": "the two schedule-route responses + A's schedule surviving (re-read as A)", + "verify": "the two schedule-route responses' status+body + A's schedule surviving (re-read as A)", "evidence": "responses + the survival read" }, { @@ -823,14 +823,14 @@ } ], "negative": [ - "any cross-owner 2xx anywhere on /api/v1/reports* is a FAIL; the schedule-route clause documents the one place a FAIL is expected TODAY — a run must not tick it green until the owner check lands" + "any cross-owner 2xx anywhere on /api/v1/reports* is a FAIL — including the schedule routes, which #6683 brought under the same canAccessReport gate (they leaked before)" ], "traps": [ "wrong-persona" ], "source": [ "packages/rest/src/rest-route-ledger.ts (reports family)", - "packages/plugins/plugin-reports/src/report-service.ts (canAccessReport #2980; the unchecked unscheduleReport/listSchedules)", + "packages/plugins/plugin-reports/src/report-service.ts (canAccessReport #2980; unscheduleReport/listSchedules owner-gated in #6683)", "packages/platform-objects/src/audit/sys-saved-report.object.ts", "docs/plans/release-15.1-test-plan.md §A10 (#2980/#2981/#2975)" ], @@ -840,6 +840,12 @@ "date": "2026-08-07", "change": "initial — 15.1 §A10 was never imported; the sweep also surfaced the unchecked schedule routes, recorded here as an expected-FAIL probe", "ref": "claude/platform-test-checklist-ocwugl" + }, + { + "revision": 2, + "date": "2026-08-08", + "change": "D1 fixed in #6683 — unscheduleReport/listSchedules now owner-gated; flipped the known-gap probe clause to a positive deny-as-404 assertion and dropped the expected-FAIL language in negative/source", + "ref": "#6683" } ] }, diff --git a/docs/qa/platform-checklist/runs/README.md b/docs/qa/platform-checklist/runs/README.md index 8179f683ea..89c841c6be 100644 --- a/docs/qa/platform-checklist/runs/README.md +++ b/docs/qa/platform-checklist/runs/README.md @@ -8,9 +8,22 @@ land in the repo (`.gitignore` here tracks only this README). The durable source the checklist itself under `../areas/`; a run is a snapshot that goes stale the moment the build moves. -**Where results go instead:** the executing environment — a CI artifact, the runner's -own workspace, the sweep's tracking issue, or an external QA store. Keep them there; -do not commit them. +**Where results go instead — the canonical home is one tracking issue per release +sweep** (a `[sweep] vN release test sweep` issue, successor to the #3358 model): + +- the issue **body** hosts the human-readable summary — the per-item verdict table + (pass / partial / fail / blocked) and the filter that selected them (`since:vN` ∪ all + `P0` ∪ items whose `source` cites a release PR); +- the machine **run-record JSON(s)** (shape below) attach to that issue — pasted in a + comment or linked as a CI artifact from the sweep job; +- every `fail` becomes its **own linked issue** (RUNNER.md makes a filed issue part of a + completed `fail` verdict), cross-referenced from the sweep issue. + +A raw CI artifact or an external QA store is a fine substitute where one exists, but the +per-release tracking issue is the default so a sweep is never lost. What NEVER lands in +the repo is the record itself — only the durable ledger under `../areas/` accumulates +here, through each item's `revision`/`history`. A verdict is interpretable only next to +the `revision` it names, so the run record stays with its build's artifacts, not in git. ## Record shape (write to `YYYY-MM-DD-.json`, kept out of git)