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.
svelte-kit-vice/audit/components/year-grid.md

58 lines
5.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 <!-- id: 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: open
### LOW: Dimension C: DOM-selector — querySelector/querySelectorAll must interpolate user/consumer — year-grid-001 <!-- id: 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<HTMLElement>(
`[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

Powered by TurnKey Linux.