Skip to content

fix: improve error handling - #3327

Open
saurabhhhcodes wants to merge 1 commit into
Karanjot786:mainfrom
saurabhhhcodes:fix/termui-44887
Open

fix: improve error handling#3327
saurabhhhcodes wants to merge 1 commit into
Karanjot786:mainfrom
saurabhhhcodes:fix/termui-44887

Conversation

@saurabhhhcodes

@saurabhhhcodes saurabhhhcodes commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes
    • Improved RSS feed handling by more reliably decoding numeric HTML character references.
    • Improved keyboard tab switching in the widget gallery by consistently interpreting numeric key presses.

@github-actions github-actions Bot added area:examples Example apps. type:bug +10 pts. Bug fix. labels Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The RSS reader bounds numeric HTML entity parsing inputs, while the widget gallery explicitly parses tab selector keys in base 10.

Changes

RSS entity decoding

Layer / File(s) Summary
Bound numeric entity parsing
examples/rss-reader/src/index.tsx
decodeEntities parses hexadecimal and decimal numeric references from bounded substrings.

Widget tab selection

Layer / File(s) Summary
Explicit decimal tab parsing
examples/widget-gallery/src/index.ts
handleKey parses numeric tab selector keys with radix 10.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested reviewers: karanjot786

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title suggests error-handling changes, but the PR actually tightens entity parsing and explicit radix handling. Rename it to reflect the real fix, e.g. fix: bound entity parsing in RSS reader or fix: use explicit radix for tab switching.
Description check ⚠️ Warning No PR description was provided, so the required template sections are missing. Add the template sections: Description, Related Issue, Which package(s)?, Type of Change, Checklist, and any reviewer notes.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 48f63a1 and cf2f7ad.

📒 Files selected for processing (2)
  • examples/rss-reader/src/index.tsx
  • examples/widget-gallery/src/index.ts

Comment on lines +30 to +35
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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}`);
  }
}
NODE

Repository: 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}`);
  }
}
NODE

Repository: 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}`);
  }
}
NODE

Repository: 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}`);
  }
}
NODE

Repository: 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}`);
  }
}
NODE

Repository: 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 0x00000x10FFFF, 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.

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

Labels

area:examples Example apps. type:bug +10 pts. Bug fix.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant