Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/formatter/src/addMissingParentheses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function addMissingParentheses(type: string): string {

validType = (validType + "\n..." + missingClosingChars.join("")).replace(
// Change (param: ...) to (param) => __RETURN_TYPE__ if needed
/(\([a-zA-Z0-9]*:[^)]*\))/,
/(\([a-zA-Z0-9]*:[^()]*\))/,
(p1) => `${p1} => ...`
);

Expand Down
21 changes: 11 additions & 10 deletions packages/formatter/src/errorMessagePrettifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {

return [
{
pattern: /(?:\s)'"(.*?)(?<!\\)"'(?:\s|:|.|$)/g,
pattern: /(?:\s)'"((?:[^"\\\n]|"(?!')|\\.)*)"'(?:\s|:|.|$)/g,
replacer: async (p1: string) => formatTypeBlock("", `"${p1}"`, codeBlock),
},
{
pattern: /['“](declare module )['”](.*)['“];['”]/g,
pattern: /['“](declare module )['”]([^'“”\n]*)['“];['”]/g,
replacer: (p1: string, p2: string) =>
formatTypeScriptBlock(`${p1} "${p2}"`),
},
Expand All @@ -73,7 +73,7 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
},
{
pattern: /(types) ['“](.*?)['”] and ['“](.*?)['”][.]?/gi,
pattern: /(types) ['“]([^'“”\n]*)['”] and ['“]([^'“”\n]*)['”][.]?/gi,
replacer: async (p1: string, p2: string, p3: string) => {
const [left, right] = await Promise.all([
formatTypeBlock(p1, p2, codeBlock),
Expand All @@ -83,7 +83,8 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
},
{
pattern: /type annotation must be ['“](.*?)['”] or ['“](.*?)['”][.]?/gi,
pattern:
/type annotation must be ['“]([^'“”\n]*)['”] or ['“]([^'“”\n]*)['”][.]?/gi,
replacer: async (p1: string, p2: string, p3: string | number) => {
if (typeof p3 === "string") {
const [left, right] = await Promise.all([
Expand All @@ -100,7 +101,7 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
},
{
pattern: /(Overload \d of \d), ['“](.*?)['”], /gi,
pattern: /(Overload \d of \d), ['“]([^'“”\n]*)['”], /gi,
replacer: async (p1: string, p2: string) =>
`${p1}${await formatTypeBlock("", p2, codeBlock)}`,
},
Expand All @@ -114,7 +115,7 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
{
pattern:
/(module|file|file name|imported via) ['"“](.*?)['"“](?=[\s(.|,]|$)/gi,
/(module|file|file name|imported via) ['"“]([^'"“”\n]*)['"“](?=[\s(.|,]|$)/gi,
replacer: async (p1: string, p2: string) =>
formatTypeBlock(p1, `"${p2}"`, codeBlock),
},
Expand All @@ -125,7 +126,7 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
{
pattern:
/['“]([^>]*)['”] (type|interface|return type|file|module|is (not )?assignable)/gi,
/['“]([^'“”>\n]*)['”] (type|interface|return type|file|module|is (not )?assignable)/gi,
replacer: async (p1: string, p2: string) =>
`${await formatTypeOrModuleBlock("", p1)} ${p2}`,
},
Expand All @@ -136,16 +137,16 @@ async function getRules(codeBlock: CodeBlockFn): Promise<Rule[]> {
},
{
pattern:
/['“](import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?.*?)['”]/g,
/['“](import|export|require|in|continue|break|let|false|true|const|new|throw|await|for await|[0-9]+)( ?[^'“”\n]*)['”]/g,
replacer: (p1: string, p2: string) => formatTypeScriptBlock(`${p1}${p2}`),
},
{
pattern: /(return|operator) ['“](.*?)['”]/gi,
pattern: /(return|operator) ['“]([^'“”\n]*)['”]/gi,
replacer: (p1: string, p2: string) =>
`${p1} ${formatTypeScriptBlock(p2)}`,
},
{
pattern: /(?<!\w)'((?:(?!["]).)*?)'(?!\w)/g,
pattern: /(?<!\w)'([^'"\n]*)'(?![\w:])/g,
replacer: (p1: string) => ` ${codeBlock(p1)} `,
},
];
Expand Down
21 changes: 21 additions & 0 deletions packages/formatter/test/formatter.vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ describe("formatter", (context) => {
);
});

it("handles malformed ReDoS regression inputs", async () => {
const attackMessages = [
"\\\\" + "\t'\"\t".repeat(2_000) + "\n",
"'declare module ”".repeat(2_000) + ";”\n'declare module '“;'",
"TYPES “".repeat(2_000) + "'\nTYPES “” AND ''",
"TYPE ANNOTATION MUST BE “".repeat(2_000) + "'",
"OVERLOAD 0 OF 0, “".repeat(2_000) + "\nOVERLOAD 0 OF 0, '', ",
" " + 'FILE "P'.repeat(2_000) + "\n",
" FILE“".repeat(2_000) + " FILE",
"'0" + "0".repeat(8_000) + "\n“0”",
"RETURN “".repeat(2_000) + "\nRETURN '”",
"'" + "'0'0\x00".repeat(2_000) + "\n",
];

expect(addMissingParentheses("(:".repeat(2_000))).toBeTypeOf("string");

for (const message of attackMessages) {
await expect(prettifyErrorMessage(message)).resolves.toBeTypeOf("string");
}
}, 3_000);

it("formats Special characters in object keys", async () => {
expect(await prettifyErrorMessage(errorWithSpecialCharsInObjectKeys)).toBe(
'Type `string` is not assignable to type `{ "abc*bc": string }`.'
Expand Down