fix: improve error handling - #3327
Conversation
📝 WalkthroughWalkthroughThe RSS reader bounds numeric HTML entity parsing inputs, while the widget gallery explicitly parses tab selector keys in base 10. ChangesRSS entity decoding
Widget tab selection
Estimated code review effort: 1 (Trivial) | ~5 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/rss-reader/src/index.tsx`:
- Around line 30-35: Update decodeEntities numeric entity handling to parse the
complete hexadecimal or decimal digit sequence without truncating it, then
accept only values from 0x0000 through 0x10FFFF before calling
String.fromCodePoint. Return the original match for malformed, overlong, or
out-of-range references in both the hexadecimal and decimal branches.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8ab05580-f96f-421a-96ef-bba6db6e28d7
📒 Files selected for processing (2)
examples/rss-reader/src/index.tsxexamples/widget-gallery/src/index.ts
| const codePoint = Number.parseInt(entity.slice(2, 10), 16); | ||
| return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match; | ||
| } | ||
|
|
||
| if (entity.startsWith('#')) { | ||
| const codePoint = Number.parseInt(entity.slice(1), 10); | ||
| const codePoint = Number.parseInt(entity.slice(1, 10), 10); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
node <<'NODE'
for (const value of [0x110000, 1285120]) {
try {
String.fromCodePoint(value);
console.error(`Unexpected success for ${value}`);
process.exitCode = 1;
} catch (error) {
console.log(`Correctly rejected ${value}: ${error.constructor.name}`);
}
}
NODERepository: Karanjot786/TermUI
Length of output: 234
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate file =="
git ls-files | grep -Fx 'examples/rss-reader/src/index.tsx' || true
echo "== relevant source =="
if [ -f examples/rss-reader/src/index.tsx ]; then
nl -ba examples/rss-reader/src/index.tsx | sed -n '1,80p'
fi
echo "== deterministic parser slice behavior =="
node <<'NODE'
function decodeEntitiesCurrent(body) {
return body.replace(/&(#?[0-9a-fA-F]{1,10});?/g, (_match, entity) => {
if (entity.startsWith('`#x`')) {
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : _match;
}
if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1, 10), 10);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : _match;
}
return _match;
});
}
const cases = [
['&`#0000000000000041`;', 'hex long'],
['&`#0000000000007fff`;', 'hex long near max'],
['&`#0000000000010ffff`;', 'unicode max extra digits'],
['&`#00000000000110000`;', 'above unicode max extra digits'],
['&`#x0000000000000041`;', 'dec long'],
['&`#x0000000000007fff`;', 'dec long near max'],
['&`#x0000000000010ffff`;', 'unicode max extra digits dec'],
['&`#x00000000000110000`;', 'above unicode max extra digits dec'],
['&`#1114112`;', '1114112 7 digits exact'],
['&`#x110000`;', 'hex above max 7 digits exact'],
['&`#1285120`;', '1285120 7 digits exact'],
['&`#0000000000000041`;', 'dec long'],
['&`#0000000000000001`;', 'one with many leading zeros'],
];
for (const input of cases.flat()) {
const body = input;
try {
console.log(`${input} -> ${JSON.stringify(decodeEntitiesCurrent(body))}`);
} catch (error) {
console.log(`${input} -> ERROR ${error.constructor.name}: ${error.message}`);
}
}
NODERepository: Karanjot786/TermUI
Length of output: 270
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file exists/size =="
test -f examples/rss-reader/src/index.tsx && wc -l examples/rss-reader/src/index.tsx
echo "== relevant source =="
awk 'NR>=1 && NR<=90 { printf "%6d %s\n", NR, $0 }' examples/rss-reader/src/index.tsx
echo "== deterministic parser slice behavior =="
node <<'NODE'
function decodeEntitiesCurrent(body) {
return body.replace(/&(#?[0-9a-fA-F]{1,10});?/g, (_match, entity) => {
if (entity.startsWith('`#x`')) {
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : _match;
}
if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1, 10), 10);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : _match;
}
return _match;
});
}
const cases = [
['&`#0000000000000041`;', 'dec long 41'],
['&`#x0000000000000041`;', 'hex long 41'],
['&`#x0000000000007fff`;', 'hex long near max'],
['&`#x0000000000010ffff`;', 'unicode max extra digits hex'],
['&`#x00000000000110000`;', 'above unicode max extra digits hex'],
['&`#x110000`;', 'hex above max exact 7 digits'],
['&`#x0110000`;', 'hex above max alternate exact 7 digits'],
['&`#1114112`;', '1114112 7 digits exact'],
['&`#1285120`;', '1285120 7 digits exact'],
['&`#0000000000000001`;', 'one with many leading zeros'],
['&`#9999999999999`;', '9 trillion digits slice'],
];
for (const input of cases) {
const body = input;
try {
console.log(`${input} -> ${JSON.stringify(decodeEntitiesCurrent(body))}`);
} catch (error) {
console.log(`${input} -> ERROR ${error.constructor.name}: ${error.message}`);
}
}
NODERepository: Karanjot786/TermUI
Length of output: 4684
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node <<'NODE'
const decodeEntitiesCurrent = body =>
body.replace(/&(`#x`?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity) => {
if (entity.startsWith('`#x`')) {
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}
if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1, 10), 10);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}
return match;
});
const cases = [
['&`#0000000000000041`;', 'dec long 41'],
['&`#x0000000000000041`;', 'hex long 41'],
['&`#x0000000000007fff`;', 'hex long near max'],
['&`#x0000000000010ffff`;', 'unicode max extra digits hex'],
['&`#x00000000000110000`;', 'above unicode max extra digits hex'],
['&`#x110000`;', 'hex above max exact 7 digits'],
['&`#x0110000`;', 'hex above max alternate 7 digits'],
['&`#1114112`;', '7fff2 7 digits exact'],
['&`#1285120`;', '1285120 7 digits exact'],
['&`#0000000000000001`;', 'one with many leading zeros'],
['&`#9999999999999`;', '9 trillion digits slice'],
['&`#1114111`;', 'valid max extra leading zero'],
['&`#1114112`;', 'explicit invalid max above'],
];
for (const [input, label] of cases) {
let decoded = '<not evaluable>';
let err = null;
try {
decoded = decodeEntitiesCurrent(input);
} catch (error) {
err = { constructor: error.constructor.name, message: String(error.message) };
}
console.log(`${label.padEnd(35)} | ${input.padEnd(28)} => ${decoded}`);
if (err) console.log(`${''.padEnd(35)} | ${''.padEnd(28)} => ERR ${err.constructor}: ${err.message}`);
}
console.log('Range validation behavior:');
for (const value of [0x110000, 1285120, 0x10ffff, 0x110001]) {
try {
const ch = String.fromCodePoint(value);
console.log(`${value} -> ${JSON.stringify(ch)`);
} catch (error) {
console.log(`${value} -> ${error.constructor.name}: ${error.message}`);
}
}
NODERepository: Karanjot786/TermUI
Length of output: 796
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node <<'NODE'
const decodeEntitiesCurrent = body =>
body.replace(/&(`#x`?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity) => {
if (entity.startsWith('`#x`')) {
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}
if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1, 10), 10);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}
return match;
});
const cases = [
['&`#0000000000000041`;', 'dec long 41'],
['&`#x0000000000000041`;', 'hex long 41'],
['&`#x0000000000007fff`;', 'hex long near max'],
['&`#x0000000000010ffff`;', 'unicode max extra digits hex'],
['&`#x00000000000110000`;', 'above unicode max extra digits hex'],
['&`#x110000`;', 'hex above max exact 7 digits'],
['&`#x0110000`;', 'hex above max alternate 7 digits'],
['&`#1114112`;', '7fff2 7 digits exact'],
['&`#1285120`;', '1285120 7 digits exact'],
['&`#0000000000000001`;', 'one with many leading zeros'],
['&`#9999999999999`;', '9 trillion digits slice'],
['&`#1114111`;', 'valid max extra leading zero'],
['&`#1114112`;', 'explicit invalid max above'],
];
for (const [input, label] of cases) {
let decoded = '<not evaluable>';
let err = null;
try {
decoded = decodeEntitiesCurrent(input);
} catch (error) {
err = { constructor: error.constructor.name, message: String(error.message) };
}
console.log(`${label.padEnd(35)} | ${input.padEnd(28)} => ${decoded}`);
if (err) console.log(`${''.padEnd(35)} | ${''.padEnd(28)} => ERR ${err.constructor}: ${err.message}`);
}
console.log('Range validation behavior:');
for (const value of [0x110000, 1285120, 0x10ffff, 0x110001]) {
try {
console.log(`${value} -> ${JSON.stringify(String.fromCodePoint(value))}`);
} catch (error) {
console.log(`${value} -> ${error.constructor.name}: ${error.message}`);
}
}
NODERepository: Karanjot786/TermUI
Length of output: 2050
Reject invalid or overlong numeric entities instead of slicing or validating too late.
decodeEntities can throw for hex/decimal references over Unicode’s valid range, and longer references are silently truncated to the wrong character. Parse the full numeric digits, reject values outside 0x0000–0x10FFFF, and return the original match for invalid references.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/rss-reader/src/index.tsx` around lines 30 - 35, Update
decodeEntities numeric entity handling to parse the complete hexadecimal or
decimal digit sequence without truncating it, then accept only values from
0x0000 through 0x10FFFF before calling String.fromCodePoint. Return the original
match for malformed, overlong, or out-of-range references in both the
hexadecimal and decimal branches.
Summary by CodeRabbit