Description
I found that pasting a cURL command whose URL contains a long run of { characters makes the import hang the server. CurlParser.parseUrl in packages/backend/src/connectors/parsers/curl.parser.ts:280 runs url.replace(/\{\{([^}]+)\}\}/g, 'PLACEHOLDER_$1') unconditionally over the pasted URL, and the same loose {{([^}]+)}} shape is used on the pasted path, headers and body at lines 108, 141, 155, 156, 183 and 225. That pattern backtracks quadratically on brace runs with no closing }}: each start position scans to the end of the string and fails, so 4x input costs ~16x time. The import endpoint (connectors.controller.ts:1492, this.curlParser.parse(dto.content)) accepts user-supplied content with a 10mb body limit (main.ts:53), and Node runs this on the event loop, so one 80KB paste stalls every request for seconds. The codebase already treats this exact pattern shape as a ReDoS: caller-context.util.ts:26-34 documents that the looser \{\{([^}]+)\}\} backtracks polynomially and uses disjoint character classes instead.
Reproduce
- Run this (same regex literal as
curl.parser.ts:280, same hostile shape the repo's own specs use):
node -e "for (const k of [2000,8000,20000]) { const u='https://api.example.com/'+'{{{{'.repeat(k); const t=process.hrtime.bigint(); u.replace(/\{\{([^}]+)\}\}/g,'PLACEHOLDER_$1'); console.log('chars='+u.length+' '+((Number(process.hrtime.bigint()-t)/1e6).toFixed(1))+'ms'); }"
- Observed output:
chars=8024 46.7ms, chars=32024 719.7ms, chars=80024 4420.8ms -- roughly 16x slower per 4x input, i.e. quadratic.
- The equivalent fixed-shape replace (
/\{\{\s*([^{}\s]+)\s*\}\}/g) takes 0.1-0.3ms on all three inputs.
- End to end, the same input reaches the regex via the connector import: paste
curl https://api.example.com/{{{{… (brace run, no closing }}) as a curl import and parseUrl scans the whole URL at curl.parser.ts:280 before any placeholder is found.
Expected
The import parsers should scan pasted text in linear time. The smallest fix is to give the {{…}} patterns disjoint character classes (e.g. [^{}\s] for the variable name, allowing surrounding \s*), exactly as caller-context.util.ts:34 already does, and apply it to every {{([^}]+)}} occurrence in curl.parser.ts, postman.parser.ts, env-interpolation.util.ts, unresolved-placeholders.util.ts, connector-secrets.util.ts and catalog-env-rebuild.util.ts.
Checklist
Distinct from fix(security): close the five open CodeQL alerts on main #719 and its follow-up #722, which fixed ReDoS in graphql-builtins.ts slug handling and the OAuth e-mail check -- different files and patterns; the {{…}} occurrences above were not touched by either.
Description
I found that pasting a cURL command whose URL contains a long run of
{characters makes the import hang the server.CurlParser.parseUrlinpackages/backend/src/connectors/parsers/curl.parser.ts:280runsurl.replace(/\{\{([^}]+)\}\}/g, 'PLACEHOLDER_$1')unconditionally over the pasted URL, and the same loose{{([^}]+)}}shape is used on the pasted path, headers and body at lines 108, 141, 155, 156, 183 and 225. That pattern backtracks quadratically on brace runs with no closing}}: each start position scans to the end of the string and fails, so 4x input costs ~16x time. The import endpoint (connectors.controller.ts:1492,this.curlParser.parse(dto.content)) accepts user-supplied content with a 10mb body limit (main.ts:53), and Node runs this on the event loop, so one 80KB paste stalls every request for seconds. The codebase already treats this exact pattern shape as a ReDoS:caller-context.util.ts:26-34documents that the looser\{\{([^}]+)\}\}backtracks polynomially and uses disjoint character classes instead.Reproduce
curl.parser.ts:280, same hostile shape the repo's own specs use):node -e "for (const k of [2000,8000,20000]) { const u='https://api.example.com/'+'{{{{'.repeat(k); const t=process.hrtime.bigint(); u.replace(/\{\{([^}]+)\}\}/g,'PLACEHOLDER_$1'); console.log('chars='+u.length+' '+((Number(process.hrtime.bigint()-t)/1e6).toFixed(1))+'ms'); }"chars=8024 46.7ms,chars=32024 719.7ms,chars=80024 4420.8ms-- roughly 16x slower per 4x input, i.e. quadratic./\{\{\s*([^{}\s]+)\s*\}\}/g) takes 0.1-0.3ms on all three inputs.curl https://api.example.com/{{{{…(brace run, no closing}}) as acurlimport andparseUrlscans the whole URL atcurl.parser.ts:280before any placeholder is found.Expected
The import parsers should scan pasted text in linear time. The smallest fix is to give the
{{…}}patterns disjoint character classes (e.g.[^{}\s]for the variable name, allowing surrounding\s*), exactly ascaller-context.util.ts:34already does, and apply it to every{{([^}]+)}}occurrence incurl.parser.ts,postman.parser.ts,env-interpolation.util.ts,unresolved-placeholders.util.ts,connector-secrets.util.tsandcatalog-env-rebuild.util.ts.Checklist
Distinct from fix(security): close the five open CodeQL alerts on main #719 and its follow-up #722, which fixed ReDoS in
graphql-builtins.tsslug handling and the OAuth e-mail check -- different files and patterns; the{{…}}occurrences above were not touched by either.