Skip to content

Commit

Permalink
Filter out expressions that are certainly safe
Browse files Browse the repository at this point in the history
Update the expressions matcher logic to not complain about expressions
that can be proven to be safe because they consist exclusively of
literals[1] and/or functions[2]  whose output does not contain any of
the input values (i.e. predicate functions as well as `hashFiles`).

The choice to match twice, once against the input with safe expressions
removed and once against the actual input, may seem unnecessarily bad
from a performance ponit of view. However, if the stripped input does
contain a problem, we want to get the matches on the original input to
present to the user. The performance impact is minimized by using the
`Regexp.Find` method which will return on the first match.

--
1. https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#literals
2. https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#functions
--

Signed-off-by: Eric Cornelissen <ericornelissen@gmail.com>
  • Loading branch information
ericcornelissen committed Dec 27, 2024
1 parent b4c97ce commit 6b3e1bb
Show file tree
Hide file tree
Showing 2 changed files with 317 additions and 83 deletions.
37 changes: 37 additions & 0 deletions matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package ades

import (
"bytes"
"regexp"
)

Expand All @@ -40,6 +41,10 @@ var allExprRegExp = regexp.MustCompile(`\$\{\{.*?\}\}`)
type allExprMatcher struct{}

func (m allExprMatcher) FindAll(v []byte) [][]byte {
if allExprRegExp.Find(stripSafe(v)) == nil {
return nil
}

return allExprRegExp.FindAll(v, len(v))
}

Expand All @@ -48,5 +53,37 @@ var conservativeExprRegExp = regexp.MustCompile(`\$\{\{.+?(github\.event\.issue\
type conservativeExprMatcher struct{}

func (m conservativeExprMatcher) FindAll(v []byte) [][]byte {
if conservativeExprRegExp.Find(stripSafe(v)) == nil {
return nil
}

return conservativeExprRegExp.FindAll(v, len(v))
}

var (
leading = `(?P<leading>\$\{\{(|.*? ))`
trailing = `(?P<trailing>( .*?|)\}\})`

LiteralInExprRegExp = regexp.MustCompile(leading + `(true|false|null|-?\d+(\.\d+)?|0x[0-9A-Fa-f]+|-?\d+\.\d+e-?\d+|'[^']+')` + trailing)
SafeFunctionInExprRegExp = regexp.MustCompile(leading + `((always|cancelled|failure|success)\(\s*\)|(contains|endsWith|startsWith)\([^,]+,[^,)]+\)|(hashFiles)\(([^,]+,)*[^,)]+\))` + trailing)

EmptyExprRegExp = regexp.MustCompile(`\$\{\{\s*\}\}`)
)

func stripSafe(v []byte) []byte {
exps := []regexp.Regexp{
*LiteralInExprRegExp,
*SafeFunctionInExprRegExp,
}

r := v
for _, exp := range exps {
r = exp.ReplaceAll(r, []byte("$leading$trailing"))
}

if !bytes.Equal(r, v) {
return stripSafe(r)
}

return EmptyExprRegExp.ReplaceAll(v, nil)
}
Loading

0 comments on commit 6b3e1bb

Please sign in to comment.