parser_assert.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package hubtest
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "github.com/antonmedv/expr"
  8. log "github.com/sirupsen/logrus"
  9. "gopkg.in/yaml.v2"
  10. "github.com/crowdsecurity/crowdsec/pkg/dumps"
  11. "github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
  12. "github.com/crowdsecurity/go-cs-lib/maptools"
  13. )
  14. type AssertFail struct {
  15. File string
  16. Line int
  17. Expression string
  18. Debug map[string]string
  19. }
  20. type ParserAssert struct {
  21. File string
  22. AutoGenAssert bool
  23. AutoGenAssertData string
  24. NbAssert int
  25. Fails []AssertFail
  26. Success bool
  27. TestData *dumps.ParserResults
  28. }
  29. func NewParserAssert(file string) *ParserAssert {
  30. ParserAssert := &ParserAssert{
  31. File: file,
  32. NbAssert: 0,
  33. Success: false,
  34. Fails: make([]AssertFail, 0),
  35. AutoGenAssert: false,
  36. TestData: &dumps.ParserResults{},
  37. }
  38. return ParserAssert
  39. }
  40. func (p *ParserAssert) AutoGenFromFile(filename string) (string, error) {
  41. err := p.LoadTest(filename)
  42. if err != nil {
  43. return "", err
  44. }
  45. ret := p.AutoGenParserAssert()
  46. return ret, nil
  47. }
  48. func (p *ParserAssert) LoadTest(filename string) error {
  49. parserDump, err := dumps.LoadParserDump(filename)
  50. if err != nil {
  51. return fmt.Errorf("loading parser dump file: %+v", err)
  52. }
  53. p.TestData = parserDump
  54. return nil
  55. }
  56. func (p *ParserAssert) AssertFile(testFile string) error {
  57. file, err := os.Open(p.File)
  58. if err != nil {
  59. return fmt.Errorf("failed to open")
  60. }
  61. if err := p.LoadTest(testFile); err != nil {
  62. return fmt.Errorf("unable to load parser dump file '%s': %s", testFile, err)
  63. }
  64. scanner := bufio.NewScanner(file)
  65. scanner.Split(bufio.ScanLines)
  66. nbLine := 0
  67. for scanner.Scan() {
  68. nbLine++
  69. if scanner.Text() == "" {
  70. continue
  71. }
  72. ok, err := p.Run(scanner.Text())
  73. if err != nil {
  74. return fmt.Errorf("unable to run assert '%s': %+v", scanner.Text(), err)
  75. }
  76. p.NbAssert++
  77. if !ok {
  78. log.Debugf("%s is FALSE", scanner.Text())
  79. failedAssert := &AssertFail{
  80. File: p.File,
  81. Line: nbLine,
  82. Expression: scanner.Text(),
  83. Debug: make(map[string]string),
  84. }
  85. match := variableRE.FindStringSubmatch(scanner.Text())
  86. var variable string
  87. if len(match) == 0 {
  88. log.Infof("Couldn't get variable of line '%s'", scanner.Text())
  89. variable = scanner.Text()
  90. } else {
  91. variable = match[1]
  92. }
  93. result, err := p.EvalExpression(variable)
  94. if err != nil {
  95. log.Errorf("unable to evaluate variable '%s': %s", variable, err)
  96. continue
  97. }
  98. failedAssert.Debug[variable] = result
  99. p.Fails = append(p.Fails, *failedAssert)
  100. continue
  101. }
  102. //fmt.Printf(" %s '%s'\n", emoji.GreenSquare, scanner.Text())
  103. }
  104. file.Close()
  105. if p.NbAssert == 0 {
  106. assertData, err := p.AutoGenFromFile(testFile)
  107. if err != nil {
  108. return fmt.Errorf("couldn't generate assertion: %s", err)
  109. }
  110. p.AutoGenAssertData = assertData
  111. p.AutoGenAssert = true
  112. }
  113. if len(p.Fails) == 0 {
  114. p.Success = true
  115. }
  116. return nil
  117. }
  118. func (p *ParserAssert) RunExpression(expression string) (interface{}, error) {
  119. //debug doesn't make much sense with the ability to evaluate "on the fly"
  120. //var debugFilter *exprhelpers.ExprDebugger
  121. var output interface{}
  122. env := map[string]interface{}{"results": *p.TestData}
  123. runtimeFilter, err := expr.Compile(expression, exprhelpers.GetExprOptions(env)...)
  124. if err != nil {
  125. log.Errorf("failed to compile '%s' : %s", expression, err)
  126. return output, err
  127. }
  128. //dump opcode in trace level
  129. log.Tracef("%s", runtimeFilter.Disassemble())
  130. output, err = expr.Run(runtimeFilter, env)
  131. if err != nil {
  132. log.Warningf("running : %s", expression)
  133. log.Warningf("runtime error : %s", err)
  134. return output, fmt.Errorf("while running expression %s: %w", expression, err)
  135. }
  136. return output, nil
  137. }
  138. func (p *ParserAssert) EvalExpression(expression string) (string, error) {
  139. output, err := p.RunExpression(expression)
  140. if err != nil {
  141. return "", err
  142. }
  143. ret, err := yaml.Marshal(output)
  144. if err != nil {
  145. return "", err
  146. }
  147. return string(ret), nil
  148. }
  149. func (p *ParserAssert) Run(assert string) (bool, error) {
  150. output, err := p.RunExpression(assert)
  151. if err != nil {
  152. return false, err
  153. }
  154. switch out := output.(type) {
  155. case bool:
  156. return out, nil
  157. default:
  158. return false, fmt.Errorf("assertion '%s' is not a condition", assert)
  159. }
  160. }
  161. func Escape(val string) string {
  162. val = strings.ReplaceAll(val, `\`, `\\`)
  163. val = strings.ReplaceAll(val, `"`, `\"`)
  164. return val
  165. }
  166. func (p *ParserAssert) AutoGenParserAssert() string {
  167. //attempt to autogen parser asserts
  168. ret := fmt.Sprintf("len(results) == %d\n", len(*p.TestData))
  169. //sort map keys for consistent order
  170. stages := maptools.SortedKeys(*p.TestData)
  171. for _, stage := range stages {
  172. parsers := (*p.TestData)[stage]
  173. //sort map keys for consistent order
  174. pnames := maptools.SortedKeys(parsers)
  175. for _, parser := range pnames {
  176. presults := parsers[parser]
  177. ret += fmt.Sprintf(`len(results["%s"]["%s"]) == %d`+"\n", stage, parser, len(presults))
  178. for pidx, result := range presults {
  179. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Success == %t`+"\n", stage, parser, pidx, result.Success)
  180. if !result.Success {
  181. continue
  182. }
  183. for _, pkey := range maptools.SortedKeys(result.Evt.Parsed) {
  184. pval := result.Evt.Parsed[pkey]
  185. if pval == "" {
  186. continue
  187. }
  188. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Evt.Parsed["%s"] == "%s"`+"\n", stage, parser, pidx, pkey, Escape(pval))
  189. }
  190. for _, mkey := range maptools.SortedKeys(result.Evt.Meta) {
  191. mval := result.Evt.Meta[mkey]
  192. if mval == "" {
  193. continue
  194. }
  195. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Evt.Meta["%s"] == "%s"`+"\n", stage, parser, pidx, mkey, Escape(mval))
  196. }
  197. for _, ekey := range maptools.SortedKeys(result.Evt.Enriched) {
  198. eval := result.Evt.Enriched[ekey]
  199. if eval == "" {
  200. continue
  201. }
  202. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Evt.Enriched["%s"] == "%s"`+"\n", stage, parser, pidx, ekey, Escape(eval))
  203. }
  204. for _, ukey := range maptools.SortedKeys(result.Evt.Unmarshaled) {
  205. uval := result.Evt.Unmarshaled[ukey]
  206. if uval == "" {
  207. continue
  208. }
  209. base := fmt.Sprintf(`results["%s"]["%s"][%d].Evt.Unmarshaled["%s"]`, stage, parser, pidx, ukey)
  210. for _, line := range p.buildUnmarshaledAssert(base, uval) {
  211. ret += line
  212. }
  213. }
  214. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Evt.Whitelisted == %t`+"\n", stage, parser, pidx, result.Evt.Whitelisted)
  215. if result.Evt.WhitelistReason != "" {
  216. ret += fmt.Sprintf(`results["%s"]["%s"][%d].Evt.WhitelistReason == "%s"`+"\n", stage, parser, pidx, Escape(result.Evt.WhitelistReason))
  217. }
  218. }
  219. }
  220. }
  221. return ret
  222. }
  223. func (p *ParserAssert) buildUnmarshaledAssert(ekey string, eval interface{}) []string {
  224. ret := make([]string, 0)
  225. switch val := eval.(type) {
  226. case map[string]interface{}:
  227. for k, v := range val {
  228. ret = append(ret, p.buildUnmarshaledAssert(fmt.Sprintf(`%s["%s"]`, ekey, k), v)...)
  229. }
  230. case map[interface{}]interface{}:
  231. for k, v := range val {
  232. ret = append(ret, p.buildUnmarshaledAssert(fmt.Sprintf(`%s["%s"]`, ekey, k), v)...)
  233. }
  234. case []interface{}:
  235. case string:
  236. ret = append(ret, fmt.Sprintf(`%s == "%s"`+"\n", ekey, Escape(val)))
  237. case bool:
  238. ret = append(ret, fmt.Sprintf(`%s == %t`+"\n", ekey, val))
  239. case int:
  240. ret = append(ret, fmt.Sprintf(`%s == %d`+"\n", ekey, val))
  241. case float64:
  242. ret = append(ret, fmt.Sprintf(`FloatApproxEqual(%s, %f)`+"\n",
  243. ekey, val))
  244. default:
  245. log.Warningf("unknown type '%T' for key '%s'", val, ekey)
  246. }
  247. return ret
  248. }