scenario_assert.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package cstest
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "regexp"
  8. "sort"
  9. "strings"
  10. "github.com/antonmedv/expr"
  11. "github.com/antonmedv/expr/vm"
  12. "github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
  13. "github.com/crowdsecurity/crowdsec/pkg/types"
  14. "github.com/pkg/errors"
  15. log "github.com/sirupsen/logrus"
  16. "gopkg.in/yaml.v2"
  17. )
  18. type ScenarioAssert struct {
  19. File string
  20. AutoGenAssert bool
  21. AutoGenAssertData string
  22. NbAssert int
  23. Fails []AssertFail
  24. Success bool
  25. TestData *BucketResults
  26. PourData *BucketPourInfo
  27. }
  28. type BucketResults []types.Event
  29. type BucketPourInfo map[string][]types.Event
  30. func NewScenarioAssert(file string) *ScenarioAssert {
  31. ScenarioAssert := &ScenarioAssert{
  32. File: file,
  33. NbAssert: 0,
  34. Success: false,
  35. Fails: make([]AssertFail, 0),
  36. AutoGenAssert: false,
  37. TestData: &BucketResults{},
  38. PourData: &BucketPourInfo{},
  39. }
  40. return ScenarioAssert
  41. }
  42. func (s *ScenarioAssert) AutoGenFromFile(filename string) (string, error) {
  43. err := s.LoadTest(filename, "")
  44. if err != nil {
  45. return "", err
  46. }
  47. ret := s.AutoGenScenarioAssert()
  48. return ret, nil
  49. }
  50. func (s *ScenarioAssert) LoadTest(filename string, bucketpour string) error {
  51. var err error
  52. bucketDump, err := LoadScenarioDump(filename)
  53. if err != nil {
  54. return fmt.Errorf("loading scenario dump file '%s': %+v", filename, err)
  55. }
  56. s.TestData = bucketDump
  57. if bucketpour != "" {
  58. pourDump, err := LoadBucketPourDump(bucketpour)
  59. if err != nil {
  60. return fmt.Errorf("loading bucket pour dump file '%s': %+v", filename, err)
  61. }
  62. s.PourData = pourDump
  63. }
  64. return nil
  65. }
  66. func (s *ScenarioAssert) AssertFile(testFile string) error {
  67. file, err := os.Open(s.File)
  68. if err != nil {
  69. return fmt.Errorf("failed to open")
  70. }
  71. if err := s.LoadTest(testFile, ""); err != nil {
  72. return fmt.Errorf("unable to load parser dump file '%s': %s", testFile, err)
  73. }
  74. scanner := bufio.NewScanner(file)
  75. scanner.Split(bufio.ScanLines)
  76. nbLine := 0
  77. for scanner.Scan() {
  78. nbLine += 1
  79. if scanner.Text() == "" {
  80. continue
  81. }
  82. ok, err := s.Run(scanner.Text())
  83. if err != nil {
  84. return fmt.Errorf("unable to run assert '%s': %+v", scanner.Text(), err)
  85. }
  86. s.NbAssert += 1
  87. if !ok {
  88. log.Debugf("%s is FALSE", scanner.Text())
  89. failedAssert := &AssertFail{
  90. File: s.File,
  91. Line: nbLine,
  92. Expression: scanner.Text(),
  93. Debug: make(map[string]string),
  94. }
  95. variableRE := regexp.MustCompile(`(?P<variable>[^ ]+) == .*`)
  96. match := variableRE.FindStringSubmatch(scanner.Text())
  97. if len(match) == 0 {
  98. log.Infof("Couldn't get variable of line '%s'", scanner.Text())
  99. continue
  100. }
  101. variable := match[1]
  102. result, err := s.EvalExpression(variable)
  103. if err != nil {
  104. log.Errorf("unable to evaluate variable '%s': %s", variable, err)
  105. continue
  106. }
  107. failedAssert.Debug[variable] = result
  108. s.Fails = append(s.Fails, *failedAssert)
  109. continue
  110. }
  111. //fmt.Printf(" %s '%s'\n", emoji.GreenSquare, scanner.Text())
  112. }
  113. file.Close()
  114. if s.NbAssert == 0 {
  115. assertData, err := s.AutoGenFromFile(testFile)
  116. if err != nil {
  117. return fmt.Errorf("couldn't generate assertion: %s", err.Error())
  118. }
  119. s.AutoGenAssertData = assertData
  120. s.AutoGenAssert = true
  121. }
  122. if len(s.Fails) == 0 {
  123. s.Success = true
  124. }
  125. return nil
  126. }
  127. func (s *ScenarioAssert) RunExpression(expression string) (interface{}, error) {
  128. var err error
  129. //debug doesn't make much sense with the ability to evaluate "on the fly"
  130. //var debugFilter *exprhelpers.ExprDebugger
  131. var runtimeFilter *vm.Program
  132. var output interface{}
  133. env := map[string]interface{}{"results": *s.TestData}
  134. if runtimeFilter, err = expr.Compile(expression, expr.Env(exprhelpers.GetExprEnv(env))); err != nil {
  135. return output, err
  136. }
  137. // if debugFilter, err = exprhelpers.NewDebugger(assert, expr.Env(exprhelpers.GetExprEnv(env))); err != nil {
  138. // log.Warningf("Failed building debugher for %s : %s", assert, err)
  139. // }
  140. //dump opcode in trace level
  141. log.Tracef("%s", runtimeFilter.Disassemble())
  142. output, err = expr.Run(runtimeFilter, exprhelpers.GetExprEnv(map[string]interface{}{"results": *s.TestData}))
  143. if err != nil {
  144. log.Warningf("running : %s", expression)
  145. log.Warningf("runtime error : %s", err)
  146. return output, errors.Wrapf(err, "while running expression %s", expression)
  147. }
  148. return output, nil
  149. }
  150. func (s *ScenarioAssert) EvalExpression(expression string) (string, error) {
  151. output, err := s.RunExpression(expression)
  152. if err != nil {
  153. return "", err
  154. }
  155. ret, err := yaml.Marshal(output)
  156. if err != nil {
  157. return "", err
  158. }
  159. return string(ret), nil
  160. }
  161. func (s *ScenarioAssert) Run(assert string) (bool, error) {
  162. output, err := s.RunExpression(assert)
  163. if err != nil {
  164. return false, err
  165. }
  166. switch out := output.(type) {
  167. case bool:
  168. return out, nil
  169. default:
  170. return false, fmt.Errorf("assertion '%s' is not a condition", assert)
  171. }
  172. }
  173. func (s *ScenarioAssert) AutoGenScenarioAssert() string {
  174. //attempt to autogen parser asserts
  175. var ret string
  176. ret += fmt.Sprintf(`len(results) == %d`+"\n", len(*s.TestData))
  177. for eventIndex, event := range *s.TestData {
  178. for ipSrc, source := range event.Overflow.Sources {
  179. ret += fmt.Sprintf(`"%s" in results[%d].Overflow.GetSources()`+"\n", ipSrc, eventIndex)
  180. ret += fmt.Sprintf(`results[%d].Overflow.Sources["%s"].IP == "%s"`+"\n", eventIndex, ipSrc, source.IP)
  181. ret += fmt.Sprintf(`results[%d].Overflow.Sources["%s"].Range == "%s"`+"\n", eventIndex, ipSrc, source.Range)
  182. ret += fmt.Sprintf(`results[%d].Overflow.Sources["%s"].GetScope() == "%s"`+"\n", eventIndex, ipSrc, *source.Scope)
  183. ret += fmt.Sprintf(`results[%d].Overflow.Sources["%s"].GetValue() == "%s"`+"\n", eventIndex, ipSrc, *source.Value)
  184. }
  185. for evtIndex, evt := range event.Overflow.Alert.Events {
  186. for _, meta := range evt.Meta {
  187. ret += fmt.Sprintf(`results[%d].Overflow.Alert.Events[%d].GetMeta("%s") == "%s"`+"\n", eventIndex, evtIndex, meta.Key, meta.Value)
  188. }
  189. }
  190. ret += fmt.Sprintf(`results[%d].Overflow.Alert.GetScenario() == "%s"`+"\n", eventIndex, *event.Overflow.Alert.Scenario)
  191. ret += fmt.Sprintf(`results[%d].Overflow.Alert.Remediation == %t`+"\n", eventIndex, *&event.Overflow.Alert.Remediation)
  192. ret += fmt.Sprintf(`results[%d].Overflow.Alert.GetEventsCount() == %d`+"\n", eventIndex, *event.Overflow.Alert.EventsCount)
  193. }
  194. return ret
  195. }
  196. func (b BucketResults) Len() int {
  197. return len(b)
  198. }
  199. func (b BucketResults) Less(i, j int) bool {
  200. return b[i].Overflow.Alert.GetScenario()+strings.Join(b[i].Overflow.GetSources(), "@") > b[j].Overflow.Alert.GetScenario()+strings.Join(b[j].Overflow.GetSources(), "@")
  201. }
  202. func (b BucketResults) Swap(i, j int) {
  203. b[i], b[j] = b[j], b[i]
  204. }
  205. func LoadBucketPourDump(filepath string) (*BucketPourInfo, error) {
  206. var bucketDump BucketPourInfo
  207. dumpData, err := os.Open(filepath)
  208. if err != nil {
  209. return nil, err
  210. }
  211. defer dumpData.Close()
  212. results, err := ioutil.ReadAll(dumpData)
  213. if err != nil {
  214. return nil, err
  215. }
  216. if err := yaml.Unmarshal(results, &bucketDump); err != nil {
  217. return nil, err
  218. }
  219. return &bucketDump, nil
  220. }
  221. func LoadScenarioDump(filepath string) (*BucketResults, error) {
  222. var bucketDump BucketResults
  223. dumpData, err := os.Open(filepath)
  224. if err != nil {
  225. return nil, err
  226. }
  227. defer dumpData.Close()
  228. results, err := ioutil.ReadAll(dumpData)
  229. if err != nil {
  230. return nil, err
  231. }
  232. if err := yaml.Unmarshal(results, &bucketDump); err != nil {
  233. return nil, err
  234. }
  235. sort.Sort(BucketResults(bucketDump))
  236. return &bucketDump, nil
  237. }