@@ -192,9 +192,12 @@ function foresta(query) {
192192 // Parse attribute selector: [property operator value]
193193 // Operators: =, ^=, $=, *=, ~= (regex)
194194 var match ;
195+ var PROPERTY_PATTERN = '([a-zA-Z._]+)' ; // Matches property names including dot notation
195196
196197 // Regex pattern: [property~/pattern/]
197- if ( ( match = attrStr . match ( / ^ ( [ a - z A - Z . _ ] + ) ~ \/ ( .+ ) \/ $ / ) ) ) {
198+ // Note: User-provided regex patterns are executed as-is. In a production environment,
199+ // consider adding validation or timeout mechanisms to prevent ReDoS attacks.
200+ if ( ( match = attrStr . match ( new RegExp ( '^' + PROPERTY_PATTERN + '~\\/(.+)\\/$' ) ) ) ) {
198201 return {
199202 property : match [ 1 ] ,
200203 operator : '~=' ,
@@ -203,7 +206,7 @@ function foresta(query) {
203206 }
204207
205208 // Starts with: [property^="value"]
206- if ( ( match = attrStr . match ( / ^ ( [ a - z A - Z . _ ] + ) \ ^= [ " ' ] ? ( [ ^ " ' ] * ) [ " ' ] ? $ / ) ) ) {
209+ if ( ( match = attrStr . match ( new RegExp ( '^' + PROPERTY_PATTERN + '\\ ^=["\']{0,1} ([^"\ ']*)["\']{0,1}$' ) ) ) ) {
207210 return {
208211 property : match [ 1 ] ,
209212 operator : '^=' ,
@@ -212,7 +215,7 @@ function foresta(query) {
212215 }
213216
214217 // Ends with: [property$="value"]
215- if ( ( match = attrStr . match ( / ^ ( [ a - z A - Z . _ ] + ) \ $= [ " ' ] ? ( [ ^ " ' ] * ) [ " ' ] ? $ / ) ) ) {
218+ if ( ( match = attrStr . match ( new RegExp ( '^' + PROPERTY_PATTERN + '\\ $=["\']{0,1} ([^"\ ']*)["\']{0,1}$' ) ) ) ) {
216219 return {
217220 property : match [ 1 ] ,
218221 operator : '$=' ,
@@ -221,7 +224,7 @@ function foresta(query) {
221224 }
222225
223226 // Contains: [property*="value"]
224- if ( ( match = attrStr . match ( / ^ ( [ a - z A - Z . _ ] + ) \ *= [ " ' ] ? ( [ ^ " ' ] * ) [ " ' ] ? $ / ) ) ) {
227+ if ( ( match = attrStr . match ( new RegExp ( '^' + PROPERTY_PATTERN + '\\ *=["\']{0,1} ([^"\ ']*)["\']{0,1}$' ) ) ) ) {
225228 return {
226229 property : match [ 1 ] ,
227230 operator : '*=' ,
@@ -230,7 +233,7 @@ function foresta(query) {
230233 }
231234
232235 // Exact match: [property=value] or [property="value"]
233- if ( ( match = attrStr . match ( / ^ ( [ a - z A - Z . _ ] + ) = ( .+ ) $ / ) ) ) {
236+ if ( ( match = attrStr . match ( new RegExp ( '^' + PROPERTY_PATTERN + ' =(.+)$' ) ) ) ) {
234237 var value = match [ 2 ] ;
235238 // Remove quotes if present
236239 if ( ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) ||
@@ -605,8 +608,13 @@ function foresta(query) {
605608
606609 switch ( prevCombinator ) {
607610 case ' ' :
608- // Space in legacy mode: consecutive parent (not descendant!)
609- // For backward compatibility with original implementation
611+ // IMPORTANT: Space combinator behavior differs from CSS!
612+ // In CSS: space means "descendant at any level"
613+ // In Foresta: space means "consecutive parent chain"
614+ // Example: "Program VariableDeclaration VariableDeclarator" requires:
615+ // - VariableDeclarator whose parent is VariableDeclaration
616+ // - whose parent is Program
617+ // This matches the original Foresta.js behavior for backward compatibility
610618 currentExpression = currentExpression . parent ;
611619 break ;
612620 case '>' :
0 commit comments