scenario_assert.go 7.3 KB

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