diff --git a/.github/actions/copacetic-action/action.yaml b/.github/actions/copacetic-action/action.yaml
index bb0aa07..9fdc4fd 100644
--- a/.github/actions/copacetic-action/action.yaml
+++ b/.github/actions/copacetic-action/action.yaml
@@ -9,9 +9,6 @@ inputs:
description: 'List of images to process separated by newline'
required: true
type: string
- # buildkit-version:
- # description: 'Buildkit version'
- # type: string
skip-upload:
description: 'Skip uploading to remote registry'
default: false
@@ -27,6 +24,11 @@ inputs:
default: 1h
required: false
type: string
+ report-cves:
+ description: 'Scan and report number of Critical and Highs'
+ default: false
+ required: false
+ type: boolean
outputs:
result:
description: "Patching result"
@@ -83,7 +85,7 @@ runs:
run: |
JSON_REPORT_PATH=$(mktemp)
echo "$JSON_REPORT" > $JSON_REPORT_PATH
- RESULT="$(devbox run -- go run main.go markdown $JSON_REPORT_PATH)"
+ RESULT="$(devbox run -- go run main.go markdown $JSON_REPORT_PATH) --print-cves=${{ inputs.report-cves }}"
echo "$RESULT" >> $GITHUB_STEP_SUMMARY
env:
JSON_REPORT: ${{ steps.execute-patch.outputs.result }}
diff --git a/.github/actions/copacetic-action/cmd/copa-action/report.go b/.github/actions/copacetic-action/cmd/copa-action/report.go
index 8ea1a6f..957728c 100644
--- a/.github/actions/copacetic-action/cmd/copa-action/report.go
+++ b/.github/actions/copacetic-action/cmd/copa-action/report.go
@@ -11,12 +11,14 @@ import (
"github.com/spf13/cobra"
)
+var printCVEs = false
+
func NewMarkdownCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "markdown PATH | -",
Short: "Generate markdown report from JSON output",
Args: cobra.ExactArgs(1),
- RunE: func(_ *cobra.Command, args []string) error {
+ RunE: func(cmd *cobra.Command, args []string) error {
input, err := cli.OpenFileOrStdin(args[0])
if err != nil {
return err
@@ -31,8 +33,9 @@ func NewMarkdownCmd() *cobra.Command {
return fmt.Errorf("failed to read JSON report: %w", err)
}
- return patch.WriteMarkdown(report, os.Stdout)
+ return patch.WriteMarkdown(cmd.Context(), report, os.Stdout, printCVEs)
},
}
+ cmd.Flags().BoolVar(&printCVEs, "print-cves", printCVEs, "enable scanning and printing number of Critical and High CVEs")
return cmd
}
diff --git a/.github/actions/copacetic-action/pkg/image/scan.go b/.github/actions/copacetic-action/pkg/image/scan.go
index f2fba5a..e4213a2 100644
--- a/.github/actions/copacetic-action/pkg/image/scan.go
+++ b/.github/actions/copacetic-action/pkg/image/scan.go
@@ -53,10 +53,17 @@ func (e *CmdErr) Error() string {
return e.Err.Error()
}
+var (
+ ScanFixableOS = []string{"--vuln-type", "os", "--ignore-unfixed"}
+ ScanAllOS = []string{"--vuln-type", "os"}
+)
+
// Scan runs a trivy scan of a image and returns back report.
-func Scan(ctx context.Context, imageName string) (*Report, error) {
+func Scan(ctx context.Context, imageName string, scanType []string) (*Report, error) {
+ flags := append([]string{"image"}, scanType...)
+ flags = append(flags, "--format", "json", imageName)
cmd, stdout, stderr := prepareCmd(
- ctx, "trivy", "image", "--vuln-type", "os", "--ignore-unfixed", "--format", "json", imageName,
+ ctx, "trivy", flags...,
)
err := cmd.Run()
if err != nil {
diff --git a/.github/actions/copacetic-action/pkg/patch/report.go b/.github/actions/copacetic-action/pkg/patch/report.go
index c78f835..a602495 100644
--- a/.github/actions/copacetic-action/pkg/patch/report.go
+++ b/.github/actions/copacetic-action/pkg/patch/report.go
@@ -1,6 +1,7 @@
package patch
import (
+ "context"
"encoding/json"
"errors"
"fmt"
@@ -44,7 +45,7 @@ func WriteJSON(tasks []*Task, w io.Writer) error {
return json.NewEncoder(w).Encode(r)
}
-func WriteMarkdown(report Report, w io.Writer) error {
+func WriteMarkdown(ctx context.Context, report Report, w io.Writer, printCVEs bool) error {
doc := md.NewMarkdown(w)
imagesTable := md.TableSet{
@@ -59,6 +60,11 @@ func WriteMarkdown(report Report, w io.Writer) error {
md.Code(row.Patched),
}
+ if printCVEs {
+ mdRow[0] = fmt.Sprintf("%s
%s", row.Image, scanImage(ctx, row.Image))
+ mdRow[1] = fmt.Sprintf("%s
%s", row.Patched, scanImage(ctx, row.Patched))
+ }
+
if row.Error != "" {
mdRow = append(mdRow, md.Link("View error", fmt.Sprintf("#error-%d", i)))
@@ -96,3 +102,26 @@ func WriteMarkdown(report Report, w io.Writer) error {
return doc.Build()
}
+
+func scanImage(ctx context.Context, imageRef string) string {
+ report, err := image.Scan(ctx, imageRef, image.ScanAllOS)
+ if err != nil {
+ return md.Code(err.Error())
+ }
+
+ counts := map[string]int{
+ "CRITICAL": 0,
+ "HIGH": 0,
+ }
+ for _, vuln := range report.Vulnerabilities() {
+ if _, ok := counts[vuln.Severity]; ok {
+ counts[vuln.Severity] = counts[vuln.Severity] + 1
+ }
+ }
+
+ parts := []string{}
+ for severity, count := range counts {
+ parts = append(parts, fmt.Sprintf("`%d` %s", count, md.Bold(severity)))
+ }
+ return strings.Join(parts, " ")
+}
diff --git a/.github/actions/copacetic-action/pkg/patch/task.go b/.github/actions/copacetic-action/pkg/patch/task.go
index aedbb71..3a3e4a4 100644
--- a/.github/actions/copacetic-action/pkg/patch/task.go
+++ b/.github/actions/copacetic-action/pkg/patch/task.go
@@ -50,7 +50,7 @@ func Run(ctx context.Context, imageRef string, reg registry.Registry, imageTagSu
// To avoid generating same patched image always scan the latest patched
// tag in the patch registry and only build image if there are available
// fixes that would change the latest patched version.
- report, err := image.Scan(ctx, imagePatch.Scanned)
+ report, err := image.Scan(ctx, imagePatch.Scanned, image.ScanFixableOS)
if err != nil {
return withErr(t, err), err
}
@@ -96,21 +96,17 @@ func Run(ctx context.Context, imageRef string, reg registry.Registry, imageTagSu
patchedRef := imagePatch.SourceRef().Context().Tag(buildTag)
logger.Info("regenerated image using copa", "patchedRef", patchedRef.String())
- patchedReport, err := image.Scan(ctx, patchedRef.String())
+ patchedReport, err := image.Scan(ctx, patchedRef.String(), image.ScanFixableOS)
if err != nil {
return withErr(t, err), err
}
- logger.Info(
- "patched vulnerability report",
- "original", report.Vulnerabilities(),
- "patched", patchedReport.Vulnerabilities(),
- )
if slices.Equal(
image.VulnerabilitiesIdsSorted(report.Vulnerabilities()),
image.VulnerabilitiesIdsSorted(patchedReport.Vulnerabilities()),
) {
- logger.Warn("no vulnerabilties were fixed by running copa",
+ logger.Warn(
+ "no vulnerabilties were fixed by running copa patch",
"scannedImage", imagePatch.Scanned,
"scanned", image.VulnerabilitiesIdsSorted(report.Vulnerabilities()),
"patched", image.VulnerabilitiesIdsSorted(patchedReport.Vulnerabilities()),
diff --git a/.github/workflows/cve-patch-images.yaml b/.github/workflows/cve-patch-images.yaml
index a05509c..67c5f1c 100644
--- a/.github/workflows/cve-patch-images.yaml
+++ b/.github/workflows/cve-patch-images.yaml
@@ -55,7 +55,6 @@ jobs:
with:
images: ${{ steps.save-images.outputs.images }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- debug: true
- skip-upload: true
timeout: 2h
+ report-cves: true