Skip to content

Commit 9662e5f

Browse files
authored
cscli hubtest: better report docker/nuclei errors (#4052)
* cscli hubtest: better report docker/nuclei errors * lint (print, space)
1 parent ecafe1d commit 9662e5f

File tree

11 files changed

+79
-40
lines changed

11 files changed

+79
-40
lines changed

cmd/crowdsec-cli/clihubtest/clean.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package clihubtest
33
import (
44
"errors"
55
"fmt"
6+
"os"
67

78
"github.com/spf13/cobra"
89

@@ -21,7 +22,7 @@ func (*cliHubTest) newCleanCmd() *cobra.Command {
2122
return errors.New("please provide test to run or --all flag")
2223
}
2324

24-
fmt.Println("Cleaning test data...")
25+
fmt.Fprintln(os.Stdout, "Cleaning test data...")
2526

2627
tests := []*hubtest.HubTestItem{}
2728

@@ -37,6 +38,7 @@ func (*cliHubTest) newCleanCmd() *cobra.Command {
3738
if err != nil {
3839
return fmt.Errorf("unable to load test '%s': %w", testName, err)
3940
}
41+
4042
tests = append(tests, test)
4143
}
4244
}

cmd/crowdsec-cli/clihubtest/coverage.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"math"
8+
"os"
89

910
"github.com/fatih/color"
1011
"github.com/spf13/cobra"
@@ -76,11 +77,11 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp
7677
if showOnlyPercent {
7778
switch {
7879
case showParserCov:
79-
fmt.Printf("parsers=%d%%", parserCoveragePercent)
80+
fmt.Fprintf(os.Stdout, "parsers=%d%%", parserCoveragePercent)
8081
case showScenarioCov:
81-
fmt.Printf("scenarios=%d%%", scenarioCoveragePercent)
82+
fmt.Fprintf(os.Stdout, "scenarios=%d%%", scenarioCoveragePercent)
8283
case showAppsecCov:
83-
fmt.Printf("appsec_rules=%d%%", appsecRuleCoveragePercent)
84+
fmt.Fprintf(os.Stdout, "appsec_rules=%d%%", appsecRuleCoveragePercent)
8485
}
8586

8687
return nil
@@ -100,40 +101,40 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp
100101
hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Appsec Rule", "Status", "Number of tests"}, parserCoverage)
101102
}
102103

103-
fmt.Println()
104+
fmt.Fprintln(os.Stdout)
104105

105106
if showParserCov {
106-
fmt.Printf("PARSERS : %d%% of coverage\n", parserCoveragePercent)
107+
fmt.Fprintf(os.Stdout, "PARSERS : %d%% of coverage\n", parserCoveragePercent)
107108
}
108109

109110
if showScenarioCov {
110-
fmt.Printf("SCENARIOS : %d%% of coverage\n", scenarioCoveragePercent)
111+
fmt.Fprintf(os.Stdout, "SCENARIOS : %d%% of coverage\n", scenarioCoveragePercent)
111112
}
112113

113114
if showAppsecCov {
114-
fmt.Printf("APPSEC RULES : %d%% of coverage\n", appsecRuleCoveragePercent)
115+
fmt.Fprintf(os.Stdout, "APPSEC RULES : %d%% of coverage\n", appsecRuleCoveragePercent)
115116
}
116117
case "json":
117118
dump, err := json.MarshalIndent(parserCoverage, "", " ")
118119
if err != nil {
119120
return err
120121
}
121122

122-
fmt.Printf("%s", dump)
123+
fmt.Fprint(os.Stdout, string(dump))
123124

124125
dump, err = json.MarshalIndent(scenarioCoverage, "", " ")
125126
if err != nil {
126127
return err
127128
}
128129

129-
fmt.Printf("%s", dump)
130+
fmt.Fprint(os.Stdout, string(dump))
130131

131132
dump, err = json.MarshalIndent(appsecRuleCoverage, "", " ")
132133
if err != nil {
133134
return err
134135
}
135136

136-
fmt.Printf("%s", dump)
137+
fmt.Fprint(os.Stdout, string(dump))
137138
default:
138139
return errors.New("only human/json output modes are supported")
139140
}

cmd/crowdsec-cli/clihubtest/create.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
3636
RunE: func(_ *cobra.Command, args []string) error {
3737
testName := args[0]
3838
testPath := filepath.Join(hubPtr.HubTestPath, testName)
39+
3940
if _, err := os.Stat(testPath); os.IsExist(err) {
4041
return fmt.Errorf("test '%s' already exists in '%s', exiting", testName, testPath)
4142
}
@@ -53,8 +54,8 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
5354
}
5455

5556
configFilePath := filepath.Join(testPath, "config.yaml")
56-
5757
configFileData := &hubtest.HubTestItemConfig{}
58+
5859
if logType == "appsec" {
5960
// create empty nuclei template file
6061
nucleiFileName := testName + ".yaml"
@@ -69,12 +70,16 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
6970
if ntpl == nil {
7071
return errors.New("unable to parse nuclei template")
7172
}
73+
7274
if err := ntpl.ExecuteTemplate(nucleiFile, "nuclei", struct{ TestName string }{TestName: testName}); err != nil {
7375
return fmt.Errorf("executing nuclei template %s: %w", nucleiFile.Name(), err)
7476
}
77+
7578
nucleiFile.Close()
79+
7680
configFileData.AppsecRules = []string{"./appsec-rules/<author>/your_rule_here.yaml"}
7781
configFileData.NucleiTemplate = nucleiFileName
82+
7883
fmt.Fprintln(os.Stdout)
7984
fmt.Fprintf(os.Stdout, " Test name : %s\n", testName)
8085
fmt.Fprintf(os.Stdout, " Test path : %s\n", testPath)
@@ -84,25 +89,32 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
8489
// create empty log file
8590
logFileName := testName + ".log"
8691
logFilePath := filepath.Join(testPath, logFileName)
92+
8793
logFile, err := os.Create(logFilePath)
8894
if err != nil {
8995
return err
9096
}
97+
9198
logFile.Close()
9299

93100
// create empty parser assertion file
94101
parserAssertFilePath := filepath.Join(testPath, hubtest.ParserAssertFileName)
102+
95103
parserAssertFile, err := os.Create(parserAssertFilePath)
96104
if err != nil {
97105
return err
98106
}
107+
99108
parserAssertFile.Close()
109+
100110
// create empty scenario assertion file
101111
scenarioAssertFilePath := filepath.Join(testPath, hubtest.ScenarioAssertFileName)
112+
102113
scenarioAssertFile, err := os.Create(scenarioAssertFilePath)
103114
if err != nil {
104115
return err
105116
}
117+
106118
scenarioAssertFile.Close()
107119

108120
parsers = append(parsers, "crowdsecurity/syslog-logs")
@@ -115,13 +127,15 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
115127
if len(postoverflows) == 0 {
116128
postoverflows = append(postoverflows, "")
117129
}
130+
118131
configFileData.Parsers = parsers
119132
configFileData.Scenarios = scenarios
120133
configFileData.PostOverflows = postoverflows
121134
configFileData.LogFile = logFileName
122135
configFileData.LogType = logType
123136
configFileData.IgnoreParsers = ignoreParsers
124137
configFileData.Labels = labels
138+
125139
fmt.Fprintln(os.Stdout)
126140
fmt.Fprintf(os.Stdout, " Test name : %s\n", testName)
127141
fmt.Fprintf(os.Stdout, " Test path : %s\n", testPath)
@@ -135,14 +149,17 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios
135149
if err != nil {
136150
return fmt.Errorf("open: %w", err)
137151
}
152+
138153
data, err := yaml.Marshal(configFileData)
139154
if err != nil {
140155
return fmt.Errorf("serialize: %w", err)
141156
}
157+
142158
_, err = fd.Write(data)
143159
if err != nil {
144160
return fmt.Errorf("write: %w", err)
145161
}
162+
146163
if err := fd.Close(); err != nil {
147164
return fmt.Errorf("close: %w", err)
148165
}

cmd/crowdsec-cli/clihubtest/eval.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package clihubtest
22

33
import (
44
"fmt"
5+
"os"
56

67
"github.com/spf13/cobra"
78

@@ -33,7 +34,7 @@ func (*cliHubTest) newEvalCmd() *cobra.Command {
3334
return err
3435
}
3536

36-
fmt.Print(output)
37+
fmt.Fprint(os.Stdout, output)
3738
}
3839

3940
return nil

cmd/crowdsec-cli/clihubtest/hubtest.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func (cli *cliHubTest) NewCommand() *cobra.Command {
4242
DisableAutoGenTag: true,
4343
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
4444
var err error
45+
4546
HubTest, err = hubtest.NewHubTest(hubPath, crowdsecPath, cscliPath, false)
4647
if err != nil {
4748
return fmt.Errorf("unable to load hubtest: %+v", err)

cmd/crowdsec-cli/clihubtest/info.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package clihubtest
22

33
import (
44
"fmt"
5+
"os"
56
"path/filepath"
67
"strings"
78

@@ -23,18 +24,21 @@ func (*cliHubTest) newInfoCmd() *cobra.Command {
2324
if err != nil {
2425
return fmt.Errorf("unable to load test '%s': %w", testName, err)
2526
}
26-
fmt.Println()
27-
fmt.Printf(" Test name : %s\n", test.Name)
28-
fmt.Printf(" Test path : %s\n", test.Path)
27+
28+
fmt.Fprintln(os.Stdout)
29+
fmt.Fprintf(os.Stdout, " Test name : %s\n", test.Name)
30+
fmt.Fprintf(os.Stdout, " Test path : %s\n", test.Path)
31+
2932
if isAppsecTest {
30-
fmt.Printf(" Nuclei Template : %s\n", test.Config.NucleiTemplate)
31-
fmt.Printf(" Appsec Rules : %s\n", strings.Join(test.Config.AppsecRules, ", "))
33+
fmt.Fprintf(os.Stdout, " Nuclei Template : %s\n", test.Config.NucleiTemplate)
34+
fmt.Fprintf(os.Stdout, " Appsec Rules : %s\n", strings.Join(test.Config.AppsecRules, ", "))
3235
} else {
33-
fmt.Printf(" Log file : %s\n", filepath.Join(test.Path, test.Config.LogFile))
34-
fmt.Printf(" Parser assertion file : %s\n", filepath.Join(test.Path, hubtest.ParserAssertFileName))
35-
fmt.Printf(" Scenario assertion file : %s\n", filepath.Join(test.Path, hubtest.ScenarioAssertFileName))
36+
fmt.Fprintf(os.Stdout, " Log file : %s\n", filepath.Join(test.Path, test.Config.LogFile))
37+
fmt.Fprintf(os.Stdout, " Parser assertion file : %s\n", filepath.Join(test.Path, hubtest.ParserAssertFileName))
38+
fmt.Fprintf(os.Stdout, " Scenario assertion file : %s\n", filepath.Join(test.Path, hubtest.ScenarioAssertFileName))
3639
}
37-
fmt.Printf(" Configuration File : %s\n", filepath.Join(test.Path, "config.yaml"))
40+
41+
fmt.Fprintf(os.Stdout, " Configuration File : %s\n", filepath.Join(test.Path, "config.yaml"))
3842
}
3943

4044
return nil

cmd/crowdsec-cli/clihubtest/list.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"os"
78

89
"github.com/fatih/color"
910
"github.com/spf13/cobra"
@@ -32,7 +33,8 @@ func (cli *cliHubTest) newListCmd() *cobra.Command {
3233
if err != nil {
3334
return err
3435
}
35-
fmt.Println(string(j))
36+
37+
fmt.Fprintln(os.Stdout, string(j))
3638
default:
3739
return errors.New("only human/json output modes are supported")
3840
}

cmd/crowdsec-cli/clihubtest/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func (cli *cliHubTest) run(ctx context.Context, all bool, nucleiTargetHost strin
4747
eg, gctx := errgroup.WithContext(ctx)
4848

4949
if isAppsecTest {
50-
log.Info("Appsec tests can not run in parallel: setting max_jobs=1")
50+
fmt.Fprintln(os.Stdout, "Appsec tests can not run in parallel: setting max_jobs=1")
5151

5252
maxJobs = 1
5353
}

cmd/crowdsec-cli/clihubtest/table.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package clihubtest
33
import (
44
"fmt"
55
"io"
6+
"os"
67
"strconv"
78

89
"github.com/jedib0t/go-pretty/v6/text"
@@ -35,7 +36,7 @@ func hubTestResultTable(out io.Writer, wantColor string, testMap map[string]*hub
3536
if showTable {
3637
t.Render()
3738
} else {
38-
fmt.Println("All tests passed, use --report-success for more details.")
39+
fmt.Fprintln(os.Stdout, "All tests passed, use --report-success for more details.")
3940
}
4041
}
4142

pkg/hubtest/nucleirunner.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ func (nc *NucleiConfig) RunNucleiTemplate(ctx context.Context, testName string,
2929
outputPrefix := fmt.Sprintf("%s/%s-%d", nc.OutputDir, testName, tstamp)
3030
// CVE-2023-34362_CVE-2023-34362-1702562399_stderr.txt
3131
args := []string{
32+
// removing the banner with --silent also removes useful [WRN] lines, so it is what it is
33+
// "-silent",
3234
"-u", target,
3335
"-t", templatePath,
3436
"-o", outputPrefix + ".json",
@@ -46,25 +48,33 @@ func (nc *NucleiConfig) RunNucleiTemplate(ctx context.Context, testName string,
4648
cmd.Stdout = &out
4749
cmd.Stderr = &outErr
4850

49-
err := cmd.Run()
51+
cmdErr := cmd.Run()
5052
if err := os.WriteFile(outputPrefix+"_stdout.txt", out.Bytes(), 0o644); err != nil {
51-
log.Warningf("Error writing stdout: %s", err)
53+
log.Errorf("Error writing stdout: %s", err)
5254
}
5355

54-
if err := os.WriteFile(outputPrefix+"_stderr.txt", outErr.Bytes(), 0o644); err != nil {
55-
log.Warningf("Error writing stderr: %s", err)
56+
errBytes := outErr.Bytes()
57+
58+
if err := os.WriteFile(outputPrefix+"_stderr.txt", errBytes, 0o644); err != nil {
59+
log.Errorf("Error writing stderr: %s", err)
5660
}
5761

58-
if err != nil {
59-
log.Warningf("Stdout saved to %s", outputPrefix+"_stdout.txt")
60-
log.Warningf("Stderr saved to %s", outputPrefix+"_stderr.txt")
61-
log.Warningf("Nuclei generated output saved to %s", outputPrefix+".json")
62+
if cmdErr != nil {
63+
// display stderr in addition to writing to a file
64+
os.Stdout.Write(errBytes)
65+
os.Stdout.WriteString("\n")
66+
67+
fmt.Fprintln(os.Stdout, "Stdout saved to", outputPrefix+"_stdout.txt")
68+
fmt.Fprintln(os.Stdout, "Stderr saved to", outputPrefix+"_stderr.txt")
69+
fmt.Fprintln(os.Stdout, "Nuclei generated output saved to", outputPrefix+".json")
70+
71+
return fmt.Errorf("%w: %v", ErrNucleiRunFail, cmdErr)
72+
}
6273

63-
return fmt.Errorf("%w: %v", ErrNucleiRunFail, err)
64-
} else if out.String() == "" {
65-
log.Warningf("Stdout saved to %s", outputPrefix+"_stdout.txt")
66-
log.Warningf("Stderr saved to %s", outputPrefix+"_stderr.txt")
67-
log.Warningf("Nuclei generated output saved to %s", outputPrefix+".json")
74+
if out.String() == "" {
75+
fmt.Fprintln(os.Stdout, "Stdout saved to", outputPrefix+"_stdout.txt")
76+
fmt.Fprintln(os.Stdout, "Stderr saved to", outputPrefix+"_stderr.txt")
77+
fmt.Fprintln(os.Stdout, "Nuclei generated output saved to", outputPrefix+".json")
6878

6979
// No stdout means no finding, it means our test failed
7080
return ErrNucleiTemplateFail

0 commit comments

Comments
 (0)