Skip to content

Commit 8ecdbab

Browse files
authored
Fix Bug #3122: Fix 'sync_list' globbing rule application (#3123)
* Add check for 'globbing' and 'wildcard' rules, that the number of segments before the first wildcard character need to match before the actual rule can be applied * Only calculate ruleSegments and pathSegments once * Use same calculation method to count path segments for applicability globbing rule as this is more consistent
1 parent a7c3dcc commit 8ecdbab

File tree

1 file changed

+87
-78
lines changed

1 file changed

+87
-78
lines changed

src/clientSideFiltering.d

+87-78
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,17 @@ class ClientSideFiltering {
335335
// wildcard (*) rules are below if we get there, if this rule does not contain a wildcard
336336
if ((to!string(syncListRuleEntry[0]) == "/") && (!canFind(syncListRuleEntry, wildcard))) {
337337
// attempt to perform an exact segment match
338-
if (exactMatchRuleSegmentsToPathSegments(syncListRuleEntry, path)) {
338+
// split both paths by '/' to create segment arrays
339+
string[] ruleSegments = syncListRuleEntry.strip.split("/").filter!(s => !s.empty).array;
340+
string[] pathSegments = path.strip.split("/").filter!(s => !s.empty).array;
341+
342+
// Print rule and input segments for validation during debug
343+
if (debugLogging) {
344+
addLogEntry("Rule Segments: " ~ to!string(ruleSegments), ["debug"]);
345+
addLogEntry("Path Segments: " ~ to!string(pathSegments), ["debug"]);
346+
}
347+
348+
if (exactMatchRuleSegmentsToPathSegments(ruleSegments, pathSegments)) {
339349
// EXACT PATH MATCH
340350
if (debugLogging) {addLogEntry("Exact path match with 'sync_list' rule entry", ["debug"]);}
341351

@@ -359,19 +369,18 @@ class ClientSideFiltering {
359369
} else {
360370
// NOT an EXACT MATCH, so check the very first path segment
361371
// - This is so that paths in 'sync_list' as specified as /some path/another path/ actually get included|excluded correctly
362-
if (matchFirstSegmentToPathFirstSegment(syncListRuleEntry, path)) {
372+
if (matchFirstSegmentToPathFirstSegment(ruleSegments, pathSegments)) {
363373
// PARENT ROOT MATCH
364374
if (debugLogging) {addLogEntry("Parent root path match with 'sync_list' rule entry", ["debug"]);}
365375

366376
// Does the 'rest' of the input path match?
367377
// We only need to do this step if the input path has more and 1 segment (the parent folder)
368-
auto inputSegments = path.strip.split("/").filter!(s => !s.empty).array;
369-
if (count(inputSegments) > 1) {
378+
if (count(pathSegments) > 1) {
370379
// More segments to check, so do a parental path match
371-
if (matchRuleSegmentsToPathSegments(syncListRuleEntry, path)) {
380+
if (matchRuleSegmentsToPathSegments(ruleSegments, pathSegments)) {
372381
// PARENTAL PATH MATCH
373382
if (debugLogging) {addLogEntry("Parental path match with 'sync_list' rule entry", ["debug"]);}
374-
383+
// What sort of rule was this?
375384
if (!thisIsAnExcludeRule) {
376385
// Include Rule
377386
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: parental path match", ["debug"]);}
@@ -481,63 +490,78 @@ class ClientSideFiltering {
481490
}
482491

483492
// Does the 'sync_list' rule contain a wildcard (*) or globbing (**) reference anywhere in the rule?
493+
// EXCLUSION
494+
// !/Programming/Projects/Android/**/build/*
495+
// INCLUSION
496+
// /Programming/Projects/Android/**/build/*
484497
if (canFind(syncListRuleEntry, wildcard)) {
485498
// reset the applicable flag
486499
wildcardRuleMatched = false;
487-
488-
// sync_list rule contains some sort of wildcard sequence
489-
if (thisIsAnExcludeRule) {
490-
if (debugLogging) {addLogEntry("wildcard (* or **) exclusion rule: !" ~ syncListRuleEntry, ["debug"]);}
491-
} else {
492-
if (debugLogging) {addLogEntry("wildcard (* or **) inclusion rule: " ~ syncListRuleEntry, ["debug"]);}
493-
}
494500

495-
// Is this a globbing rule (**) or just a single wildcard (*) entries
496-
if (canFind(syncListRuleEntry, globbing)) {
497-
// globbing (**) rule processing
498-
if (matchPathAgainstRule(path, syncListRuleEntry)) {
499-
// set the applicable flag
500-
wildcardRuleMatched = true;
501-
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: globbing pattern match", ["debug"]);}
502-
}
501+
// Does this 'wildcard' rule even apply to this path?
502+
auto wildcardDepth = firstWildcardDepth(syncListRuleEntry);
503+
auto pathSegments = count(path.strip.split("/").filter!(s => !s.empty).array);
504+
505+
// are there enough path segments for this wildcard rule to apply?
506+
if (pathSegments < wildcardDepth) {
507+
// there are not enough path segments up to the first wildcard character for this rule to even be applicable
508+
if (debugLogging) {addLogEntry("- This sync list wildcard rule should not be evaluated as the wildcard appears beyond the current input path", ["debug"]);}
503509
} else {
504-
// wildcard (*) rule processing
505-
// create regex from 'syncListRuleEntry'
506-
auto allowedMask = regex(createRegexCompatiblePath(syncListRuleEntry));
507-
if (matchAll(path, allowedMask)) {
508-
// set the applicable flag
509-
wildcardRuleMatched = true;
510-
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard pattern match", ["debug"]);}
510+
// path segments are enough for this wildcard rule to potentially apply
511+
// sync_list rule contains some sort of wildcard sequence
512+
if (thisIsAnExcludeRule) {
513+
if (debugLogging) {addLogEntry("wildcard (* or **) exclusion rule: !" ~ syncListRuleEntry, ["debug"]);}
511514
} else {
512-
// matchAll no match ... try another way just to be sure
515+
if (debugLogging) {addLogEntry("wildcard (* or **) inclusion rule: " ~ syncListRuleEntry, ["debug"]);}
516+
}
517+
518+
// Is this a globbing rule (**) or just a single wildcard (*) entries
519+
if (canFind(syncListRuleEntry, globbing)) {
520+
// globbing (**) rule processing
513521
if (matchPathAgainstRule(path, syncListRuleEntry)) {
514522
// set the applicable flag
515523
wildcardRuleMatched = true;
516-
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard pattern match using segment matching", ["debug"]);}
524+
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: globbing pattern match using segment matching", ["debug"]);}
517525
}
518-
}
519-
}
520-
521-
// Was the rule matched?
522-
if (wildcardRuleMatched) {
523-
// Is this an exclude rule?
524-
if (thisIsAnExcludeRule) {
525-
// Yes exclude rule
526-
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard|globbing rule matched and must be excluded", ["debug"]);}
527-
excludeWildcardMatched = true;
528-
exclude = true;
529-
finalResult = true;
530526
} else {
531-
// include rule
532-
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard|globbing pattern matched and must be included", ["debug"]);}
533-
finalResult = false;
534-
excludeWildcardMatched = false;
527+
// wildcard (*) rule processing
528+
// create regex from 'syncListRuleEntry'
529+
auto allowedMask = regex(createRegexCompatiblePath(syncListRuleEntry));
530+
if (matchAll(path, allowedMask)) {
531+
// set the applicable flag
532+
wildcardRuleMatched = true;
533+
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard pattern match", ["debug"]);}
534+
} else {
535+
// matchAll no match ... try another way just to be sure
536+
if (matchPathAgainstRule(path, syncListRuleEntry)) {
537+
// set the applicable flag
538+
wildcardRuleMatched = true;
539+
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard pattern match using segment matching", ["debug"]);}
540+
}
541+
}
542+
}
543+
544+
// Was the rule matched?
545+
if (wildcardRuleMatched) {
546+
// Is this an exclude rule?
547+
if (thisIsAnExcludeRule) {
548+
// Yes exclude rule
549+
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard|globbing rule matched and must be excluded", ["debug"]);}
550+
excludeWildcardMatched = true;
551+
exclude = true;
552+
finalResult = true;
553+
} else {
554+
// include rule
555+
if (debugLogging) {addLogEntry("Evaluation against 'sync_list' rule result: wildcard|globbing pattern matched and must be included", ["debug"]);}
556+
finalResult = false;
557+
excludeWildcardMatched = false;
558+
}
535559
}
536560
}
537561
}
538562
}
539563

540-
564+
// debug logging post 'sync_list' rule evaluations
541565
if (debugLogging) {
542566
// Rule evaluation complete
543567
addLogEntry("------------------------------------------------------------------------", ["debug"]);
@@ -564,6 +588,18 @@ class ClientSideFiltering {
564588
return finalResult;
565589
}
566590

591+
// Calculate wildcard character depth in path
592+
int firstWildcardDepth(string syncListRuleEntry) {
593+
int depth = 0;
594+
foreach (segment; pathSplitter(syncListRuleEntry))
595+
{
596+
if (segment.canFind("*")) // Check for wildcard characters
597+
return depth;
598+
depth++;
599+
}
600+
return depth; // No wildcard found should be '0'
601+
}
602+
567603
// Create a wildcard regex compatible string based on the sync list rule
568604
string createRegexCompatiblePath(string regexCompatiblePath) {
569605
regexCompatiblePath = regexCompatiblePath.replace(".", "\\."); // Escape the dot (.) if present
@@ -625,18 +661,9 @@ class ClientSideFiltering {
625661
return j == ruleSegments.length || (j == ruleSegments.length - 1 && ruleSegments[j] == "**") || lastSegmentMatchesRule;
626662
}
627663

628-
bool exactMatchRuleSegmentsToPathSegments(string rulePath, string inputPath) {
664+
bool exactMatchRuleSegmentsToPathSegments(string[] ruleSegments, string[] inputSegments) {
629665
if (debugLogging) {addLogEntry("Running exactMatchRuleSegmentsToPathSegments()", ["debug"]);}
630-
// Split both paths by '/'
631-
auto ruleSegments = rulePath.strip.split("/").filter!(s => !s.empty).array;
632-
auto inputSegments = inputPath.strip.split("/").filter!(s => !s.empty).array;
633666

634-
// Print rule and input segments for validation
635-
if (debugLogging) {
636-
addLogEntry("Rule Segments: " ~ to!string(ruleSegments), ["debug"]);
637-
addLogEntry("Input Segments: " ~ to!string(inputSegments), ["debug"]);
638-
}
639-
640667
// If rule has more segments than input, or input has more segments than rule, no match is possible
641668
if ((ruleSegments.length > inputSegments.length) || ( inputSegments.length > ruleSegments.length)) {
642669
return false;
@@ -655,18 +682,9 @@ class ClientSideFiltering {
655682
return true;
656683
}
657684

658-
bool matchRuleSegmentsToPathSegments(string rulePath, string inputPath) {
685+
bool matchRuleSegmentsToPathSegments(string[] ruleSegments, string[] inputSegments) {
659686
if (debugLogging) {addLogEntry("Running matchRuleSegmentsToPathSegments()", ["debug"]);}
660-
// Split both paths by '/'
661-
auto ruleSegments = rulePath.strip.split("/").filter!(s => !s.empty).array;
662-
auto inputSegments = inputPath.strip.split("/").filter!(s => !s.empty).array;
663687

664-
// Print rule and input segments for validation
665-
if (debugLogging) {
666-
addLogEntry("Rule Segments: " ~ to!string(ruleSegments), ["debug"]);
667-
addLogEntry("Input Segments: " ~ to!string(inputSegments), ["debug"]);
668-
}
669-
670688
// If rule has more segments than input, no match is possible
671689
if (ruleSegments.length > inputSegments.length) {
672690
return false;
@@ -676,18 +694,9 @@ class ClientSideFiltering {
676694
return equal(ruleSegments, inputSegments[0 .. ruleSegments.length]);
677695
}
678696

679-
bool matchFirstSegmentToPathFirstSegment(string rulePath, string inputPath) {
697+
bool matchFirstSegmentToPathFirstSegment(string[] ruleSegments, string[] inputSegments) {
680698
if (debugLogging) {addLogEntry("Running matchFirstSegmentToPathFirstSegment()", ["debug"]);}
681-
// Split both paths by '/'
682-
auto ruleSegments = rulePath.strip.split("/").filter!(s => !s.empty).array;
683-
auto inputSegments = inputPath.strip.split("/").filter!(s => !s.empty).array;
684-
685-
// Print rule and input segments for validation
686-
if (debugLogging) {
687-
addLogEntry("Rule Segments: " ~ to!string(ruleSegments), ["debug"]);
688-
addLogEntry("Input Segments: " ~ to!string(inputSegments), ["debug"]);
689-
}
690-
699+
691700
// Check that both segments are not empty
692701
if (ruleSegments.length == 0 || inputSegments.length == 0) {
693702
return false; // Return false if either segment array is empty

0 commit comments

Comments
 (0)