shelving 1.267.3 → 1.267.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/markup/MarkupParser.d.ts +27 -1
- package/markup/MarkupParser.js +31 -1
- package/markup/rule/blockquote.d.ts +1 -0
- package/markup/rule/blockquote.js +11 -1
- package/markup/rule/code.js +3 -1
- package/markup/rule/inline.js +4 -1
- package/markup/rule/link.js +3 -1
- package/markup/rule/separator.js +3 -1
- package/markup/rule/table.js +1 -1
- package/markup/util/regexp.d.ts +4 -4
- package/markup/util/regexp.js +3 -3
- package/package.json +1 -1
- package/ui/misc/Markup.d.ts +1 -0
- package/ui/misc/Markup.js +1 -0
- package/ui/misc/Markup.md +1 -0
- package/ui/misc/Markup.tsx +1 -0
- package/ui/tree/TreeMarkup.d.ts +1 -0
- package/ui/tree/TreeMarkup.js +1 -0
- package/ui/tree/TreeMarkup.md +1 -1
- package/ui/tree/TreeMarkup.tsx +1 -0
package/markup/MarkupParser.d.ts
CHANGED
|
@@ -42,6 +42,13 @@ export type MarkupOptions = {
|
|
|
42
42
|
* Default context to use if one isn't set. Defaults to `"block"`
|
|
43
43
|
*/
|
|
44
44
|
readonly context?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Current nesting depth — used to cap self-recursive rules (e.g. blockquotes) so pathological input can't
|
|
47
|
+
* overflow the stack. Internal: set automatically as rules recurse via `MarkupParser.nested()`; callers
|
|
48
|
+
* normally leave it at the default `0`.
|
|
49
|
+
* @default 0
|
|
50
|
+
*/
|
|
51
|
+
readonly depth?: number | undefined;
|
|
45
52
|
};
|
|
46
53
|
/**
|
|
47
54
|
* Parses a Markdownish markup string and renders it as a React node using a tiered, masking rule engine.
|
|
@@ -90,10 +97,29 @@ export declare class MarkupParser implements Parser<string, ReactNode> {
|
|
|
90
97
|
* @see https://shelving.cc/markup/MarkupParser/context
|
|
91
98
|
*/
|
|
92
99
|
readonly context: string;
|
|
93
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Current nesting depth, incremented by `nested()` as self-recursive rules descend. Used to cap recursion so
|
|
102
|
+
* pathological input (e.g. a long `>>>>…` blockquote) can't overflow the stack.
|
|
103
|
+
* @see https://shelving.cc/markup/MarkupParser/depth
|
|
104
|
+
*/
|
|
105
|
+
readonly depth: number;
|
|
106
|
+
constructor({ rules, rel, url, root, schemes, context, depth }?: MarkupOptions);
|
|
107
|
+
/**
|
|
108
|
+
* Return a copy of this parser with `depth` incremented by one, sharing all other options.
|
|
109
|
+
* - Self-recursive rules (e.g. blockquotes) parse their nested content through `nested()` so `depth` tracks how
|
|
110
|
+
* deep the recursion has gone, letting them stop before the call stack overflows.
|
|
111
|
+
*
|
|
112
|
+
* @returns A new `MarkupParser` identical to this one but one level deeper.
|
|
113
|
+
* @see https://shelving.cc/markup/MarkupParser/nested
|
|
114
|
+
*/
|
|
115
|
+
nested(): MarkupParser;
|
|
94
116
|
/**
|
|
95
117
|
* Parse a text string as Markdownish markup syntax and render it as a React node.
|
|
96
118
|
* - Syntax is not defined by this code, but by the rules supplied to it.
|
|
119
|
+
* - **Untrusted input:** parsing is linear for normal content, but a few rules can still degrade on adversarial
|
|
120
|
+
* input — long unbroken runs of backtick code fences, and deeply-nested `>` blockquotes (each leading `>`
|
|
121
|
+
* recurses one level, so a pathological line can overflow the stack). When `input` is user-generated, cap its
|
|
122
|
+
* length first; a sane maximum for your use case (for typical user content, tens of kilobytes) bounds worst-case work.
|
|
97
123
|
*
|
|
98
124
|
* @param input The string content possibly containing markup syntax, e.g. `"This is a *bold* string."`.
|
|
99
125
|
* @param context The context to render in (defaults to this parser's `context`, i.e. `"block"` unless overridden).
|
package/markup/MarkupParser.js
CHANGED
|
@@ -48,7 +48,13 @@ export class MarkupParser {
|
|
|
48
48
|
* @see https://shelving.cc/markup/MarkupParser/context
|
|
49
49
|
*/
|
|
50
50
|
context;
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Current nesting depth, incremented by `nested()` as self-recursive rules descend. Used to cap recursion so
|
|
53
|
+
* pathological input (e.g. a long `>>>>…` blockquote) can't overflow the stack.
|
|
54
|
+
* @see https://shelving.cc/markup/MarkupParser/depth
|
|
55
|
+
*/
|
|
56
|
+
depth;
|
|
57
|
+
constructor({ rules = MARKUP_RULES, rel, url, root, schemes = HTTP_SCHEMES, context = "block", depth = 0 } = {}) {
|
|
52
58
|
this.rules = rules;
|
|
53
59
|
this.priorities = _getPriorities(rules);
|
|
54
60
|
this.rel = rel;
|
|
@@ -56,10 +62,34 @@ export class MarkupParser {
|
|
|
56
62
|
this.root = root;
|
|
57
63
|
this.schemes = schemes;
|
|
58
64
|
this.context = context;
|
|
65
|
+
this.depth = depth;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Return a copy of this parser with `depth` incremented by one, sharing all other options.
|
|
69
|
+
* - Self-recursive rules (e.g. blockquotes) parse their nested content through `nested()` so `depth` tracks how
|
|
70
|
+
* deep the recursion has gone, letting them stop before the call stack overflows.
|
|
71
|
+
*
|
|
72
|
+
* @returns A new `MarkupParser` identical to this one but one level deeper.
|
|
73
|
+
* @see https://shelving.cc/markup/MarkupParser/nested
|
|
74
|
+
*/
|
|
75
|
+
nested() {
|
|
76
|
+
return new MarkupParser({
|
|
77
|
+
rules: this.rules,
|
|
78
|
+
rel: this.rel,
|
|
79
|
+
url: this.url,
|
|
80
|
+
root: this.root,
|
|
81
|
+
schemes: this.schemes,
|
|
82
|
+
context: this.context,
|
|
83
|
+
depth: this.depth + 1,
|
|
84
|
+
});
|
|
59
85
|
}
|
|
60
86
|
/**
|
|
61
87
|
* Parse a text string as Markdownish markup syntax and render it as a React node.
|
|
62
88
|
* - Syntax is not defined by this code, but by the rules supplied to it.
|
|
89
|
+
* - **Untrusted input:** parsing is linear for normal content, but a few rules can still degrade on adversarial
|
|
90
|
+
* input — long unbroken runs of backtick code fences, and deeply-nested `>` blockquotes (each leading `>`
|
|
91
|
+
* recurses one level, so a pathological line can overflow the stack). When `input` is user-generated, cap its
|
|
92
|
+
* length first; a sane maximum for your use case (for typical user content, tens of kilobytes) bounds worst-case work.
|
|
63
93
|
*
|
|
64
94
|
* @param input The string content possibly containing markup syntax, e.g. `"This is a *bold* string."`.
|
|
65
95
|
* @param context The context to render in (defaults to this parser's `context`, i.e. `"block"` unless overridden).
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* - `>` quote character followed by zero or more spaces.
|
|
4
4
|
* - No spaces can appear before the `>` quote character.
|
|
5
5
|
* - Quote block is only broken by `\n\n` two newline characters.
|
|
6
|
+
* - Nesting is capped at `MAX_DEPTH` (10) levels; deeper `>` prefixes are rendered as plain text rather than recursed, so pathological input can't overflow the stack.
|
|
6
7
|
*
|
|
7
8
|
* @example new MarkupParser({ rules: [BLOCKQUOTE_RULE] }).parse("> Quoted text")
|
|
8
9
|
* @see https://shelving.cc/markup/BLOCKQUOTE_RULE
|
|
@@ -3,13 +3,23 @@ import { createMarkupRule } from "../MarkupRule.js";
|
|
|
3
3
|
import { BLOCK_CONTENT_REGEXP, createBlockRegExp } from "../util/regexp.js";
|
|
4
4
|
const PREFIX = ">";
|
|
5
5
|
const INDENT = new RegExp(`^${PREFIX}`, "gm");
|
|
6
|
+
/** Maximum blockquote nesting depth. Each `>` level recurses once, so an unbounded `>>>>…` line would overflow the stack; beyond this the remaining source is left as plain text. */
|
|
7
|
+
const MAX_DEPTH = 10;
|
|
6
8
|
/**
|
|
7
9
|
* Blockquote block.
|
|
8
10
|
* - `>` quote character followed by zero or more spaces.
|
|
9
11
|
* - No spaces can appear before the `>` quote character.
|
|
10
12
|
* - Quote block is only broken by `\n\n` two newline characters.
|
|
13
|
+
* - Nesting is capped at `MAX_DEPTH` (10) levels; deeper `>` prefixes are rendered as plain text rather than recursed, so pathological input can't overflow the stack.
|
|
11
14
|
*
|
|
12
15
|
* @example new MarkupParser({ rules: [BLOCKQUOTE_RULE] }).parse("> Quoted text")
|
|
13
16
|
* @see https://shelving.cc/markup/BLOCKQUOTE_RULE
|
|
14
17
|
*/
|
|
15
|
-
export const BLOCKQUOTE_RULE = createMarkupRule(createBlockRegExp(`(?<quote>${PREFIX}${BLOCK_CONTENT_REGEXP})`), (key, { quote }, parser) =>
|
|
18
|
+
export const BLOCKQUOTE_RULE = createMarkupRule(createBlockRegExp(`(?<quote>${PREFIX}${BLOCK_CONTENT_REGEXP})`), (key, { quote }, parser) => {
|
|
19
|
+
const inner = quote.replace(INDENT, "");
|
|
20
|
+
// This blockquote is level `depth + 1`; once it reaches the cap, render the (still `>`-prefixed) remainder as
|
|
21
|
+
// plain text instead of recursing, so nesting stops at exactly MAX_DEPTH levels and can't overflow the stack.
|
|
22
|
+
if (parser.depth + 1 >= MAX_DEPTH)
|
|
23
|
+
return _jsx("blockquote", { children: inner }, key);
|
|
24
|
+
return _jsx("blockquote", { children: parser.nested().parse(inner, "block") }, key);
|
|
25
|
+
}, ["block", "list"]);
|
package/markup/rule/code.js
CHANGED
|
@@ -15,4 +15,6 @@ import { BLOCK_CONTENT_REGEXP } from "../util/regexp.js";
|
|
|
15
15
|
*/
|
|
16
16
|
// Priority 10: code is a higher-precedence tier, resolved and masked before links/emphasis, so a
|
|
17
17
|
// code span that straddles a link delimiter wins and the link cannot form across it.
|
|
18
|
-
export const CODE_RULE = createMarkupRule(
|
|
18
|
+
export const CODE_RULE = createMarkupRule(
|
|
19
|
+
// Fence length is bounded (`{1,64}`) so a long run of backticks can't force O(n²) backtracking against the closing backreference.
|
|
20
|
+
getRegExp(`(?<fence>\`{1,64})(?<code>${BLOCK_CONTENT_REGEXP})\\k<fence>`), (key, { code }) => _jsx("code", { children: code }, key), ["inline", "list", "link"], 10);
|
package/markup/rule/inline.js
CHANGED
|
@@ -19,7 +19,10 @@ const INLINE_CHARS = { "~": "del", "+": "ins", "*": "strong", _: "em", "=": "mar
|
|
|
19
19
|
* @example new MarkupParser({ rules: [INLINE_RULE] }).parse("This is *bold* and _italic_")
|
|
20
20
|
* @see https://shelving.cc/markup/INLINE_RULE
|
|
21
21
|
*/
|
|
22
|
-
export const INLINE_RULE = createMarkupRule(createWordRegExp(
|
|
22
|
+
export const INLINE_RULE = createMarkupRule(createWordRegExp(
|
|
23
|
+
// Marker run is bounded (`{1,8}`) and the inner span is bounded (`{0,2000}?`) so a long run of
|
|
24
|
+
// unclosed markers can't force O(n²) backtracking — real emphasis never approaches these limits.
|
|
25
|
+
`(?<wrap>(?<char>[${Object.keys(INLINE_CHARS).join("")}]){1,8})(?<text>(?!\\k<char>)\\S(?:[\\s\\S]{0,2000}?(?!\\k<char>)\\S)?)\\k<wrap>`), (key, { char, text }, parser) => {
|
|
23
26
|
const Inline = INLINE_CHARS[char];
|
|
24
27
|
return _jsx(Inline, { children: parser.parse(text, "inline") }, key);
|
|
25
28
|
}, ["inline", "list", "link"]);
|
package/markup/rule/link.js
CHANGED
|
@@ -30,5 +30,7 @@ _renderLink, ["inline", "list"]);
|
|
|
30
30
|
* @example new MarkupParser({ rules: [AUTOLINK_RULE] }).parse("http://google.com/maps (Google Maps)")
|
|
31
31
|
* @see https://shelving.cc/markup/AUTOLINK_RULE
|
|
32
32
|
*/
|
|
33
|
-
export const AUTOLINK_RULE = createMarkupRule(
|
|
33
|
+
export const AUTOLINK_RULE = createMarkupRule(
|
|
34
|
+
// Scheme length is bounded (`{3,64}?`) so a long run of letters with no `:` can't force O(n²) backtracking.
|
|
35
|
+
getRegExp(/(?<href>[a-z]{3,64}?:\S+)(?: +(?:\((?<title>[^)\n]*?)\)))?/), //
|
|
34
36
|
_renderLink, ["inline", "list"]);
|
package/markup/rule/separator.js
CHANGED
|
@@ -11,6 +11,8 @@ import { createLineRegExp } from "../util/regexp.js";
|
|
|
11
11
|
* @example new MarkupParser({ rules: [SEPARATOR_RULE] }).parse("---")
|
|
12
12
|
* @see https://shelving.cc/markup/SEPARATOR_RULE
|
|
13
13
|
*/
|
|
14
|
-
export const SEPARATOR_RULE = createMarkupRule(
|
|
14
|
+
export const SEPARATOR_RULE = createMarkupRule(
|
|
15
|
+
// Inter-character whitespace is bounded (`{0,2000}`) so a long run of spaces can't force O(n²) backtracking.
|
|
16
|
+
createLineRegExp("([-*•+_=])(?: {0,2000}\\1){2,}"), //
|
|
15
17
|
//
|
|
16
18
|
key => _jsx("hr", {}, key), ["block"]);
|
package/markup/rule/table.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createMarkupRule } from "../MarkupRule.js";
|
|
3
3
|
import { createBlockRegExp, LINE_SPACE_REGEXP } from "../util/regexp.js";
|
|
4
4
|
// Constants.
|
|
5
|
-
const _SPACE = `${LINE_SPACE_REGEXP}
|
|
5
|
+
const _SPACE = `${LINE_SPACE_REGEXP}{0,2000}`; // Run of line whitespace (never crosses a newline); bounded so a long whitespace line can't force O(n²) backtracking in the delimiter row.
|
|
6
6
|
const _CELL = `${_SPACE}:?-+:?${_SPACE}`; // Delimiter-row cell: one or more dashes with optional `:` alignment markers.
|
|
7
7
|
const _DELIMITER_SOURCE = `${_SPACE}\\|?(?:${_CELL}\\|)+(?:${_CELL})?${_SPACE}`; // Delimiter row: pipe-separated dash cells.
|
|
8
8
|
const _DELIMITER = new RegExp(`^${_DELIMITER_SOURCE}$`, "u"); // Tests whether a single line is a delimiter row.
|
package/markup/util/regexp.d.ts
CHANGED
|
@@ -13,12 +13,12 @@ export declare const BLOCK_SPACE_REGEXP = "\\s";
|
|
|
13
13
|
* Regular expression source matching the start of a block — the start of the string, or one linebreak.
|
|
14
14
|
* @see https://shelving.cc/markup/BLOCK_START_REGEXP
|
|
15
15
|
*/
|
|
16
|
-
export declare const BLOCK_START_REGEXP = "(?:\\s
|
|
16
|
+
export declare const BLOCK_START_REGEXP = "(?:\\s{0,2000}\\n|^)";
|
|
17
17
|
/**
|
|
18
18
|
* Regular expression source matching the end of a block — the end of the string, or two linebreaks, with trailing whitespace trimmed.
|
|
19
19
|
* @see https://shelving.cc/markup/BLOCK_END_REGEXP
|
|
20
20
|
*/
|
|
21
|
-
export declare const BLOCK_END_REGEXP = "\\s
|
|
21
|
+
export declare const BLOCK_END_REGEXP = "\\s{0,2000}(?:$|\\n\\s{0,2000}\\n)";
|
|
22
22
|
/**
|
|
23
23
|
* Regular expression source matching line content — the shortest run of any character except newline.
|
|
24
24
|
* @see https://shelving.cc/markup/LINE_CONTENT_REGEXP
|
|
@@ -33,12 +33,12 @@ export declare const LINE_SPACE_REGEXP = "[^\\n\\S]";
|
|
|
33
33
|
* Regular expression source matching the start of a line — the start of the string, or one linebreak.
|
|
34
34
|
* @see https://shelving.cc/markup/LINE_START_REGEXP
|
|
35
35
|
*/
|
|
36
|
-
export declare const LINE_START_REGEXP = "(?:\\s
|
|
36
|
+
export declare const LINE_START_REGEXP = "(?:\\s{0,2000}\\n|^)";
|
|
37
37
|
/**
|
|
38
38
|
* Regular expression source matching the end of a line — the end of the string, or one linebreak, with trailing whitespace trimmed.
|
|
39
39
|
* @see https://shelving.cc/markup/LINE_END_REGEXP
|
|
40
40
|
*/
|
|
41
|
-
export declare const LINE_END_REGEXP = "[^\\n\\S]
|
|
41
|
+
export declare const LINE_END_REGEXP = "[^\\n\\S]{0,2000}(?:\\s{0,2000}\\n|$)";
|
|
42
42
|
/**
|
|
43
43
|
* Regular expression source matching word content — at least one letter or number character.
|
|
44
44
|
* @see https://shelving.cc/markup/WORD_CONTENT_REGEXP
|
package/markup/util/regexp.js
CHANGED
|
@@ -13,12 +13,12 @@ export const BLOCK_SPACE_REGEXP = "\\s"; // Block whitespace (run of any whitesp
|
|
|
13
13
|
* Regular expression source matching the start of a block — the start of the string, or one linebreak.
|
|
14
14
|
* @see https://shelving.cc/markup/BLOCK_START_REGEXP
|
|
15
15
|
*/
|
|
16
|
-
export const BLOCK_START_REGEXP = "(?:\\s
|
|
16
|
+
export const BLOCK_START_REGEXP = "(?:\\s{0,2000}\\n|^)"; // Start of block (start of string, or one linebreak). Whitespace is bounded so a long whitespace run can't force O(n²) backtracking at every offset.
|
|
17
17
|
/**
|
|
18
18
|
* Regular expression source matching the end of a block — the end of the string, or two linebreaks, with trailing whitespace trimmed.
|
|
19
19
|
* @see https://shelving.cc/markup/BLOCK_END_REGEXP
|
|
20
20
|
*/
|
|
21
|
-
export const BLOCK_END_REGEXP = "\\s
|
|
21
|
+
export const BLOCK_END_REGEXP = "\\s{0,2000}(?:$|\\n\\s{0,2000}\\n)"; // End of block (end of string, or two linebreaks, trimmed whitespace). Whitespace runs bounded to avoid O(n²) backtracking.
|
|
22
22
|
/**
|
|
23
23
|
* Regular expression source matching line content — the shortest run of any character except newline.
|
|
24
24
|
* @see https://shelving.cc/markup/LINE_CONTENT_REGEXP
|
|
@@ -38,7 +38,7 @@ export const LINE_START_REGEXP = BLOCK_START_REGEXP; // Start of line (start of
|
|
|
38
38
|
* Regular expression source matching the end of a line — the end of the string, or one linebreak, with trailing whitespace trimmed.
|
|
39
39
|
* @see https://shelving.cc/markup/LINE_END_REGEXP
|
|
40
40
|
*/
|
|
41
|
-
export const LINE_END_REGEXP = `${LINE_SPACE_REGEXP}
|
|
41
|
+
export const LINE_END_REGEXP = `${LINE_SPACE_REGEXP}{0,2000}(?:\\s{0,2000}\\n|$)`; // End of line (end of string, or one linebreak, trimmed whitespace). Whitespace runs bounded to avoid O(n²) backtracking.
|
|
42
42
|
/**
|
|
43
43
|
* Regular expression source matching word content — at least one letter or number character.
|
|
44
44
|
* @see https://shelving.cc/markup/WORD_CONTENT_REGEXP
|
package/package.json
CHANGED
package/ui/misc/Markup.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface MarkupProps extends Partial<MarkupOptions> {
|
|
|
14
14
|
* - Defaults to `MARKUP_OPTIONS` (full block + inline rule set). Pass `rules`, `rel`, `url`, `root`, or `schemes` as props to override.
|
|
15
15
|
* - `url`/`root` default to the current `<Meta>` context so link rules resolve site-absolute and relative hrefs.
|
|
16
16
|
* - Renders inside whatever ancestor element the caller provides — wrap in `<Prose>` to get the standard prose typography for the produced `<p>` / `<ul>` / `<pre>` / etc.
|
|
17
|
+
* - **Untrusted input:** when `children` comes from users, cap its length before rendering — parsing is linear for normal content but a few rules can degrade on adversarial input (a sane maximum for your use case, typically tens of kilobytes, bounds worst-case work). See `MarkupParser.parse()`.
|
|
17
18
|
*
|
|
18
19
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
19
20
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|
package/ui/misc/Markup.js
CHANGED
|
@@ -5,6 +5,7 @@ import { requireMeta } from "./MetaContext.js";
|
|
|
5
5
|
* - Defaults to `MARKUP_OPTIONS` (full block + inline rule set). Pass `rules`, `rel`, `url`, `root`, or `schemes` as props to override.
|
|
6
6
|
* - `url`/`root` default to the current `<Meta>` context so link rules resolve site-absolute and relative hrefs.
|
|
7
7
|
* - Renders inside whatever ancestor element the caller provides — wrap in `<Prose>` to get the standard prose typography for the produced `<p>` / `<ul>` / `<pre>` / etc.
|
|
8
|
+
* - **Untrusted input:** when `children` comes from users, cap its length before rendering — parsing is linear for normal content but a few rules can degrade on adversarial input (a sane maximum for your use case, typically tens of kilobytes, bounds worst-case work). See `MarkupParser.parse()`.
|
|
8
9
|
*
|
|
9
10
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
10
11
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|
package/ui/misc/Markup.md
CHANGED
|
@@ -7,6 +7,7 @@ Parses a markup string and renders the resulting React nodes inline. Defaults to
|
|
|
7
7
|
- `url` and `root` default to the current `<Meta>` context, so link rules resolve site-absolute and relative hrefs correctly. Override any `MarkupOptions` prop (`rules`, `rel`, `url`, `root`, `schemes`) directly on `<Markup>` for a custom rule set or base URL.
|
|
8
8
|
- Renders `null` when `children` is empty.
|
|
9
9
|
- Wrap in `<Prose>` to give the produced `<p>` / `<ul>` / `<pre>` / etc. the standard prose typography.
|
|
10
|
+
- When `children` comes from users, cap its length before rendering: parsing is linear for normal content, but a few rules can degrade on adversarial input (long backtick runs, deeply-nested `>` blockquotes). A sane maximum for your use case — tens of kilobytes for typical user content — keeps worst-case work bounded.
|
|
10
11
|
|
|
11
12
|
## Usage
|
|
12
13
|
|
package/ui/misc/Markup.tsx
CHANGED
|
@@ -17,6 +17,7 @@ export interface MarkupProps extends Partial<MarkupOptions> {
|
|
|
17
17
|
* - Defaults to `MARKUP_OPTIONS` (full block + inline rule set). Pass `rules`, `rel`, `url`, `root`, or `schemes` as props to override.
|
|
18
18
|
* - `url`/`root` default to the current `<Meta>` context so link rules resolve site-absolute and relative hrefs.
|
|
19
19
|
* - Renders inside whatever ancestor element the caller provides — wrap in `<Prose>` to get the standard prose typography for the produced `<p>` / `<ul>` / `<pre>` / etc.
|
|
20
|
+
* - **Untrusted input:** when `children` comes from users, cap its length before rendering — parsing is linear for normal content but a few rules can degrade on adversarial input (a sane maximum for your use case, typically tens of kilobytes, bounds worst-case work). See `MarkupParser.parse()`.
|
|
20
21
|
*
|
|
21
22
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
22
23
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|
package/ui/tree/TreeMarkup.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare const TREE_MARKUP_RULES: MarkupRules;
|
|
|
21
21
|
* - Like `<Markup>` but defaults to `TREE_MARKUP_RULES`, so each backtick span resolves through `TreeLink` against the surrounding `<TreeProvider>` — a known token links to its canonical page, an unknown one stays plain code.
|
|
22
22
|
* - The standard way to render documentation-site content (READMEs, docblocks, per-symbol pages); plain `<Markup>` stays the choice for general user content that shouldn't cross-link.
|
|
23
23
|
* - Falls back to plain code spans outside a `<TreeProvider>`, so it's safe to use anywhere. Pass any `MarkupOptions` prop (including `rules`) to override.
|
|
24
|
+
* - Inherits `<Markup>`'s input-length guidance: when `children` is untrusted, cap its length before rendering (see `MarkupParser.parse()`).
|
|
24
25
|
*
|
|
25
26
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
26
27
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|
package/ui/tree/TreeMarkup.js
CHANGED
|
@@ -26,6 +26,7 @@ export const TREE_MARKUP_RULES = MARKUP_RULES.map(rule => (rule === CODE_RULE ?
|
|
|
26
26
|
* - Like `<Markup>` but defaults to `TREE_MARKUP_RULES`, so each backtick span resolves through `TreeLink` against the surrounding `<TreeProvider>` — a known token links to its canonical page, an unknown one stays plain code.
|
|
27
27
|
* - The standard way to render documentation-site content (READMEs, docblocks, per-symbol pages); plain `<Markup>` stays the choice for general user content that shouldn't cross-link.
|
|
28
28
|
* - Falls back to plain code spans outside a `<TreeProvider>`, so it's safe to use anywhere. Pass any `MarkupOptions` prop (including `rules`) to override.
|
|
29
|
+
* - Inherits `<Markup>`'s input-length guidance: when `children` is untrusted, cap its length before rendering (see `MarkupParser.parse()`).
|
|
29
30
|
*
|
|
30
31
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
31
32
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|
package/ui/tree/TreeMarkup.md
CHANGED
|
@@ -7,7 +7,7 @@ Renders a markup string just like `<Markup>`, but auto-links every inline code s
|
|
|
7
7
|
- Defaults to `TREE_MARKUP_RULES` — the full default rule set with the inline-code rule swapped for `TREE_CODE_RULE`, which renders each span through `TreeLink`.
|
|
8
8
|
- Resolution is exact-match against the tree map: a hit (`` `BooleanSchema` ``, `` `Store.get` ``) links; a miss (`` `bun run fix` ``, `` `string` ``) falls back to a plain code token, so unknown spans never produce a broken link.
|
|
9
9
|
- Falls back to plain code spans outside a `<TreeProvider>`, so it's safe to use anywhere — it simply stops linking.
|
|
10
|
-
- Inherits everything else from `<Markup>`: `url` / `root` default to the current `<Meta>` context, and any `MarkupOptions` prop (`rules`, `rel`, `url`, `root`, `schemes`) can be overridden directly.
|
|
10
|
+
- Inherits everything else from `<Markup>`: `url` / `root` default to the current `<Meta>` context, and any `MarkupOptions` prop (`rules`, `rel`, `url`, `root`, `schemes`) can be overridden directly. That includes the input-length guidance — cap untrusted `children` length before rendering.
|
|
11
11
|
- Wrap in `<Prose>` to give the produced `<p>` / `<ul>` / `<pre>` / etc. the standard prose typography.
|
|
12
12
|
|
|
13
13
|
## Usage
|
package/ui/tree/TreeMarkup.tsx
CHANGED
|
@@ -30,6 +30,7 @@ export const TREE_MARKUP_RULES: MarkupRules = MARKUP_RULES.map(rule => (rule ===
|
|
|
30
30
|
* - Like `<Markup>` but defaults to `TREE_MARKUP_RULES`, so each backtick span resolves through `TreeLink` against the surrounding `<TreeProvider>` — a known token links to its canonical page, an unknown one stays plain code.
|
|
31
31
|
* - The standard way to render documentation-site content (READMEs, docblocks, per-symbol pages); plain `<Markup>` stays the choice for general user content that shouldn't cross-link.
|
|
32
32
|
* - Falls back to plain code spans outside a `<TreeProvider>`, so it's safe to use anywhere. Pass any `MarkupOptions` prop (including `rules`) to override.
|
|
33
|
+
* - Inherits `<Markup>`'s input-length guidance: when `children` is untrusted, cap its length before rendering (see `MarkupParser.parse()`).
|
|
33
34
|
*
|
|
34
35
|
* @param children The source markup string to parse and render (renders `null` when empty).
|
|
35
36
|
* @returns The parsed markup as React nodes, or `null` when `children` is empty.
|