Skip to content

Commit 0530273

Browse files
committed
address review feedback
1 parent c3e9b0f commit 0530273

2 files changed

Lines changed: 32 additions & 34 deletions

File tree

packages/agentflow/src/core/utils/fieldVisibility.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ describe('conditionMatches', () => {
8585
expect(conditionMatches(['aaa'], longPattern)).toBe(false)
8686
})
8787

88+
it('rejects nested quantifier patterns to mitigate ReDoS', () => {
89+
expect(conditionMatches('aaa', '(a+)+$')).toBe(false)
90+
expect(conditionMatches(['aaa'], '(a*)*')).toBe(false)
91+
})
92+
8893
it('scalar boolean strict equality', () => {
8994
expect(conditionMatches(true, true)).toBe(true)
9095
expect(conditionMatches(true, false)).toBe(false)

packages/agentflow/src/core/utils/fieldVisibility.ts

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@ const REGEX_INTENT = /[|()^$*+?[\]]/
99
/** Maximum length for regex patterns to mitigate ReDoS from untrusted input. */
1010
const MAX_REGEX_LENGTH = 200
1111

12-
/** Timeout-safe regex test: compiles and tests the pattern, returning false for invalid or oversized patterns. */
12+
/** Detects nested quantifiers that can cause catastrophic backtracking, e.g. (a+)+, (a*)*, (\w+)+. */
13+
const NESTED_QUANTIFIER = /[+*]\)[+*?]/
14+
15+
/**
16+
* Safe regex test: rejects patterns that are oversized, contain nested quantifiers
17+
* (the primary cause of catastrophic backtracking), or are syntactically invalid.
18+
*/
1319
function safeRegexTest(pattern: string, value: string): boolean {
1420
if (pattern.length > MAX_REGEX_LENGTH) return false
21+
if (NESTED_QUANTIFIER.test(pattern)) return false
1522
try {
1623
return new RegExp(pattern).test(value)
1724
} catch {
@@ -22,44 +29,30 @@ function safeRegexTest(pattern: string, value: string): boolean {
2229
/**
2330
* Check if a ground value matches a comparison value using the same matrix
2431
* as the UI's _showHideOperation in genericHelper.js.
32+
*
33+
* Ground values are normalized to arrays so scalar and array inputs share
34+
* a single code path, reducing duplication.
2535
*/
2636
export function conditionMatches(groundValue: unknown, comparisonValue: unknown): boolean {
27-
if (Array.isArray(groundValue)) {
28-
if (Array.isArray(comparisonValue)) {
29-
return comparisonValue.some((val) => (groundValue as unknown[]).includes(val))
30-
}
31-
if (typeof comparisonValue === 'string') {
32-
return (groundValue as unknown[]).some((val) => {
33-
if (comparisonValue === val) return true
34-
if (REGEX_INTENT.test(comparisonValue)) {
35-
return safeRegexTest(comparisonValue, String(val))
36-
}
37-
return false
38-
})
39-
}
40-
if (typeof comparisonValue === 'boolean' || typeof comparisonValue === 'number') {
41-
return (groundValue as unknown[]).includes(comparisonValue)
42-
}
43-
if (typeof comparisonValue === 'object' && comparisonValue !== null) {
44-
return (groundValue as unknown[]).some((val) => isEqual(comparisonValue, val))
45-
}
46-
} else {
47-
if (Array.isArray(comparisonValue)) {
48-
return comparisonValue.includes(groundValue)
49-
}
50-
if (typeof comparisonValue === 'string') {
51-
if (comparisonValue === groundValue) return true
37+
const groundArr: unknown[] = Array.isArray(groundValue) ? groundValue : [groundValue]
38+
39+
if (Array.isArray(comparisonValue)) {
40+
return comparisonValue.some((val) => groundArr.includes(val))
41+
}
42+
if (typeof comparisonValue === 'string') {
43+
return groundArr.some((val) => {
44+
if (comparisonValue === val) return true
5245
if (REGEX_INTENT.test(comparisonValue)) {
53-
return safeRegexTest(comparisonValue, String(groundValue))
46+
return safeRegexTest(comparisonValue, String(val))
5447
}
5548
return false
56-
}
57-
if (typeof comparisonValue === 'boolean' || typeof comparisonValue === 'number') {
58-
return comparisonValue === groundValue
59-
}
60-
if (typeof comparisonValue === 'object' && comparisonValue !== null) {
61-
return isEqual(comparisonValue, groundValue)
62-
}
49+
})
50+
}
51+
if (typeof comparisonValue === 'boolean' || typeof comparisonValue === 'number') {
52+
return groundArr.includes(comparisonValue)
53+
}
54+
if (typeof comparisonValue === 'object' && comparisonValue !== null) {
55+
return groundArr.some((val) => isEqual(comparisonValue, val))
6356
}
6457
return false
6558
}

0 commit comments

Comments
 (0)