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  }