fix(frontend): use shared quote-safe escapeHtml in apikeys and users#1214
fix(frontend): use shared quote-safe escapeHtml in apikeys and users#1214cristim wants to merge 1 commit into
Conversation
|
Warning Review limit reached
More reviews will be available in 26 minutes and 32 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughConsolidates divergent ChangesHTML escaping consolidation and attribute-injection protection
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
4802828 to
862e3b2
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
escapeHtml was implemented three times with diverging security semantics: the shared frontend/src/utils.ts copy escapes &, <, >, " and ', while the local copies in apikeys.ts and users/utils.ts used a DOM round-trip (div.textContent -> div.innerHTML) that leaves both quote characters unescaped. Those weak copies were interpolated inside double-quoted attributes (data-key-id in apikeys.ts, aria-label and option value across users/userList.ts, filters.ts and permissionMatrix.ts), so a value containing a double quote could break out of the attribute and inject markup. Delete both local copies: apikeys.ts now imports escapeHtml from ./utils and users/utils.ts re-exports it from ../utils (same pattern already used there for formatRelativeTime/formatDate), so all users/ modules pick up the quote-safe implementation unchanged. Update the users/utils tests that previously asserted the unescaped quote behavior, and add regression tests replicating the real scenarios: a key id containing a double quote rendered through renderApiKeysList, and a quoted value in attribute position through the users escapeHtml. Both fail against the pre-fix code. Closes #1195
862e3b2 to
53d335b
Compare
Adversarial-review passRan the playbook across the OWASP-5 escape contract, the XSS sinks the PR touches, the other frontend modules that interpolate API data into innerHTML (i.e. the regression surface the PR could have missed), and the UNSTABLE CI checks. Confirmed clean
In-scope fix pushed (
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Problem
Closes #1195 (review finding ARCH-07, P2).
escapeHtmlwas implemented three times in the frontend with diverging security semantics:frontend/src/utils.tsescapes&,<,>,"and'(quote-safe).frontend/src/apikeys.tsandfrontend/src/users/utils.tseach re-implemented it via a DOM round-trip (div.textContent = text; return div.innerHTML;), which escapes only&,<,>and leaves both quote characters intact.The weak copies were interpolated inside double-quoted attributes:
data-key-id="${escapeHtml(key.id)}"in apikeys.ts, and aria-label / option value positions acrossusers/userList.ts,users/filters.tsandusers/permissionMatrix.ts. A value containing a double quote could break out of the attribute and inject markup (latent attribute-injection trap; exploitability today is low since the values are mostly server-generated IDs).Fix
Per the report recommendation, delete both local copies and route everything through the shared quote-safe implementation:
apikeys.ts: localescapeHtmlremoved; importsescapeHtmlfrom./utils.users/utils.ts: localescapeHtmlreplaced with a re-export from../utils(same pattern already used there forformatRelativeTime/formatDate), so allusers/modules pick up the safe implementation with no import churn.Tests that previously asserted the unescaped-quote behavior were updated to assert the quote-safe output, and the
11-M2filters test now asserts the real invariant (no injected element; option value round-trips the raw id) instead of a side effect of the old truncation.Regression tests
Replicating the real failing scenarios:
apikeys.test.ts: a key id ofkey-1" onmouseover="alert(1)rendered throughrenderApiKeysListmust not inject anonmouseoverattribute, anddata-key-idmust round-trip the raw id.users.test.ts: a quoted value interpolated in attribute position through the usersescapeHtmlcannot break out of the attribute; plus direct quote-escaping assertions.Verified fail-before / pass-after: with the source fix stashed, 7 tests fail (the new regression tests plus the updated quote assertions); with the fix applied, all pass.
Test evidence
npx tsc --noEmit: no errorsnpx jest: 2555 passed, 0 failed, 1 skippednpm run build: succeedsnpm run lintis broken onmainas well (no ESLint config file in the repo), unrelated to this changeSummary by CodeRabbit
Release Notes
Bug Fixes
Tests