fix: resolve 4 bugs in termui - #3582
Conversation
📝 WalkthroughWalkthroughThe changes correct hexadecimal entity length handling, numeric sorting of selected options, and NaN validation for prompt choices. ChangesRSS entity parsing
MultiSelect ordering
Prompt choice validation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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-31: Update the entity decoding logic around codePoint to parse
the complete hexadecimal payload rather than truncating it with entity.slice(2,
10). Reject payloads exceeding the supported Unicode hexadecimal length or
values outside the valid code point range before calling String.fromCodePoint,
and return match for invalid entities while preserving valid decoding.
🪄 Autofix
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: d59f8d2f-dd99-4cb6-8e93-eafd78987eb8
📒 Files selected for processing (3)
examples/rss-reader/src/index.tsxpackages/ui/src/MultiSelect.tspackages/ui/src/prompts.ts
| const codePoint = Number.parseInt(entity.slice(2, 10), 16); | ||
| return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file outline =="
ast-grep outline examples/rss-reader/src/index.tsx --view compact || true
echo "== relevant decodeEntities code =="
rg -n "decodeEntities|String.fromCodePoint|Number.parseInt|number;hex" examples/rss-reader/src/index.tsx -C 8
echo "== read file 1-80 =="
sed -n '1,80p' examples/rss-reader/src/index.tsx
echo "== deterministic JS probe =="
node - <<'JS'
const samples = [
['&`#x00001F600`;', 8],
['&`#xFFF`;', 3],
['&`#xFFFFFFFF`;', 8],
['&`#x110000`;', 5],
['&`#x999999999`;', 9],
];
for (const [input, sliceParam] of samples) {
const entity = input;
const truncatedCodePoint = Number.parseInt(entity.slice(2, 10), 16);
const fullHex = entity.slice(2);
const fullCodePoint = Number.parseInt(fullHex, 16);
let out = null;
let err = null;
try {
const result = Number.isFinite(truncatedCodePoint) ? String.fromCodePoint(truncatedCodePoint) : input;
out = result;
} catch (e) {
err = e.toString();
}
console.log(JSON.stringify({ input, sliceParam, truncated: truncatedCodePoint, fullHex, fullCodePoint, out, err }));
}
JSRepository: Karanjot786/TermUI
Length of output: 5281
🌐 Web query:
MDN String.fromCodePoint RangeError values outside the Unicode code point range 0x0000 to 0x10FFFF
💡 Result:
The JavaScript static method String.fromCodePoint throws a RangeError if any provided argument is not a valid Unicode code point [1][2]. According to the ECMAScript specification and MDN documentation, a valid code point must be an integral number within the inclusive range of 0x0000 to 0x10FFFF [1][3][2]. A RangeError is triggered if the provided argument: - Is a non-integer (e.g., 3.14) [1][2] - Is less than 0 [1][2] - Is greater than 0x10FFFF (1114111) [1][2] - Is NaN [1][4] Values outside the 0x0000 to 0x10FFFF range are considered invalid because they fall outside the defined Unicode codespace [1][2]. While UTF-16 encoding, which JavaScript uses for strings, represents higher code points using surrogate pairs, the method itself strictly validates that the input corresponds to a valid, representable Unicode code point [2].
Citations:
- 1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_valid_code_point
- 2: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
- 3: https://tc39.es/ecma262/2026/multipage/text-processing.html
- 4: https://typeerror.org/docs/javascript/global_objects/string/fromcodepoint
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node - <<'JS'
function decodeEntities(value) {
const entities = {
amp: '&',
lt: '<',
gt: '>',
quot: '"',
apos: "'",
nbsp: ' ',
};
return value.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);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}
return entities[entity.toLowerCase()] ?? match;
});
}
const samples = [
'&`#x00001F600`;',
'&`#xFFF`;',
'&`#xFFFFFFFF`;',
'&`#x110000`;',
'&`#x999999999`;',
'&`#99`;',
];
for (const input of samples) {
let result;
try {
result = decodeEntities(input);
} catch (e) {
console.log(JSON.stringify({ input, result: null, error: e.name + ': ' + e.message }));
continue;
}
console.log(JSON.stringify({ input, result }));
}
JSRepository: Karanjot786/TermUI
Length of output: 528
Denial of Service (CWE-20): Improper Input Validation
Exploitability: Theoretical
Reject oversized hexadecimal entities instead of truncating them.
entity.slice(2, 10) keeps only the first eight digits, so 😀 decodes to U+1F60 instead of completing the entity. Values such as � pass Number.isFinite, then String.fromCodePoint throws RangeError. Validate the full payload length and the Unicode range before decoding.
🤖 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 - 31, Update the entity
decoding logic around codePoint to parse the complete hexadecimal payload rather
than truncating it with entity.slice(2, 10). Reject payloads exceeding the
supported Unicode hexadecimal length or values outside the valid code point
range before calling String.fromCodePoint, and return match for invalid entities
while preserving valid decoding.
Description
This PR fixes real bugs found in the codebase:
parseInt: without10, strings like'0x1F'or'08'parse in unintended bases.x === trueis equivalent tox(andx === falseto!x), and shorter to read.isNaNwithNumber.isNaN: the global version coerces its argument, soisNaN('1')returns false whileNumber.isNaNis strict..sort()coerces elements to strings, so[10, 9, 2]sorts as[10, 2, 9]; numeric comparator sorts correctly.Type of Change
How Has This Been Tested?
Checklist
Related Issue
Ref: #3581
Summary by CodeRabbit