You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.5 KiB
5.5 KiB
Audit: year-grid
audit-version: 1 audited-at: 2026-06-26 scope: soma, sema (SCOPE-DRIFT → SYS-1) method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL personally re-verified by the lead. Batch-3 ground-truth: each picker fires trigger(close) (close NOT inert), but open/commit-reset ARE inert; calendar/range-calendar are MID-REFACTOR (uncommitted view-switch work). provider: src/uix/soma/components/year-grid/year-grid-provider.svelte.ts
Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 1. systemic hits: SYS-1. composition (A27): N/A (year-grid is not a picker; it is a standalone grid component, not a composition of primitives)"
Findings
MEDIUM: Dimension A: Scope Drift (SYS-1) — morfo declares scope:['soma','sema'] but eidos director — year-grid-003
- dimension: A-Contract-scope
- rule: Dimension A: Scope Drift (SYS-1) — morfo declares scope:['soma','sema'] but eidos directory exists
- location: src/uix/morfo/components/year-grid.ts:7 vs. src/uix/eidos/components/year-grid/
- evidence: Morfo line 7:
scope: ['soma', 'sema'],. Directory listing shows eidos/components/year-grid/ exists with CSS and Svelte components (year-grid.css, year-grid.svelte, etc.). - impact: The morfo contract claims eidos is out-of-scope, but the eidos layer clearly exists and is active. This is the documented SYS-1 systemic issue.
- proposed-fix: Update morfo scope to
['soma', 'sema', 'eidos']to reflect reality. - verify: [confirmed] Confirmed at MEDIUM (documented systemic SYS-1 scope-drift). Morfo line 7:
scope: ['soma', 'sema'],— eidos omitted. Yet src/uix/eidos/components/year-grid/year-grid.css exists (284 lines, mtime Jun 23, real recipe selecting [data-year-grid], [data-year-grid-cell], [data-year-grid-grid], variant/color/size cascades) plus a full eidos component dir (year-grid.svelte, year-grid-cell.svelte, year-grid-grid.svelte, index.ts, types.ts, README.md). The eidos layer is active and shipping, so the morfo scope under-declares reality. Matches the known SYS-1 baseline; MEDIUM is appropriate. - fix-status: fixed (
212624e0)
LOW: Dimension C: DOM-selector — querySelector/querySelectorAll must interpolate user/consumer — year-grid-001
- dimension: C-DOM-selector
- rule: Dimension C: DOM-selector — querySelector/querySelectorAll must interpolate user/consumer values via CSS.escape
- location: src/uix/soma/components/year-grid/year-grid-provider.svelte.ts:218-220
- evidence: ```
const el = root.querySelector(
[data-year-grid-cell][data-value="${nextYear!}"]);
The selector interpolates `nextYear!` (a number cast to string) without CSS.escape(). While years are numeric and safe in this case, the pattern violates the rule: any consumer-derived value must be escaped.
- impact: If a malicious or accidental value contains CSS selector characters (though unlikely for year numbers), the query could fail or match unintended elements.
- proposed-fix: Import CSS.escape and wrap: ``const el = root.querySelector<HTMLElement>(`[data-year-grid-cell][data-value="${CSS.escape(String(nextYear!))}"]`);``
- verify: [downgraded] Downgraded CRITICAL -> LOW. At provider line 218-220 the selector is `root.querySelector(`[data-year-grid-cell][data-value="${nextYear!}"]`)`. `nextYear` is provably a `number` in every branch of handleCellKeydown (lines 190-197): `year + 1`, `year - 1`, `year + 3`, `year - 3`, `this.pageStart`, `this.pageEnd`, `year - this.decadeSize`, `year + this.decadeSize` — all arithmetic on numbers. No consumer/user-supplied string ever reaches the selector, so it cannot contain CSS metacharacters; the query can never break. CRITICAL ('wrong behavior users hit') is unsupported — the auditor's own evidence concedes 'years are numeric and safe in this case'. The CSS.escape callers elsewhere (listbox-provider:196, radio-group-provider:93, tabs-provider:109, tree-view-provider:110, grid-list-provider:184, command-provider:394) all interpolate an arbitrary consumer `value: string` — a genuinely different risk class. The numeric-grid sibling month-grid-provider.svelte.ts:247-248 follows the identical no-escape pattern (`data-value="${this.placeholderYear}-${nextMonth!}"`), confirming the grid family treats numeric interpolation as safe-by-construction. Real but cosmetic consistency nit at most.
- fix-status: open
## No-findings dimensions
A-Contract-parts, A-Contract-keyboard, A-Morfo-signature, B-Roving-focus, B-A30-heading-registration, D-Soma-eidos-imports
## Theming facts (E-bis)
- magic z-index: none
- magic literals: none
- undeclared parts: none
- roles clean: true · variants clean: true
- conformance: All roles (gridcell, grid, button) are declared in morfo. All parts (provider, header, heading, prev-button, next-button, grid, cell) are registered via runtime.part(). Variants (size, variant, color) are eidos-owned visual attributes, not morfo-declared.
## Tests (F)
- exists: false · env: N/A
- covers:
- untested: Year-grid keyboard routing (all 9 keys: Arrow×4, Home, End, PageUp, PageDown); Focus trap during page navigation; Roving focus tabindex switching; Selection on Enter/Space; Boundary conditions (pageStart/pageEnd edge years); min/max value constraints; readonly state prevents selection; Two-moment sequence: placeholder change + trigger event
## Style observations (non-blocking)
- Keyboard routing follows APG grid pattern correctly (Arrow×4 + Home/End + PageUp/PageDown)
- Roving focus implementation clean with tabindex toggle
- All morfo keyboard actions properly handled in provider