File: explain.go

package info (click to toggle)
crowdsec 1.4.6-10.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 18,500 kB
  • sloc: sh: 2,870; makefile: 386; python: 74
file content (149 lines) | stat: -rw-r--r-- 4,468 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/crowdsecurity/crowdsec/pkg/hubtest"
	"github.com/crowdsecurity/crowdsec/pkg/types"
)

func NewExplainCmd() *cobra.Command {
	/* ---- HUB COMMAND */
	var logFile string
	var dsn string
	var logLine string
	var logType string
	var opts hubtest.DumpOpts
	var err error

	var cmdExplain = &cobra.Command{
		Use:   "explain",
		Short: "Explain log pipeline",
		Long: `
Explain log pipeline 
		`,
		Example: `
cscli explain --file ./myfile.log --type nginx 
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
cscli explain --dsn "file://myfile.log" --type nginx
tail -n 5 myfile.log | cscli explain --type nginx -f -
		`,
		Args:              cobra.ExactArgs(0),
		DisableAutoGenTag: true,
		Run: func(cmd *cobra.Command, args []string) {
			fileInfo, _ := os.Stdin.Stat()

			if logType == "" || (logLine == "" && logFile == "" && dsn == "") {
				printHelp(cmd)
				fmt.Println()
				fmt.Printf("Please provide --type flag\n")
				os.Exit(1)
			}

			if logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
				log.Fatal("-f - is intended to work with pipes.")
			}

			var f *os.File
			dir := os.TempDir()

			tmpFile := ""
			// we create a  temporary log file if a log line/stdin has been provided
			if logLine != "" || logFile == "-" {
				tmpFile = filepath.Join(dir, "cscli_test_tmp.log")
				f, err = os.Create(tmpFile)
				if err != nil {
					log.Fatal(err)
				}

				if logLine != "" {
					_, err = f.WriteString(logLine)
					if err != nil {
						log.Fatal(err)
					}
				} else if logFile == "-" {
					reader := bufio.NewReader(os.Stdin)
					errCount := 0
					for {
						input, err := reader.ReadBytes('\n')
						if err != nil && err == io.EOF {
							break
						}
						_, err = f.Write(input)
						if err != nil {
							errCount++
						}
					}
					if errCount > 0 {
						log.Warnf("Failed to write %d lines to tmp file", errCount)
					}
				}
				f.Close()
				//this is the file that was going to be read by crowdsec anyway
				logFile = tmpFile
			}

			if logFile != "" {
				absolutePath, err := filepath.Abs(logFile)
				if err != nil {
					log.Fatalf("unable to get absolute path of '%s', exiting", logFile)
				}
				dsn = fmt.Sprintf("file://%s", absolutePath)
				lineCount := types.GetLineCountForFile(absolutePath)
				if lineCount > 100 {
					log.Warnf("log file contains %d lines. This may take lot of resources.", lineCount)
				}
			}

			if dsn == "" {
				log.Fatal("no acquisition (--file or --dsn) provided, can't run cscli test.")
			}

			cmdArgs := []string{"-c", ConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", "./", "-no-api"}
			crowdsecCmd := exec.Command("crowdsec", cmdArgs...)
			crowdsecCmd.Dir = dir
			output, err := crowdsecCmd.CombinedOutput()
			if err != nil {
				fmt.Println(string(output))
				log.Fatalf("fail to run crowdsec for test: %v", err)
			}

			// rm the temporary log file if only a log line/stdin was provided
			if tmpFile != "" {
				if err := os.Remove(tmpFile); err != nil {
					log.Fatalf("unable to remove tmp log file '%s': %+v", tmpFile, err)
				}
			}
			parserDumpFile := filepath.Join(dir, hubtest.ParserResultFileName)
			bucketStateDumpFile := filepath.Join(dir, hubtest.BucketPourResultFileName)

			parserDump, err := hubtest.LoadParserDump(parserDumpFile)
			if err != nil {
				log.Fatalf("unable to load parser dump result: %s", err)
			}

			bucketStateDump, err := hubtest.LoadBucketPourDump(bucketStateDumpFile)
			if err != nil {
				log.Fatalf("unable to load bucket dump result: %s", err)
			}

			hubtest.DumpTree(*parserDump, *bucketStateDump, opts)
		},
	}
	cmdExplain.PersistentFlags().StringVarP(&logFile, "file", "f", "", "Log file to test")
	cmdExplain.PersistentFlags().StringVarP(&dsn, "dsn", "d", "", "DSN to test")
	cmdExplain.PersistentFlags().StringVarP(&logLine, "log", "l", "", "Log line to test")
	cmdExplain.PersistentFlags().StringVarP(&logType, "type", "t", "", "Type of the acquisition to test")
	cmdExplain.PersistentFlags().BoolVarP(&opts.Details, "verbose", "v", false, "Display individual changes")
	cmdExplain.PersistentFlags().BoolVar(&opts.SkipOk, "failures", false, "Only show failed lines")

	return cmdExplain
}