github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/compiler/ast/secl.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 ast holds ast related files 7 package ast 8 9 import ( 10 "bytes" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/alecthomas/participle" 16 "github.com/alecthomas/participle/lexer" 17 "github.com/alecthomas/participle/lexer/ebnf" 18 ) 19 20 // ParsingContext defines a parsing context 21 type ParsingContext struct { 22 ruleParser *participle.Parser 23 macroParser *participle.Parser 24 } 25 26 // NewParsingContext returns a new parsing context 27 func NewParsingContext() *ParsingContext { 28 seclLexer := lexer.Must(ebnf.New(` 29 Comment = ("#" | "//") { "\u0000"…"\uffff"-"\n" } . 30 CIDR = IP "/" digit { digit } . 31 IP = (ipv4 | ipv6) . 32 Variable = "${" (alpha | "_") { "_" | alpha | digit | "." } "}" . 33 Duration = digit { digit } ("m" | "s" | "m" | "h") { "s" } . 34 Regexp = "r\"" { "\u0000"…"\uffff"-"\""-"\\" | "\\" any } "\"" . 35 Ident = (alpha | "_") { "_" | alpha | digit | "." | "[" | "]" } . 36 String = "\"" { "\u0000"…"\uffff"-"\""-"\\" | "\\" any } "\"" . 37 Pattern = "~\"" { "\u0000"…"\uffff"-"\""-"\\" | "\\" any } "\"" . 38 Int = [ "-" | "+" ] digit { digit } . 39 Punct = "!"…"/" | ":"…"@" | "["…` + "\"`\"" + ` | "{"…"~" . 40 Whitespace = ( " " | "\t" | "\n" ) { " " | "\t" | "\n" } . 41 ipv4 = (digit { digit } "." digit { digit } "." digit { digit } "." digit { digit }) . 42 ipv6 = ( [hex { hex }] ":" [hex { hex }] ":" [hex { hex }] [":" | "."] [hex { hex }] [":" | "."] [hex { hex }] [":" | "."] [hex { hex }] [":" | "."] [hex { hex }] [":" | "."] [hex { hex }]) . 43 hex = "a"…"f" | "A"…"F" | "0"…"9" . 44 alpha = "a"…"z" | "A"…"Z" . 45 digit = "0"…"9" . 46 any = "\u0000"…"\uffff" . 47 `)) 48 49 return &ParsingContext{ 50 ruleParser: buildParser(&Rule{}, seclLexer), 51 macroParser: buildParser(&Macro{}, seclLexer), 52 } 53 } 54 55 func buildParser(obj interface{}, lexer lexer.Definition) *participle.Parser { 56 parser, err := participle.Build(obj, 57 participle.Lexer(lexer), 58 participle.Elide("Whitespace", "Comment"), 59 participle.Map(unquoteLiteral, "String"), 60 participle.Map(parseDuration, "Duration"), 61 participle.Map(unquotePattern, "Pattern", "Regexp"), 62 ) 63 if err != nil { 64 panic(err) 65 } 66 return parser 67 } 68 69 func unquoteLiteral(t lexer.Token) (lexer.Token, error) { 70 unquoted := strings.TrimSpace(t.Value) 71 unquoted = unquoted[1 : len(unquoted)-1] 72 t.Value = unquoted 73 return t, nil 74 } 75 76 func unquotePattern(t lexer.Token) (lexer.Token, error) { 77 unquoted := strings.TrimSpace(t.Value[1:]) 78 unquoted = unquoted[1 : len(unquoted)-1] 79 t.Value = unquoted 80 return t, nil 81 } 82 83 func parseDuration(t lexer.Token) (lexer.Token, error) { 84 duration, err := time.ParseDuration(t.Value) 85 if err != nil { 86 return t, participle.Errorf(t.Pos, "invalid duration string %q: %s", t.Value, err) 87 } 88 89 t.Value = strconv.Itoa(int(duration.Nanoseconds())) 90 91 return t, nil 92 } 93 94 // ParseRule parses a SECL rule. 95 func (pc *ParsingContext) ParseRule(expr string) (*Rule, error) { 96 rule := &Rule{} 97 err := pc.ruleParser.Parse(bytes.NewBufferString(expr), rule) 98 if err != nil { 99 return nil, err 100 } 101 rule.Expr = expr 102 103 return rule, nil 104 } 105 106 // Rule describes a SECL rule 107 type Rule struct { 108 Pos lexer.Position 109 Expr string 110 111 BooleanExpression *BooleanExpression `parser:"@@"` 112 } 113 114 // ParseMacro parses a SECL macro 115 func (pc *ParsingContext) ParseMacro(expr string) (*Macro, error) { 116 macro := &Macro{} 117 err := pc.macroParser.Parse(bytes.NewBufferString(expr), macro) 118 if err != nil { 119 return nil, err 120 } 121 122 return macro, nil 123 } 124 125 // Macro describes a SECL macro 126 type Macro struct { 127 Pos lexer.Position 128 129 Expression *Expression `parser:"@@"` 130 Array *Array `parser:"| @@"` 131 Primary *Primary `parser:"| @@"` 132 } 133 134 // BooleanExpression describes a boolean expression 135 type BooleanExpression struct { 136 Pos lexer.Position 137 138 Expression *Expression `parser:"@@"` 139 } 140 141 // Expression describes an expression 142 type Expression struct { 143 Pos lexer.Position 144 145 Comparison *Comparison `parser:"@@"` 146 Op *string `parser:"[ @( \"|\" \"|\" | \"or\" | \"&\" \"&\" | \"and\" )"` 147 Next *BooleanExpression `parser:"@@ ]"` 148 } 149 150 // Comparison describes a comparison 151 type Comparison struct { 152 Pos lexer.Position 153 154 ArithmeticOperation *ArithmeticOperation `parser:"@@"` 155 ScalarComparison *ScalarComparison `parser:"[ @@"` 156 ArrayComparison *ArrayComparison `parser:"| @@ ]"` 157 } 158 159 // ScalarComparison describes a scalar comparison : the operator with the right operand 160 type ScalarComparison struct { 161 Pos lexer.Position 162 163 Op *string `parser:"@( \">\" \"=\" | \">\" | \"<\" \"=\" | \"<\" | \"!\" \"=\" | \"=\" \"=\" | \"=\" \"~\" | \"!\" \"~\" )"` 164 Next *Comparison `parser:"@@"` 165 } 166 167 // ArrayComparison describes an operation that tests membership in an array 168 type ArrayComparison struct { 169 Pos lexer.Position 170 171 Op *string `parser:"( @( \"in\" | \"not\" \"in\" | \"allin\" )"` 172 Array *Array `parser:"@@ )"` 173 } 174 175 // BitOperation describes an operation on bits 176 type BitOperation struct { 177 Pos lexer.Position 178 179 Unary *Unary `parser:"@@"` 180 Op *string `parser:"[ @( \"&\" | \"|\" | \"^\" )"` 181 Next *BitOperation `parser:"@@ ]"` 182 } 183 184 // ArithmeticOperation describes an arithmetic operation 185 type ArithmeticOperation struct { 186 Pos lexer.Position 187 188 First *BitOperation `parser:"@@"` 189 Rest []*ArithmeticElement `parser:"[ @@ { @@ } ]"` 190 } 191 192 // ArithmeticElement defines an arithmetic element 193 type ArithmeticElement struct { 194 Op string `parser:"@( \"+\" | \"-\" )"` 195 Operand *BitOperation `parser:"@@"` 196 } 197 198 // Unary describes an unary operation like logical not, binary not, minus 199 type Unary struct { 200 Pos lexer.Position 201 202 Op *string `parser:"( @( \"!\" | \"not\" | \"-\" | \"^\" )"` 203 Unary *Unary `parser:"@@ )"` 204 Primary *Primary `parser:"| @@"` 205 } 206 207 // Primary describes a single operand. It can be a simple identifier, a number, 208 // a string or a full expression in parenthesis 209 type Primary struct { 210 Pos lexer.Position 211 212 Ident *string `parser:"@Ident"` 213 CIDR *string `parser:"| @CIDR"` 214 IP *string `parser:"| @IP"` 215 Number *int `parser:"| @Int"` 216 Variable *string `parser:"| @Variable"` 217 String *string `parser:"| @String"` 218 Pattern *string `parser:"| @Pattern"` 219 Regexp *string `parser:"| @Regexp"` 220 Duration *int `parser:"| @Duration"` 221 SubExpression *Expression `parser:"| \"(\" @@ \")\""` 222 } 223 224 // StringMember describes a String based array member 225 type StringMember struct { 226 Pos lexer.Position 227 228 String *string `parser:"@String"` 229 Pattern *string `parser:"| @Pattern"` 230 Regexp *string `parser:"| @Regexp"` 231 } 232 233 // CIDRMember describes a CIDR based array member 234 type CIDRMember struct { 235 Pos lexer.Position 236 237 IP *string `parser:"@IP"` 238 CIDR *string `parser:"| @CIDR"` 239 } 240 241 // Array describes an array of values 242 type Array struct { 243 Pos lexer.Position 244 245 CIDR *string `parser:"@CIDR"` 246 Variable *string `parser:"| @Variable"` 247 Ident *string `parser:"| @Ident"` 248 StringMembers []StringMember `parser:"| \"[\" @@ { \",\" @@ } \"]\""` 249 CIDRMembers []CIDRMember `parser:"| \"[\" @@ { \",\" @@ } \"]\""` 250 Numbers []int `parser:"| \"[\" @Int { \",\" @Int } \"]\""` 251 }