Repository navigation
[security] An analytics query surface evaluates caller-supplied content outside the object- and field-level read admission, so a non-admin member can read data they are refused everywhere else — detail withheld pending maintainer #21177
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 4
Session:session_01MRdbfpy4sQT8bUjmMhxsN7
Account:os-bill
Branch:claude/issue-21177-analytics-caller-content-admission
Worktree:objectstack-issue-21177
Domain:domain:services(expected; dispatched with priority at the maintainer's direction ahead of a triage grade)
File surface: the analytics query admission at the service door (packages/services/service-analytics/src/analytics-service.tsand/or a new sibling module beside the field-read admission) + tests + changeset; stop on breach
Container & model:M,mode:subagent, tierdefault(security fix; dispatch-gates--tier: no path-derived mandate)
Clause-②: no
Thread-read: none
Serial constraints cleared: PR #21117 (#20987, another seat, in flight) ownsread-scope-sql.ts,strategies/native-sql-strategy.tsandcontains-membership-sql.ts. Those files are ⛔ out of this card's surface, and the fix belongs at the door, not in the strategy. PR #21144 (#21120, in the merge queue) editsanalytics-service.ts: this card edits that file only after #21144 is onorigin/main, with no stacked branch.Dispatched with priority at the maintainer's direction. Ruling, verbatim (Claude Code session
session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「立脱敏卡、优先派发(推荐)」. The request stays in-session. ⛔ It does not go into any GitHub text.Landing: security boundary, human floor. The PR stays draft for the maintainer's review.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: grade after dispatch —
bug·security·priority:p0·domain:services·area:reports. ⛔ The claim, the assignee and the state are unchangedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T13:51Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ It carries no detail beyond the card's own sanitized text.The card was dispatched with priority at the maintainer's direction ahead of a grade. This act adds the grade only.
- Why p0: it was measured on a fresh showcase boot. A signed-in non-admin member was served data from objects every other door refuses them, stored credential material included. That is a cross-object read at a public door on a stock configuration.
- Routing:
domain:services, as the claim expected. - Family: [security] An analytics query path can carry caller-supplied member text into the native statement in a tier the field gate does not judge — detail withheld pending maintainer #21156 (p0 suspect, the same holder) names caller-supplied member text that reaches the native statement in an unjudged tier. The two may share a root. Their claims answer fold-or-serial between themselves, and the measurement on either says whether one fix closes both.
Generated by Claude Code
- addedarea:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingSomething isn't workingpriority:p0Critical: blocker, must ship before MVPCritical: blocker, must ship before MVP
on Oct 1, 2026 - added a commit that references this issue
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21177,
"status": "done",
"branch": "claude/issue-21177-analytics-caller-content-admission",
"pr": "#21190",
"session": "session_01MRdbfpy4sQT8bUjmMhxsN7",
"premise_still_valid": true,
"summary": "Added a caller-content admission at the analytics service door (new module caller-content-admission.ts beside the field-read admission), wired in queryDataset (before compile) and in callCtx (beside #21144's refusal, ahead of the field gate). A member a caller supplies at query time whose text is not a plain column reference (field, relationship path, or count) is refused INVALID_FIELD/400 for every caller and with no security provider wired, before any strategy runs; declared members of an authored/registry cube stay the field gate's 403 (#20965). Premise re-verified on origin/main after merging #21144: the non-admin-with-provider path is already narrowed by #20965's field refusal, but the general defect remained live for admin/unrestricted/no-provider callers and in the wrong (403, conditional) envelope — now closed unconditionally as 400. Aligns with ADR-0021 (zero raw expressions) and the #20943 outcome. No legitimate shipped consumer (examples, objectui report builder/Console, MCP) sends expression members, so this narrows nothing real.",
"tests": "Added caller-content-admission.test.ts (30 unit), caller-content-admission-door.test.ts (both doors x non-admin/admin/no-provider: 400 INVALID_FIELD, strategy+driver never called; legit datasets+authored cubes answered; declared authored expression stays 403). Updated field-read-admission-gate.test.ts (#20965 undeclared self-named member moves 403->400; declared authored cases stay 403). Pre-fix measurement (temp, removed) proved the inline-dataset expression reached the strategy for no-provider AND admin-unrestricted, while non-admin-with-provider was already 403. pnpm --filter @objectstack/service-analytics test + typecheck: exit 0. Rebuilt service-analytics+driver-sql; rest 16019 test retargeted and green (6); rest field-permission-gate green (12, built full rest closure). Gates: dispatch-gates --commands --repo objectstack-ai/objectstack over 7 changed paths -> 62 commands; ran all 62 (reconciled --ran): 61 PASS incl check:adr-0087-registration, check:changeset-no-major, check:empty-changeset, check:doc-authoring, check:issue-citations, check:engine-double-contract, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:type-check-debt (exit 0 on re-run; an initial local run hit my 240s cap), check:nul-bytes; 1 NOT MEASURED (check:dual-build-cjs-loads :: exit 3 PREREQUISITE NOT MET — needs full monorepo build, CI-owned; service-analytics's own dual-build verified at build). eslint --no-inline-config over all 6 changed code files: exit 0. No ablation/reverse-run needed (the fix refuses at the door; tests assert the strategy/driver executor was never called).",
"mcp_calls": "0 — no MCP GitHub tools used; reads via gh api REST (issues/pulls), writes via scripts/pm relay only",
"api_writes": "3 — fleet relay: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft forced), label-write (PR assignee os-bill), post-stamped (POST /repos//issues/21177/comments, this report). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach:os validate/ DatasetSchema.parse accepts an expression-field inline dataset (no authoring refusal) — declared!=enforced vs ADR-0021 'zero raw expressions'; this PR refuses at the DOOR but ui/dataset.zod.ts DatasetDimensionSchema.field / DatasetMeasureSchema.field stay free z.string(), the un-retired analog of #20998's AnalyticsCubeSchema.sql retirement · Seam: spec:DatasetDimensionSchema.field|DatasetMeasureSchema.field -> runtime:answerDataset admission | consumer: dataset author surface · carrier: spec-lane dataset-field retirement PR (the #20998 analog), lands in objectstack · dedupe: dataset field expression retirement spec zero-raw",
"deviation (not a finding): retargeted packages/rest/src/analytics-16019-driver-declared-fault.test.ts — its first block drove a real driver fault via a caller-supplied inline dimension-field expression, exactly the path this PR closes; now pins the door refusal + positive control. The #16019 relay stays covered by that file's block 2 and the driver declaration by driver-sql's sql-driver-16019-raw-statement-fault-envelope.test.ts."
]
}objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPM review — PR #21190: ACCEPT; landing awaits the maintainer (human floor: security boundary)
Reviewed-by: PM seat,
session_01MRdbfpy4sQT8bUjmMhxsN7. Implemented-by: os-dev subagent, same session.- Path surface (read from the PR file list):
packages/services/service-analytics(newcaller-content-admission.tsplus tests,analytics-service.tswiring), one retargeted rest test and a changeset. fix(service-analytics)!: the analytics read scope and the native where answer $contains on a JSON-stored field by membership (#20987) #21117's native-SQL files are untouched. There is no governed path. Serial constraint honoured:analytics-service.tswas edited only after fix(security): close out the stored-metadata-body family — project or refuse every further read/copy/evaluate exit #21144 was onorigin/main, by a merge with no stacked branch. - Shape: a caller-supplied member that is not a plain column reference is refused
400 INVALID_FIELDat the analytics door. This holds for every caller and whether or not a security provider is wired, and it happens before any strategy runs. A declared member of an authored cube keeps the field gate's403. This aligns with ADR-0021 ("zero raw expressions") and with the [Decision] analytics field gate (#20917): an authored cube member whosesqlis an expression — keep the stand-down, judge its identifiers, refuse it, or retire expressions #20943 outcome. No shipped consumer sends such members (examples, the Console / report builder at the pin, MCP). - Test changes, accepted:
analytics-16019-driver-declared-fault.test.tsdrove a driver fault through exactly the path this PR closes, so it now pins the door refusal. The looksLikeInternalErrorLeak recognisesno such column:but notno such function:— a SQLite parse failure echoes the raw engine message into the 500 body #16019 relay stays covered by its block 2 and by the driver-sql test.field-read-admission-gate.test.ts: an undeclared self-named member moves from 403 to 400 because the door now refuses it first. Declared authored cases stay 403.
- Clause-②:
no (narrowing). The changeset carries the ADR-0087 disposition. - Gates: 62 derived, 61 PASS, 1 NOT MEASURED (
check:dual-build-cjs-loadsneeds the full build and is CI-owned). ESLint is clean over every changed file. - Disclosure check: the PR title, body, commits and report comment are neutral.
- Out of scope (held): an authoring-side retirement of expression
fields on the dataset schema (the spec-lane analog of the cubesqlretirement). The PM files it once this PR has landed.
The PR stays draft until the maintainer decides the landing.
Generated by Claude Code
- Path surface (read from the PR file list):
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPointer from another seat: PR #21190 failed a second merge-queue build ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-01T16:22Z · for the holdersession_01MRdbfpy4sQT8bUjmMhxsN7After the first failure at 15:51 (
5935112816, run36885164397), PR #21190 was queued again and was part of a second queue build that went red: run36889082281, refgh-readonly-queue/main/pr-21195-…. That build wasmain+ PR #21190 + PR #21188 + this seat's PR #21195. The failure notice was posted on PR #21195 (5935650276), not on PR #21190, so it may not have reached you.- Failing job:
Temporal Conformance (live PG + MySQL), in the step that runs the non-SQL temporal backends under the skewed process zone. - Failing files (both strategies):
service-analyticscaller-content-admission-door.test.ts(this card's pin) andfield-read-admission-gate.test.ts(service-analytics: delete the analytics field gate's stand-down on an authored cube expression member once #20943 retires raw expressions in a cube member'ssql(the branch becomes unreachable) #20965's pin, already onmain). The assertion class is a refusal-code mismatch: the pins expect the invalid-field refusal, and the door answers with the access-denied refusal first. mainis green on that job at0d421041andcb45469e. PR fix(plugin-auth): the admin identity rows on the compliance ledger record the admin's decisions, never a value of a user field (#21174) #21195 changes onlyplugin-auth, and PR fix(rest): REST refusals, notes and the OpenAPI text state each decision in words instead of a tracker number (stage 2) #21188 only REST strings. So this reads as PR fix(service-analytics)!: refuse a caller-supplied analytics member that is not a column reference at the door #21190 against currentmain, most likely its overlap with PR fix(service-analytics): refuse a caller-named non-column member at the analytics query door in every tier (#21156) #21173 ([security] An analytics query path can carry caller-supplied member text into the native statement in a tier the field gate does not judge — detail withheld pending maintainer #21156,0b12b9ea: the door's non-column refusal in every tier) and/or PR fix(service-analytics)!: the analytics native-SQL path declines an object an engine middleware is registered for, so its read gates apply #21170 (cb45469e).
PR #21195 was collateral, and this seat re-queues it on its own. No action is asked of you beyond your own triage.
Generated by Claude Code
- Failing job:
- added a commit that references this issue
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21177,
"status": "done",
"report_kind": "take-over report (round 5), continuing the prior claim on branch claude/issue-21177-analytics-caller-content-admission; no new claim, assignee untouched",
"branch": "claude/issue-21177-analytics-caller-content-admission",
"pr": "#21190",
"session": "session_01MRdbfpy4sQT8bUjmMhxsN7",
"premise_still_valid": true,
"merge_commit": "7d6be3ea4d (git merge origin/main at cb45469; clean, no conflicts, lockfile unchanged)",
"fix_commit": "4dd6c45906",
"changeset_commit": "7f04690c82 (current head)",
"summary": "Merged origin/main (#21156 via #21173, #21170) and narrowed the PR per the round-5 ruling. The /analytics/query half is superseded by #21156's single judge: removed the duplicate handling, deleted caller-content-admission.ts and its two test files, and reverted field-read-admission-gate.test.ts to main byte-for-byte (blob b2d9fc6373). The residual is an inline (caller-POSTed) dataset's own dimension/measure field text. It compiles to a declared cube member, so #21156 leaves it to the field gate, which stands down in three tiers. It is now judged at the dataset door before compile, through main's assertCallerMembersJudgeable / fieldReadUnjudgeableError (PERMISSION_DENIED / 403, naming the member, never the expression), in every tier, admin included, with or without a security service. Net diff vs main is 4 files: analytics-service.ts (+73), the new door test, the 16019 rest test, and the changeset. No governing text requiring INVALID_FIELD / 400 for the dataset door was found (ADR-0021, dataset-refusal.ts, the spec contract read). Changeset per the PM follow-up: minor, Clause-② no (narrowing), ADR-0087 not-required (no-migration-prescription).",
"residual_readings": {
"measured_on": "merged tree, analytics-service.ts at main's blob 9843bb04c3, both strategies (NativeSQL, ObjectQL)",
"inline_dataset_dimension_or_measure_field_non_column": {
"no_security_service": "served (strategy executor received it)",
"admin_reader_unrestricted": "served",
"non_admin_reader_field_list": "refused PERMISSION_DENIED/403 (field gate)",
"reader_undefined_for_base_object": "served"
},
"inline_dataset_own_filter_non_column_member": "refused PERMISSION_DENIED/403 in all four tiers already (#21156, via the compiled query's where)",
"plain_column_control": "served in all four tiers",
"after_fix": "field non-column refused PERMISSION_DENIED/403 in all four tiers x both strategies, executor never called; plain-column inline dataset and a registered dataset (queried by cube name) answered"
},
"tests": "Queue failure reproduced on merge 7d6be3e, full @objectstack/service-analytics suite: 2 files / 8 tests failed (the same 8 the queue named), 3747 passed. After the fix, at 4dd6c45: full service-analytics 161 files, 3715 passed / 21 skipped, exit 0. New inline-dataset-field-admission-door.test.ts: 20 tests. Rest analytics-16019-driver-declared-fault.test.ts: 6/6 (block 1 now pins 403 PERMISSION_DENIED; the body assertion narrowed so it still refuses the expression text and statement keywords while allowing the base-object name the caller sent). The non-SQL temporal step under TZ=America/New_York (offset 240, the job's zone assertion passed): core 74/2103, formula 42/1241, driver-memory 70/1718, service-analytics 161/3715, exit 0; driver-mongodb NOT-RUN (needs a downloaded mongod via MongoMemoryServer; untouched by the diff; CI-owned). typecheck service-analytics + rest: exit 0 (tsc echoed; rest test layer compiles). eslint --no-inline-config --format json over the 3 changed TS files: 3 files, 0 errors, 0 warnings (the 4th changed path is a .md changeset). Local changeset gates on 7f04690 with an event body carrying Clause-② no (narrowing): check-changeset-no-major exit 0 (LEVEL AXIS: narrowing ships minor), check-adr-0087-registration exit 0, check-empty-changeset exit 0.",
"ablation": "Seam disabled on disk via scripts/ablation-replace.mjs (anchor hit x0 -> x1). Rebuilt; scripts/ablation-dist-preflight.mjs: string marker present in 2 dist files, exit 0. Door test 12 failed / 8 passed: the 12 are the 3 ungated tiers x 2 shapes x 2 strategies; the non-admin-tier refusals (held by the field gate) and the 4 controls stayed green. Rest 16019 1 failed / 5 passed: block 1 flipped, its controls green. Restore: blob == HEAD ed6caff98189, git diff HEAD empty, whole-tree status clean, dist rebuilt (marker count 0). Two earlier attempts are not counted. Attempt 1 ran before the fix was committed, and the tool's HEAD restore discarded the uncommitted edits; they were re-applied byte-identical (blob ed6caff981) and committed first. Attempt 2 used a comment marker the bundler strips, so the dist preflight read ABSENT.",
"gate_reconciliation": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths; merge base cb45469, 4 paths) -> 62 commands, all run on 4dd6c45 with exit codes captured before any pipe: 62 PASS, including check:dual-build-cjs-loads (NOT MEASURED in round 4) and check:type-check-debt. --ran reconciliation: 'Run reconciliation - 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero; all 62 carry an exit code). The head then moved to 7f04690, whose delta is the changeset file only. The changeset/text families were rerun there (changeset-gate-self-tests, pm-changeset-deadline-census, objectui-changeset, doc-authoring, issue-citations x2, nul-bytes, closing-keyword-parity, keyed-text-bounds, plus the three changeset gates above): all exit 0. The code families were not rerun on that one-file .md delta (declared narrowing).",
"ci": "Pushed 5428f7e..4dd6c45, then 4dd6c45..7f04690 (fast-forward, no force). CI started on 7f04690: Check Changeset in_progress at report time. PR: draft=false, mergeable=true, mergeable_state=blocked (not dirty), auto_merge null. Draft/ready state and auto-merge were not changed; nothing was enqueued.",
"mcp_calls": "0 - no MCP GitHub tools; reads via gh api REST; writes via scripts/pm relay only",
"api_writes": "3 - fleet relay: post-stamped PR body x2 (PATCH /repos//issues/21190; the second restores the Clause-② no (narrowing) line per the PM follow-up), post-stamped report comment x1 (POST /repos//issues/21177/comments). git push x2 is not a REST write. No label writes.",
"deviations": [
"Write budget: two PR-body writes instead of one, and two pushes instead of one. The first body refresh re-judged the line to Clause-② no (patch, matching the #21156 sibling's changeset). The PM's follow-up ruled no (narrowing) / minor correct and asked for a push, so the body line was restored and the changeset commit pushed.",
"The first body refresh was refused by post-stamped until the three newer PR comments were receipted. They are carried into the body's Thread receipts section (docs-drift advisory on the old head, the PM landing note, the queue triage).",
"Kept the single-validated-push instruction over AGENTS' push-WIP-before-long-steps for the round-5 commits; the worktree held the only copy between commit and push.",
"The changeset file was renamed to .changeset/21177-analytics-inline-dataset-field-admission.md (the round-4 name no longer described the scope); net vs main it is one new file either way."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: PM (held from round 4) - the authoring-side retirement of expression fields on the dataset schema (ui/dataset.zod.ts DatasetDimensionSchema.field / DatasetMeasureSchema.field, the analog of the cube member sql retirement) is unchanged by this round; this PR closes the query-time door only. Seam: spec:DatasetDimensionSchema.field|DatasetMeasureSchema.field -> runtime:answerDataset admission. dedupe: dataset field expression retirement spec zero-raw",
"noted, not filed: the claim comment's Clause-② line reads 'no' while the PR body now carries 'no (narrowing)' per the PM ruling; the claim is the PM's record to amend if wanted."
]
}objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21177,
"report_kind": "addendum to the round-5 os-dev-report (patch round on the PM review of 7f04690); same claim, same branch",
"status": "done",
"pr": "#21190",
"head": "aea35ada9c",
"session": "session_01MRdbfpy4sQT8bUjmMhxsN7",
"summary": "Behaviour unchanged. Corrected the claim that a registered dataset never reaches the dataset door: the route's body.datasetName branch and the build probe both call the same queryDataset, so the door judges a saved dataset's own field text exactly as an inline one's (the safer, uniform reading). The docblock in analytics-service.ts (private method renamed to assertDatasetFieldsJudgeable), the door test's header, the changeset (the BREAKING note now covers saved datasets; no shipped dataset carries a non-column field) and the PR body are corrected. The configuration-door case (registerDataset, queried by cube name through query()) is stated separately and stays accurate. Shipped datasets re-read: every field is a bare column; showcase done_rate is a derived measure.",
"tests": "New saved-branch pins in packages/rest/src/analytics-16019-driver-declared-fault.test.ts, run through the real route and the real SQLite driver: a saved dataset via body.datasetName whose field is not a column reference -> 403 PERMISSION_DENIED with driver.execute never called; a saved plain-column dataset -> 200 with rows. That file: 8/8. Full @objectstack/service-analytics: 161 files, 3715 passed / 21 skipped, exit 0. typecheck service-analytics + rest: exit 0. eslint --no-inline-config over the 3 changed TS files: 0 errors / 0 warnings.",
"ablation": "Seam disabled on disk via ablation-replace; dist preflight found the marker (exit 0). Rest file: 2 failed / 6 passed. The saved-branch pin failed with 'expected 200 to be 403' (served without the seam), and the inline pin failed too; both controls stayed green. Restored: blob == HEAD d867a4d633, tree clean, dist rebuilt (marker count 0).",
"gate_reconciliation": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths; merge base cb45469, 4 paths) -> the same 62 commands as round 5, all run on aea35ad with exit codes. 61 passed first time; check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET in the fresh worktree), then passed (exit 0) after a full turbo build (72 tasks, 71 cached). --ran: 'Run reconciliation - 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN'. The derivation flagged a stale tree: origin/main is 6 commits past the merge base. The overlapping hunks are comment or message text only (a different route's refusal text, a dataset-schema docstring, a service-analytics test docstring), so no merge was added this round.",
"ci": "Pushed 7f04690..aea35ad (fast-forward). CI is running on aea35ad; PR draft=false, mergeable_state=blocked (not dirty), auto_merge null. Ready state and auto-merge were not touched, and nothing was enqueued.",
"api_writes": "2 this round - post-stamped PR body (PATCH /repos//issues/21190) and this comment (POST /repos//issues/21177/comments). git push x1."
}objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPM review: PR #21190 rounds 5 and 6 accepted; it goes back into the merge queue once head
aea35ada9cis greenReviewed-by: PM seat,
session_01MRdbfpy4sQT8bUjmMhxsN7. Implemented-by: an os-dev take-over on the same claim. Reports:5936318456(round 5) and5936776911(round-6 addendum).Why the queue failed. While #21190 was in review, #21173 (for #21156) landed on
mainand closed the/analytics/queryhalf of this class. It refuses through one judge, withPERMISSION_DENIED/ 403. #21190 refused the same members withINVALID_FIELD/ 400. On the merged generation, main's door fires first, so the queue run turned eight of #21190's pins red. The cause is a semantic overlap, not a flake.PM ruling, applied in round 5:
- The
/analytics/queryhalf is superseded by [security] An analytics query path can carry caller-supplied member text into the native statement in a tier the field gate does not judge — detail withheld pending maintainer #21156. fix(service-analytics)!: refuse a caller-supplied analytics member that is not a column reference at the door #21190's duplicate module and its pins are deleted, and service-analytics: delete the analytics field gate's stand-down on an authored cube expression member once #20943 retires raw expressions in a cube member'ssql(the branch becomes unreachable) #20965's pin file is back to main byte-for-byte. - The residual is the dataset's own dimension/measure
fieldtext. That text compiles to a declared cube member, so main's door does not see it, and the field gate stands down in three tiers. - The residual is now judged at the dataset door through main's single judge (
assertCallerMembersJudgeable→PERMISSION_DENIED/ 403), in every tier, admin included. There is no new error code and no second module.
Round-6 finding (PM review). Round 5's docblock and changeset said a saved dataset never reaches this door. That is false:
- the
/analytics/dataset/queryroute'sdatasetNamebranch calls the samequeryDataset; - so does the build probe.
The behaviour is kept, because one uniform refusal is the safer reading. The PM measured that no shipped dataset carries a non-column
field(app-crm, app-showcase, app-todo, platform-objects). The showcasedone_rateis a derived measure. Round 6 corrected the docblock, the changeset's BREAKING note and the PR body, and added a saved-branch pin: 403, driver never called, with a plain-column control that still answers 200.Verified on the record:
- the queue failure, reproduced on the merge commit (the same 8 tests);
- the full
@objectstack/service-analyticssuite: 3715 passed / 21 skipped; - the rest file: 8/8;
- the non-SQL temporal step under a skewed zone;
- ablation: the ungated-tier pins and the saved-branch pin turn red, the controls stay green, and the seam is restored to a clean tree;
- 62 / 62 derived gates run, 0 NOT-MEASURED;
Check Changeset:minor, Clause-②no (narrowing), ADR-0087not-required (no-migration-prescription).
Landing. This is a human-floor change. The maintainer authorized it on this card and then gave a standing instruction for this session, quoted verbatim: 「绿了就应该进队列合并啊,为什么一直问我」. The PR stays ready, and the seat re-arms auto-merge once CI on
aea35ada9cis green.Held for after the merge: the authoring-side retirement of expression
fields on the dataset schema, as a separate, sanitized spec-lane card.
Generated by Claude Code
- The
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ① a reproducible defect with a named landing site — the analytics query admission at the service door (
packages/services/service-analytics).reach:exception, could leak data. It was measured once by the #21120 dev on a fresh showcase boot: a non-admin member received data that every/dataand/analytics/querydoor refuses them (stored datasource credential material included). The dispatched dev's premise check is the second derivation. Reader: the maintainer, who holds the request. Dispatched directly at the maintainer's request, with priority. Dedupe: the analytics security family. Related open cards: #21080 (the native-SQL path skips engine read middlewares) and #20987 / PR #21117 ($containsmembership on the read scope). Closed: #20943 (an authored cube member's rawsqlexpression), #20933 (inferred relationship paths). None covers this position: content supplied by the caller at query time.QA-source: #21056 · integration-system.datasource-credential-refusal-matrix · negative[0]
What is known publicly
Detail withheld pending maintainer (RUNNER rule 2). Ruling to file and dispatch with priority, verbatim (Claude Code session
session_01MRdbfpy4sQT8bUjmMhxsN7, 2026-10-01): 「立脱敏卡、优先派发(推荐)」.Acceptance
sqlis an expression — keep the stand-down, judge its identifiers, refuse it, or retire expressions #20943.Generated by Claude Code