Repository navigation
fix(english/lightnovelto): sanitize chapter HTML and handle protocol-relative URLs in resolveUrl - #2727
fix(english/lightnovelto): sanitize chapter HTML and handle protocol-relative URLs in resolveUrl#2727RevDev909 wants to merge 3 commits into
Conversation
…relative URLs in resolveUrl
[Medium impact] The changes affect chapter reading and URL resolution for LightNovel.to. Fix the executable HTML and URL bypasses before merging. Findings
SummaryAdds chapter HTML filtering, supports protocol-relative URLs in
Reviews (1) · Last reviewed commit: "fix(english/lightnovelto): sanitize chap..." · Reviewed by Greptile |
| .replace(/\son\w+\s*=\s*"[^"]*"/gi, '') | ||
| .replace(/\son\w+\s*=\s*'[^']*'/gi, '') | ||
| .replace(/\son\w+\s*=\s*[^\s>]+/gi, '') |
There was a problem hiding this comment.
Slash-separated handlers still run
The event filters require whitespace before the attribute name, so <img src="x"/onerror=alert(1)> survives unchanged. Browsers read the slash-separated onerror as an attribute and run it when the image fails to load. Parse tags and remove event attributes instead of matching whitespace-prefixed text.
How this was verified: The handler survives every replacement and reaches dangerouslySetInnerHTML through parseChapter.
Knowledge Base Used: Plugin authoring contract
| .replace(/&/g, '&') | ||
| .replace(/</g, '<') | ||
| .replace(/>/g, '>') | ||
| .replace(/"/g, '"') | ||
| .replace(/'/g, "'"); |
There was a problem hiding this comment.
decodeEntities misses browser-supported references such as : and numeric references without a semicolon. For example, <a href="javascript:alert(1)">open</a> passes the check, but the browser reads its destination as javascript:alert(1). Clicking it can run JavaScript. Use a complete HTML attribute decoder before checking the scheme.
How this was verified: The decoder leaves : unchanged, the scheme check accepts it, and the returned link reaches raw HTML rendering.
Knowledge Base Used: Plugin authoring contract
| const quoted = /"([^"]*)"|'([^']*)'/.exec(attr); | ||
| if (quoted) { | ||
| raw = quoted[1] !== undefined ? quoted[1] : quoted[2]; | ||
| } else { | ||
| const unquoted = /=\s*([^\s>]+)/.exec(attr); |
There was a problem hiding this comment.
Quotes hide script destinations
isSafeUrlValue treats any quoted substring as the whole attribute value. For <a href=javascript:alert('x')>open</a>, it checks only x and keeps the link. Browsers retain those quotes inside the unquoted value, so clicking the link can run JavaScript. Read the value from immediately after = and distinguish quoted values from quotes inside an unquoted value.
How this was verified: The attribute matcher captures the full unquoted destination, but isSafeUrlValue selects only 'x' and returns true.
Knowledge Base Used: Plugin authoring contract
| .replace(/\son\w+\s*=\s*[^\s>]+/gi, '') | ||
| .replace(/\sstyle\s*=\s*"[^"]*"/gi, '') | ||
| .replace(/\sstyle\s*=\s*'[^']*'/gi, '') |
There was a problem hiding this comment.
Ordinary chapter text disappears
These replacements run over story text as well as tag attributes. For example, <p>Set online=true before continuing.</p> becomes <p>Set before continuing.</p> because online matches on\w+. The style replacements have the same problem. Limit attribute removal to parsed tags so chapter text stays intact.
Knowledge Base Used: Plugin authoring contract
…arated handlers, encoded schemes, and quote-hidden destinations
…arated handlers, encoded schemes, and quote-hidden destinations
Chapter HTML is now sanitized before it reaches the reader: scripts, frames, form elements, inline event handlers, and javascript:/vbscript:/data:text/html URLs are stripped. URL values are checked regardless of quoting (double, single, or unquoted) and after decoding HTML entities, so single-quoted, unquoted, and entity-encoded javascript: links are caught too, along with mixed-case and control-character-obfuscated schemes, while story text, links, and images are kept. resolveUrl also handles protocol-relative URLs now. Version bumped to 1.0.1. Tested: eslint and prettier clean, check:plugin all four checks pass.
Checklist
type(scope): description(e.g.feat(<generator>): add new source)