explain.go 4.4 KB

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