Skip to content

Add per-component sanitizer overrides for component render output#57

Merged
JosunLP merged 7 commits intodevelopmentfrom
copilot/add-sanitizer-options-to-component
Mar 13, 2026
Merged

Add per-component sanitizer overrides for component render output#57
JosunLP merged 7 commits intodevelopmentfrom
copilot/add-sanitizer-options-to-component

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 13, 2026

Component render output in @bquery/bquery/component was sanitized against a fixed allowlist, which stripped valid shadow-DOM markup like open, colspan, rowspan, or opt-in inline style values. This change exposes a per-component sanitizer override so components can extend the base allowlist without relaxing global sanitization defaults.

  • API

    • Added sanitize to ComponentDefinition:
      • allowTags?: string[]
      • allowAttributes?: string[]
    • Scoped to component render sanitization only; global sanitizeHtml() behavior is unchanged.
    • Added JSDoc guidance warning that sensitive opt-ins such as style must only be used with trusted or validated values, because CSS values are not value-sanitized.
  • Render-path behavior

    • Moved the component module’s built-in shadow-DOM sanitizer allowances into shared base constants.
    • Merged definition.sanitize with the existing component baseline before writing render output into the shadow root.
    • Precomputes the merged per-component tag and attribute allowlists once when creating the component class, instead of rebuilding them on every render.
    • Keeps the existing security model intact while allowing explicit per-component opt-ins.
  • Coverage

    • Added focused component tests covering:
      • preservation of opted-in attributes (open, style)
      • retention of existing base allowed attributes
      • continued stripping of dangerous handler content (onclick / injected script text)
      • removal of a disallowed tag by default and preservation of that tag when included in sanitize.allowTags
  • Docs

    • Documented the new sanitize option in the component guide with a minimal usage example.
    • Adjusted the wording so the guide does not imply opted-in attributes are inherently safe.
    • Added an explicit warning that style remains excluded by default and can reintroduce security risks when enabled for untrusted input.
component('bq-dialog', {
  sanitize: {
    allowAttributes: ['open'],
  },
  render: () => html`<div role="dialog" open>Hello</div>`,
});
  • Screenshot
    • Manual browser verification of a component rendering with opt-in sanitizer attributes:

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits March 13, 2026 11:19
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Copilot AI changed the title [WIP] Add per-component sanitizer options in ComponentDefinition Add per-component sanitizer overrides for component render output Mar 13, 2026
Copilot AI requested a review from JosunLP March 13, 2026 11:24
@JosunLP JosunLP marked this pull request as ready for review March 13, 2026 11:27
Copilot AI review requested due to automatic review settings March 13, 2026 11:27
@github-actions github-actions bot added component Changes to the component module docs Changes to the documentation tests Chenges to the tests labels Mar 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a per-component hook to extend the HTML sanitizer allowlist during component shadow DOM rendering, enabling components to opt into extra tags/attributes without changing global sanitization defaults.

Changes:

  • Extend ComponentDefinition with an optional sanitize allowlist (allowTags, allowAttributes).
  • Update component rendering to merge per-component sanitizer options with the component base allowlist.
  • Add a regression test and update the components guide with usage instructions.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/component.test.ts Adds a test asserting component-level sanitizer allowlist extension works and still strips event handlers.
src/component/types.ts Introduces the new sanitize option type on component definitions.
src/component/component.ts Merges component base allowlist with per-component sanitizer options when sanitizing rendered markup.
docs/guide/components.md Documents the new sanitize option for extending a component’s render allowlist.

…onent

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
@JosunLP
Copy link
Copy Markdown
Collaborator

JosunLP commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Applied in 6b4a46d. I updated the component guide and ComponentDefinition.sanitize JSDoc to warn about sensitive opt-ins like style, changed the guide example to use open instead of implying style is safe, and cleaned up the stale branch artifacts that were blocking validation. Screenshot: https://github.com/user-attachments/assets/984e6454-d9cd-43b7-9ca8-198002f260fb

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-component sanitizer configuration to the Web Component rendering pipeline, allowing individual components to extend the sanitizer allowlist (while keeping global defaults unchanged) and documenting/testing the behavior.

Changes:

  • Introduces ComponentDefinition.sanitize (allowTags/allowAttributes) to extend sanitizer options per component.
  • Updates component render sanitization to merge base component allowlists with per-component overrides.
  • Adds a regression test and documentation describing how to extend the allowlist and the security implications (notably for style).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/component.test.ts Adds a test for per-component sanitizer allowlist merging (attributes).
src/component/types.ts Adds sanitize option typing + JSDoc guidance to ComponentDefinition.
src/component/component.ts Defines base component allowlists and merges them with definition.sanitize during render sanitization.
docs/guide/components.md Documents the new sanitize option and warns about style risks.

@JosunLP
Copy link
Copy Markdown
Collaborator

JosunLP commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Copilot AI and others added 2 commits March 13, 2026 15:58
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Applied in b79caa6, with the render-path cleanup finalized in 89ebf2a. I added end-to-end coverage showing dialog is stripped by default and preserved when opt-in sanitize.allowTags includes it, and I moved the merged sanitizer allowlists out of render() so they’re computed once per component class. Screenshot: https://github.com/user-attachments/assets/984e6454-d9cd-43b7-9ca8-198002f260fb

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a per-component sanitize configuration to the Web Components helpers so component render output can opt into a few extra allowed tags/attributes (without changing the global sanitizer defaults), along with tests and docs to lock in the behavior.

Changes:

  • Add sanitize?: { allowTags?: string[]; allowAttributes?: string[] } to ComponentDefinition typings.
  • Merge component-specific sanitizer allowlists into the component render sanitizer call (defineComponent / component).
  • Add tests covering attribute/tag allowlist extension and document the new option.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
tests/component.test.ts Adds coverage for per-component sanitizer allowlist merging (attrs and tags).
src/component/types.ts Extends ComponentDefinition with a typed sanitize option (+ security-focused doc comment).
src/component/component.ts Refactors component sanitizer allowlists into constants and merges in per-definition allowlists at render time.
docs/guide/components.md Documents the new sanitize option and cautions about risks (e.g., style).

@JosunLP JosunLP merged commit fdea8a8 into development Mar 13, 2026
14 checks passed
@JosunLP JosunLP deleted the copilot/add-sanitizer-options-to-component branch March 13, 2026 18:38
JosunLP added a commit that referenced this pull request Mar 15, 2026
* Add `bool()` support for boolean attributes in component templates (#55)

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Add typed state generics to component definitions (#56)

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Pass attribute change metadata to component `updated()` hooks (#58)

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Add per-component sanitizer overrides for component render output (#57)

* Initial plan

* feat: add per-component sanitizer options

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: clarify component sanitizer baseline

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: warn about sensitive component sanitizer opts

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* test: cover sanitizer allowTags overrides

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* perf: precompute component sanitizer allowlists

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Pass previous props into component `beforeUpdate` (#59)

* Initial plan

* Add previous props to beforeUpdate hook

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Refine beforeUpdate old props implementation

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Fix beforeUpdate typing regressions

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Add explicit signal subscriptions to `component()` renders (#60)

* Initial plan

* feat: add component signal subscriptions

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* chore: document signal subscription tracking

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: address signal integration review feedback

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: tighten component signal typing

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: resolve eslint failure in component signal test

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: handle reconnect lifecycle and signal effect errors

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: preserve reconnect signal wiring after hook errors

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: skip beforeUpdate for signal rerenders

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Add trusted sanitized fragments for `safeHtml` composition (#61)

* Initial plan

* feat: add trusted safeHtml fragment support

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* chore: finalize trusted safeHtml support

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: address trusted safeHtml review feedback

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: fix second trusted safeHtml review follow-up

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* refactor: split trusted html helpers from sanitizer

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* refactor: tighten trusted html branding

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: tighten trusted html internals

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>

* Reuse component shadow-root style nodes across re-renders (#62)

* Initial plan

* fix: reuse component style element on rerender

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* chore: finalize component style reuse fix

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* test: tighten style reuse regression assertions

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Add `@bquery/bquery/storybook` template helpers for Storybook stories (#63)

* Initial plan

* feat: add storybook template helper

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: clarify storybook when fallback behavior

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: keep storybook helpers out of full bundle

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix: sanitize storybook helper output

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* test: tighten storybook helper security coverage

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* feat: enhance storyHtml with improved boolean attribute handling and sanitization tests

* fix: update storyHtml example to reflect correct boolean attribute output

* fix: defensive tag delimiter and parser comments per code review

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* chore: release version 1.6.0

- Update CHANGELOG.md for version 1.6.0 release notes including added features, changes, fixes, and security improvements.
- Enhance README.md to document new boolean attribute helpers and Storybook integration.
- Update bun.lock to reflect dependency upgrades for Storybook and happy-dom.
- Modify definition.md to include new Storybook helpers and component state management features.
- Revise components.md to demonstrate new boolean attribute handling and typed state in components.
- Update getting-started.md with Storybook authoring examples and improved module descriptions.
- Enhance security.md to clarify sanitization practices and introduce trusted fragment handling.
- Revise index.md to highlight new Storybook helpers in the feature list.
- Update llms.txt to reflect the new version 1.6.0.
- Bump version in package.json to 1.6.0 and update devDependencies for Storybook.

* Address review feedback for test setup, test type coverage, and component state keys (#65)

* Initial plan

* Address review feedback for test typing and setup

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Finalize review feedback fixes

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Export component state key type

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Default component state key generic

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* Tighten component/state update semantics and Storybook attribute sanitization (#66)

* Initial plan

* fix review follow-up for state updates and storybook sanitization

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix storybook attribute value scanning

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* fix storybook interpolation boundary parsing

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs align storybook attribute scan jsdoc

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
JosunLP added a commit that referenced this pull request Mar 27, 2026
* Initial plan

* feat: add per-component sanitizer options

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: clarify component sanitizer baseline

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* docs: warn about sensitive component sanitizer opts

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* test: cover sanitizer allowTags overrides

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

* perf: precompute component sanitizer allowlists

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>

---------

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: Jonas Pfalzgraf <info@josunlp.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component Changes to the component module docs Changes to the documentation tests Chenges to the tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants