Skip to content

fix(security): prevent XSS in library search snippets#3

Open
farce1 wants to merge 1 commit intomainfrom
codex/fix-unsanitized-fts-snippets-in-search
Open

fix(security): prevent XSS in library search snippets#3
farce1 wants to merge 1 commit intomainfrom
codex/fix-unsanitized-fts-snippets-in-search

Conversation

@farce1
Copy link
Copy Markdown
Owner

@farce1 farce1 commented Apr 10, 2026

Motivation

  • The library search returned SQLite FTS snippet(...) output containing raw <mark> HTML that was rendered via dangerouslySetInnerHTML, allowing attacker-controlled meeting titles/transcripts to inject executable HTML/JS.
  • The goal was to remove unsafe HTML injection while preserving visible search highlight behavior and minimal surface change to existing UI.

Description

  • Added non-HTML sentinel constants SEARCH_SNIPPET_HIGHLIGHT_START and SEARCH_SNIPPET_HIGHLIGHT_END in src/lib/searchSnippet.ts to be used as FTS highlight markers instead of <mark> tags.
  • Updated the FTS query in src/hooks/useLibrary.ts to request snippets with the sentinel markers by switching the snippet(...) call to snippet(meetings_fts, 1, $2, $3, '...', 32) AS snippet and passing the new sentinel constants as query parameters.
  • Replaced unsafe dangerouslySetInnerHTML usage by removing renderSearchSnippet from src/views/LibraryView.tsx and changing SearchResultRow (src/components/library/SearchResultRow.tsx) to a safe renderer that splits the snippet on the sentinel tokens and constructs text nodes and explicit <mark> React nodes, ensuring any HTML in content is treated as text.
  • Kept the highlighting and visual behavior intact while eliminating injection of raw HTML into the DOM.

Testing

  • Ran a production build with npm run build, which completed successfully.
  • Executed unit tests for the touched library logic with npm run test -- src/hooks/useLibrary.test.ts, and all tests passed (1 file, 5 tests).

Codex Task

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c2fc83db8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +33 to +35
const start = remaining.indexOf(SEARCH_SNIPPET_HIGHLIGHT_START);

if (start === -1) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle literal highlight sentinel collisions

renderSnippet parses any occurrence of SEARCH_SNIPPET_HIGHLIGHT_START/END as markup boundaries, but those tokens now come from normal searchable content too, so a meeting transcript/title that literally contains these strings is mis-rendered. With an unmatched start token, the marker is stripped and text can be dropped (end === -1 path), and with both tokens present, arbitrary content is shown as highlighted even when it was not matched, which makes search results misleading for user-controlled data.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant