From 67dfca82e554ab618720ec3ed8fd6761a81d06c6 Mon Sep 17 00:00:00 2001 From: Christoph Hartmann Date: Mon, 22 Jan 2024 11:33:33 +0100 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=EF=B8=8F=20support=20writing=20report?= =?UTF-8?q?s=20to=20file=20(#176)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .goreleaser.yml | 2 +- .../components/provisioner/cnspec/README.md | 4 +- .../components/provisioner/mondoo/README.md | 4 +- README.md | 18 +++++++- .../provisioner/Config-not-required.mdx | 4 +- examples/packer-docker/docker-ubuntu.pkr.hcl | 2 + provisioner/provisioner.go | 44 +++++++++++-------- provisioner/provisioner.hcl2spec.go | 2 + 8 files changed, 56 insertions(+), 24 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index e07af5a..135bc0c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -7,7 +7,7 @@ env: before: hooks: - go mod download - - make ci-release-docs + - make generate # Check plugin compatibility with required version of the Packer SDK - make plugin-check builds: diff --git a/.web-docs/components/provisioner/cnspec/README.md b/.web-docs/components/provisioner/cnspec/README.md index f138b65..91f68b6 100644 --- a/.web-docs/components/provisioner/cnspec/README.md +++ b/.web-docs/components/provisioner/cnspec/README.md @@ -78,9 +78,11 @@ Optional Parameters: - `use_proxy` (bool) - Use proxy to connect to host to scan. This configuration will fall-back to packer proxy for cases where the provisioner cannot access the target directly -- `output` (string) - Set output format: summary, full, yaml, json, csv, compact, report, junit +- `output` (string) - Set output format: compact, csv, full, json, junit, report, summary, yaml (default "compact") +- `output_target` (string) - Set output target. E.g. path to local file + - `score_threshold` (int) - An integer value to set the `score_threshold` of mondoo scans. Defaults to `0` which results in a passing score regardless of what scan results are returned. diff --git a/.web-docs/components/provisioner/mondoo/README.md b/.web-docs/components/provisioner/mondoo/README.md index 26db037..370680f 100644 --- a/.web-docs/components/provisioner/mondoo/README.md +++ b/.web-docs/components/provisioner/mondoo/README.md @@ -92,9 +92,11 @@ Optional Parameters: - `use_proxy` (bool) - Use proxy to connect to host to scan. This configuration will fall-back to packer proxy for cases where the provisioner cannot access the target directly -- `output` (string) - Set output format: summary, full, yaml, json, csv, compact, report, junit +- `output` (string) - Set output format: compact, csv, full, json, junit, report, summary, yaml (default "compact") +- `output_target` (string) - Set output target. E.g. path to local file + - `score_threshold` (int) - An integer value to set the `score_threshold` of mondoo scans. Defaults to `0` which results in a passing score regardless of what scan results are returned. diff --git a/README.md b/README.md index ee02126..4eb4d82 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,15 @@ packer build amazon-linux-2.pkr.hcl | `on_failure` | Set `on_failure = "continue"` to ignore build failures that do not meet any set `score_threshold`. | `string` | None | No | | `score_threshold` | Set a score threshold for Packer builds `[0-100]`. Any scans that fall below the `score_threshold` will fail unless `on_failure = "continue"`. To learn more, read [How Mondoo scores policies](https://mondoo.com/docs/platform/console/monitor/#how-mondoo-scores-policies) in the Mondoo documentation. | `int` | None | No | | `sudo` | Use sudo to elevate permissions when running Mondoo scans. | `bool` | None | No | -| `mondoo_config_path` | The path to the configuration to be used when running Mondoo scans. | `string` | None | No | +| `mondoo_config_path` | The path to the Mondoo's service account. Defaults to `$HOME/.config/mondoo/mondoo.yml` | `string` | None | No | +| `output` | Set output format: compact, csv, full, json, junit, report, summary, yaml (default "compact") | `string` | None | No | +| `output_target` | Set output target. E.g. path to local file `result.xml` | `string` | None | No | ### Example: Complete Configuration -```bash +A simple configuration where we set a score threshold of 85 and use sudo to elevate permissions when running the scans: + +```hcl provisioner "cnspec" { on_failure = "continue" score_threshold = 85 @@ -96,6 +100,16 @@ provisioner "cnspec" { } ``` +The following configuration shows how to set the output format to JUnit and the output target to `test-results.xml`: + +```hcl +provisioner "cnspec" { + on_failure = "continue" + output = "junit" + output_target = "test-results.xml" +} +``` + ## Sample Packer Templates You can find example Packer templates in the [examples](/examples/) directory in this repository. You can also find a [GitHub Action workflow example](/examples/github-actions/packer-build-scan.yaml) of how to use cnspec to test builds as part of a CI/CD pipeline. diff --git a/docs-partials/provisioner/Config-not-required.mdx b/docs-partials/provisioner/Config-not-required.mdx index 65efb30..839819b 100644 --- a/docs-partials/provisioner/Config-not-required.mdx +++ b/docs-partials/provisioner/Config-not-required.mdx @@ -60,9 +60,11 @@ - `use_proxy` (bool) - Use proxy to connect to host to scan. This configuration will fall-back to packer proxy for cases where the provisioner cannot access the target directly -- `output` (string) - Set output format: summary, full, yaml, json, csv, compact, report, junit +- `output` (string) - Set output format: compact, csv, full, json, junit, report, summary, yaml (default "compact") +- `output_target` (string) - Set output target. E.g. path to local file + - `score_threshold` (int) - An integer value to set the `score_threshold` of mondoo scans. Defaults to `0` which results in a passing score regardless of what scan results are returned. diff --git a/examples/packer-docker/docker-ubuntu.pkr.hcl b/examples/packer-docker/docker-ubuntu.pkr.hcl index 8b7a3fa..a6403a7 100644 --- a/examples/packer-docker/docker-ubuntu.pkr.hcl +++ b/examples/packer-docker/docker-ubuntu.pkr.hcl @@ -45,5 +45,7 @@ build { annotations = { Name = "${var.image_prefix}-${local.timestamp}" } + output = "junit" + output_target = "test-results.xml" } } diff --git a/provisioner/provisioner.go b/provisioner/provisioner.go index 1361efc..0c4b843 100644 --- a/provisioner/provisioner.go +++ b/provisioner/provisioner.go @@ -104,9 +104,11 @@ type Config struct { // Use proxy to connect to host to scan. This configuration will fall-back to // packer proxy for cases where the provisioner cannot access the target directly UseProxy bool `mapstructure:"use_proxy"` - // Set output format: summary, full, yaml, json, csv, compact, report, junit + // Set output format: compact, csv, full, json, junit, report, summary, yaml // (default "compact") Output string `mapstructure:"output"` + // Set output target. E.g. path to local file + OutputTarget string `mapstructure:"output_target"` // An integer value to set the `score_threshold` of mondoo scans. Defaults to // `0` which results in a passing score regardless of what scan results are // returned. @@ -523,12 +525,12 @@ func (p *Provisioner) executeCnspec(ui packer.Ui, comm packer.Communicator) erro updateProviders(ui) - var result *scan.ScanResult + var res *scan.ScanResult var err error if p.config.Incognito { ui.Message("scan packer build in incognito mode") scanService := scan.NewLocalScanner() - result, err = scanService.RunIncognito(context.Background(), scanJob) + res, err = scanService.RunIncognito(context.Background(), scanJob) if err != nil { return err } @@ -555,33 +557,39 @@ func (p *Provisioner) executeCnspec(ui packer.Ui, comm packer.Communicator) erro ui.Message("scan packer build") scanService := scan.NewLocalScanner(scannerOpts...) - result, err = scanService.Run(context.Background(), scanJob) + res, err = scanService.Run(context.Background(), scanJob) if err != nil { ui.Error("scan failed: " + err.Error()) return err } } + report := res.GetFull() ui.Message("scan completed successfully") // render terminal output + handlerConf := reporter.HandlerConfig{ + Format: p.config.Output, + OutputTarget: p.config.OutputTarget, + Incognito: p.config.Incognito, + } + outputHandler, err := reporter.NewOutputHandler(handlerConf) + if err != nil { + ui.Error("failed to create an output handler: " + err.Error()) + } + buf := &bytes.Buffer{} - output := p.config.Output - format := reporter.Formats[output] - r := reporter.NewReporter(format, p.config.Incognito).WithOutput(buf) + if x, ok := outputHandler.(*reporter.Reporter); ok { + x.WithOutput(buf) + } - fullReport := result.GetFull() - if fullReport == nil { - rErr := errors.New("could not gather the full report") - ui.Error(rErr.Error()) - return rErr + if err := outputHandler.WriteReport(context.Background(), report); err != nil { + ui.Error("failed to write report to output target: " + err.Error()) } - err = r.WriteReport(context.Background(), fullReport) - if err != nil { - return err + if buf.Len() > 0 { + ui.Message(buf.String()) } - ui.Message(buf.String()) // default is to pass all controls scoreThreshold := 100 @@ -593,8 +601,8 @@ func (p *Provisioner) executeCnspec(ui packer.Ui, comm packer.Communicator) erro scoreThreshold = p.config.ScoreThreshold } - if fullReport.GetWorstScore() < uint32(scoreThreshold) { - return fmt.Errorf("scan has completed with %d score, does not pass score threshold %d", fullReport.GetWorstScore(), scoreThreshold) + if report.GetWorstScore() < uint32(scoreThreshold) { + return fmt.Errorf("scan has completed with %d score, does not pass score threshold %d", report.GetWorstScore(), scoreThreshold) } return nil diff --git a/provisioner/provisioner.hcl2spec.go b/provisioner/provisioner.hcl2spec.go index dff9ce9..073332f 100644 --- a/provisioner/provisioner.hcl2spec.go +++ b/provisioner/provisioner.hcl2spec.go @@ -37,6 +37,7 @@ type FlatConfig struct { WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` UseProxy *bool `mapstructure:"use_proxy" cty:"use_proxy" hcl:"use_proxy"` Output *string `mapstructure:"output" cty:"output" hcl:"output"` + OutputTarget *string `mapstructure:"output_target" cty:"output_target" hcl:"output_target"` ScoreThreshold *int `mapstructure:"score_threshold" cty:"score_threshold" hcl:"score_threshold"` MondooConfigPath *string `mapstructure:"mondoo_config_path" cty:"mondoo_config_path" hcl:"mondoo_config_path"` } @@ -80,6 +81,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, "use_proxy": &hcldec.AttrSpec{Name: "use_proxy", Type: cty.Bool, Required: false}, "output": &hcldec.AttrSpec{Name: "output", Type: cty.String, Required: false}, + "output_target": &hcldec.AttrSpec{Name: "output_target", Type: cty.String, Required: false}, "score_threshold": &hcldec.AttrSpec{Name: "score_threshold", Type: cty.Number, Required: false}, "mondoo_config_path": &hcldec.AttrSpec{Name: "mondoo_config_path", Type: cty.String, Required: false}, }