explain.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. log "github.com/sirupsen/logrus"
  10. "github.com/spf13/cobra"
  11. "github.com/crowdsecurity/crowdsec/pkg/hubtest"
  12. "github.com/crowdsecurity/crowdsec/pkg/types"
  13. )
  14. func runExplain(cmd *cobra.Command, args []string) error {
  15. flags := cmd.Flags()
  16. logFile, err := flags.GetString("file")
  17. if err != nil {
  18. return err
  19. }
  20. dsn, err := flags.GetString("dsn")
  21. if err != nil {
  22. return err
  23. }
  24. logLine, err := flags.GetString("log")
  25. if err != nil {
  26. return err
  27. }
  28. logType, err := flags.GetString("type")
  29. if err != nil {
  30. return err
  31. }
  32. opts := hubtest.DumpOpts{}
  33. opts.Details, err = flags.GetBool("verbose")
  34. if err != nil {
  35. return err
  36. }
  37. opts.SkipOk, err = flags.GetBool("failures")
  38. if err != nil {
  39. return err
  40. }
  41. opts.ShowNotOkParsers, err = flags.GetBool("only-successful-parsers")
  42. opts.ShowNotOkParsers = !opts.ShowNotOkParsers
  43. if err != nil {
  44. return err
  45. }
  46. crowdsec, err := flags.GetString("crowdsec")
  47. if err != nil {
  48. return err
  49. }
  50. fileInfo, _ := os.Stdin.Stat()
  51. if logType == "" || (logLine == "" && logFile == "" && dsn == "") {
  52. printHelp(cmd)
  53. fmt.Println()
  54. fmt.Printf("Please provide --type flag\n")
  55. os.Exit(1)
  56. }
  57. if logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
  58. return fmt.Errorf("the option -f - is intended to work with pipes")
  59. }
  60. var f *os.File
  61. dir := os.TempDir()
  62. tmpFile := ""
  63. // we create a temporary log file if a log line/stdin has been provided
  64. if logLine != "" || logFile == "-" {
  65. tmpFile = filepath.Join(dir, "cscli_test_tmp.log")
  66. f, err = os.Create(tmpFile)
  67. if err != nil {
  68. return err
  69. }
  70. if logLine != "" {
  71. _, err = f.WriteString(logLine)
  72. if err != nil {
  73. return err
  74. }
  75. } else if logFile == "-" {
  76. reader := bufio.NewReader(os.Stdin)
  77. errCount := 0
  78. for {
  79. input, err := reader.ReadBytes('\n')
  80. if err != nil && err == io.EOF {
  81. break
  82. }
  83. _, err = f.Write(input)
  84. if err != nil {
  85. errCount++
  86. }
  87. }
  88. if errCount > 0 {
  89. log.Warnf("Failed to write %d lines to tmp file", errCount)
  90. }
  91. }
  92. f.Close()
  93. // this is the file that was going to be read by crowdsec anyway
  94. logFile = tmpFile
  95. }
  96. if logFile != "" {
  97. absolutePath, err := filepath.Abs(logFile)
  98. if err != nil {
  99. return fmt.Errorf("unable to get absolute path of '%s', exiting", logFile)
  100. }
  101. dsn = fmt.Sprintf("file://%s", absolutePath)
  102. lineCount := types.GetLineCountForFile(absolutePath)
  103. if lineCount > 100 {
  104. log.Warnf("log file contains %d lines. This may take lot of resources.", lineCount)
  105. }
  106. }
  107. if dsn == "" {
  108. return fmt.Errorf("no acquisition (--file or --dsn) provided, can't run cscli test")
  109. }
  110. cmdArgs := []string{"-c", ConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", "./", "-no-api"}
  111. crowdsecCmd := exec.Command(crowdsec, cmdArgs...)
  112. crowdsecCmd.Dir = dir
  113. output, err := crowdsecCmd.CombinedOutput()
  114. if err != nil {
  115. fmt.Println(string(output))
  116. return fmt.Errorf("fail to run crowdsec for test: %v", err)
  117. }
  118. // rm the temporary log file if only a log line/stdin was provided
  119. if tmpFile != "" {
  120. if err := os.Remove(tmpFile); err != nil {
  121. return fmt.Errorf("unable to remove tmp log file '%s': %+v", tmpFile, err)
  122. }
  123. }
  124. parserDumpFile := filepath.Join(dir, hubtest.ParserResultFileName)
  125. bucketStateDumpFile := filepath.Join(dir, hubtest.BucketPourResultFileName)
  126. parserDump, err := hubtest.LoadParserDump(parserDumpFile)
  127. if err != nil {
  128. return fmt.Errorf("unable to load parser dump result: %s", err)
  129. }
  130. bucketStateDump, err := hubtest.LoadBucketPourDump(bucketStateDumpFile)
  131. if err != nil {
  132. return fmt.Errorf("unable to load bucket dump result: %s", err)
  133. }
  134. hubtest.DumpTree(*parserDump, *bucketStateDump, opts)
  135. return nil
  136. }
  137. func NewExplainCmd() *cobra.Command {
  138. cmdExplain := &cobra.Command{
  139. Use: "explain",
  140. Short: "Explain log pipeline",
  141. Long: `
  142. Explain log pipeline
  143. `,
  144. Example: `
  145. cscli explain --file ./myfile.log --type nginx
  146. cscli explain --log "Sep 19 18:33:22 scw-d95986 sshd[24347]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=1.2.3.4" --type syslog
  147. cscli explain --dsn "file://myfile.log" --type nginx
  148. tail -n 5 myfile.log | cscli explain --type nginx -f -
  149. `,
  150. Args: cobra.ExactArgs(0),
  151. DisableAutoGenTag: true,
  152. RunE: runExplain,
  153. }
  154. flags := cmdExplain.Flags()
  155. flags.StringP("file", "f", "", "Log file to test")
  156. flags.StringP("dsn", "d", "", "DSN to test")
  157. flags.StringP("log", "l", "", "Log line to test")
  158. flags.StringP("type", "t", "", "Type of the acquisition to test")
  159. flags.BoolP("verbose", "v", false, "Display individual changes")
  160. flags.Bool("failures", false, "Only show failed lines")
  161. flags.Bool("only-successful-parsers", false, "Only show successful parsers")
  162. flags.String("crowdsec", "crowdsec", "Path to crowdsec")
  163. return cmdExplain
  164. }