Skip to content

fix: harden composables against prototype pollution, CSS injection, and unbounded allocation#271

Open
johnleider wants to merge 4 commits into
masterfrom
worktree-sec-hardening
Open

fix: harden composables against prototype pollution, CSS injection, and unbounded allocation#271
johnleider wants to merge 4 commits into
masterfrom
worktree-sec-hardening

Conversation

@johnleider
Copy link
Copy Markdown
Member

Security hardening pass over the v0 composables — three independent fixes plus a rule documenting the convention.

  • usePermissionscreatePermissions built a nested plain object keyed by caller-supplied role/action/subject names with no guard, so a __proto__ key in a permissions config (e.g. one fetched from a backend) could pollute Object.prototype. It now skips keys in the shared UNSAFE_KEYS blocklist at all three levels; UNSAFE_KEYS is exported from #v0/utilities so it is reused rather than duplicated.
  • createRatingrange(toValue(size)) allocated an unbounded array, so a large or non-finite size could exhaust memory. The count is now clamped to a finite upper bound before allocating, mirroring the existing guard in createPagination.
  • useThemeThemeAdapter's UNSAFE_CSS pattern now also rejects < and >, closing a </style> breakout on the SSR/unhead innerHTML path. Theme color values never legitimately contain angle brackets.

Also adds a ## Security primitives section to .claude/rules/implementation.md mapping each sink shape to its existing guard, so new code reaches for the primitive instead of reinventing it.

For context: mergeDeep was already guarded, and registry/selection/nested/tokens keyed state is Map-based and pollution-immune by construction. packages/0 typecheck, lint, and the affected test suites are green.

createPermissions built a nested plain object keyed by caller-supplied role/action/subject names with no guard, so a __proto__ key in a permissions config (e.g. one sourced from a backend) could pollute Object.prototype. Skip keys in the shared UNSAFE_KEYS blocklist at all three levels; export UNSAFE_KEYS from #v0/utilities so it is reused rather than duplicated.
range(toValue(size)) allocated an unbounded array, so a large or non-finite size could exhaust memory. Clamp the count to a finite upper bound before allocating, mirroring the guard in createPagination.
Extend ThemeAdapter UNSAFE_CSS to also reject < and >, closing a </style> breakout on the SSR/unhead innerHTML path. Theme color values never legitimately contain angle brackets.
Codify the four sink-to-guard patterns (UNSAFE_KEYS, CSS sanitizer, CSS.escape, bounded range) so new code reaches for the existing guard instead of reinventing it.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 5, 2026

Open in StackBlitz

commit: ec12553

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant