github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/compiler/eval/pattern.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the Apache License Version 2.0.
     3  // This product includes software developed at Datadog (https://www.datadoghq.com/).
     4  // Copyright 2016-present Datadog, Inc.
     5  
     6  // Package eval holds eval related files
     7  package eval
     8  
     9  import (
    10  	"strings"
    11  )
    12  
    13  func nextSegment(str string) (bool, string, int) {
    14  	var inSegment bool
    15  	var start, end int
    16  
    17  	var star bool
    18  	if str[0] == '*' {
    19  		star = true
    20  	}
    21  
    22  	for i, c := range []byte(str) {
    23  		if c != '*' {
    24  			if !inSegment {
    25  				start = i
    26  				inSegment = true
    27  			}
    28  			end = i
    29  		} else if inSegment {
    30  			break
    31  		}
    32  	}
    33  
    34  	if star && start == 0 {
    35  		return star, "", 1
    36  	}
    37  
    38  	end++
    39  
    40  	return star, str[start:end], end
    41  }
    42  
    43  func index(s, subtr string, caseInsensitive bool) int {
    44  	if caseInsensitive {
    45  		s = strings.ToLower(s)
    46  		subtr = strings.ToLower(subtr)
    47  	}
    48  	return strings.Index(s, subtr)
    49  }
    50  
    51  func hasPrefix(s, prefix string, caseInsensitive bool) bool {
    52  	if caseInsensitive {
    53  		s = strings.ToLower(s)
    54  		prefix = strings.ToLower(prefix)
    55  	}
    56  	return strings.HasPrefix(s, prefix)
    57  }
    58  
    59  // PatternMatches matches a pattern against a string
    60  func PatternMatches(pattern string, str string, caseInsensitive bool) bool {
    61  	patternElem := newPatternElement(pattern)
    62  	return PatternMatchesWithSegments(patternElem, str, caseInsensitive)
    63  }
    64  
    65  // PatternMatchesWithSegments matches a pattern against a string
    66  func PatternMatchesWithSegments(patternElem patternElement, str string, caseInsensitive bool) bool {
    67  	if patternElem.pattern == "*" {
    68  		return true
    69  	}
    70  
    71  	if len(patternElem.pattern) == 0 {
    72  		return len(str) == 0
    73  	}
    74  
    75  	for _, seg := range patternElem.segments {
    76  		if seg.star {
    77  			// there is no pattern to match after the last star
    78  			if len(seg.segment) == 0 {
    79  				return true
    80  			}
    81  
    82  			index := index(str, seg.segment, caseInsensitive)
    83  			if index == -1 {
    84  				return false
    85  			}
    86  			str = str[index+len(seg.segment):]
    87  		} else {
    88  			if !hasPrefix(str, seg.segment, caseInsensitive) {
    89  				return false
    90  			}
    91  			str = str[len(seg.segment):]
    92  		}
    93  	}
    94  
    95  	// return false if there is still some str to match
    96  	return len(str) == 0
    97  }
    98  
    99  type patternElement struct {
   100  	pattern  string
   101  	segments []patternSegment
   102  }
   103  
   104  type patternSegment struct {
   105  	star    bool
   106  	segment string
   107  }
   108  
   109  func newPatternElement(element string) patternElement {
   110  	el := patternElement{
   111  		pattern: element,
   112  	}
   113  
   114  	for len(element) > 0 {
   115  		star, segment, nextIndex := nextSegment(element)
   116  		element = element[nextIndex:]
   117  		el.segments = append(el.segments, patternSegment{star, segment})
   118  	}
   119  
   120  	return el
   121  }