1
0
mirror of https://github.com/siderolabs/kres.git synced 2026-02-05 09:45:35 +01:00

feat: add govulncheck wrapper

This wrapper enables excluding vulnerabilities to pass scans, if we are unaffected.

Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
This commit is contained in:
Mateusz Urbanek
2025-08-14 11:23:43 +02:00
parent 9f63e23bda
commit 696c7c7796
10 changed files with 223 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2022-11-13T18:59:41Z by kres b4a6481.
# Generated on 2025-08-14T09:17:18Z by kres 9f63e23-dirty.
*
!cmd
@@ -10,3 +10,4 @@
!.golangci.yml
!README.md
!.markdownlint.json
!hack/govulncheck.sh

View File

@@ -2,7 +2,7 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2025-08-12T16:59:55Z by kres 79636f7-dirty.
# Generated on 2025-08-14T09:31:18Z by kres df7e867-dirty.
ARG TOOLCHAIN
@@ -20,7 +20,7 @@ RUN bunx markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ig
# base toolchain image
FROM --platform=${BUILDPLATFORM} ${TOOLCHAIN} AS toolchain
RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev
RUN apk --update --no-cache add bash build-base curl jq protoc protobuf-dev
# build tools
FROM --platform=${BUILDPLATFORM} toolchain AS tools
@@ -85,8 +85,9 @@ RUN --mount=type=cache,target=/root/.cache/go-build,id=kres/root/.cache/go-build
# runs govulncheck
FROM base AS lint-govulncheck
COPY --chmod=0755 hack/govulncheck.sh ./hack/govulncheck.sh
WORKDIR /src
RUN --mount=type=cache,target=/root/.cache/go-build,id=kres/root/.cache/go-build --mount=type=cache,target=/go/pkg,id=kres/go/pkg govulncheck ./...
RUN --mount=type=cache,target=/root/.cache/go-build,id=kres/root/.cache/go-build --mount=type=cache,target=/go/pkg,id=kres/go/pkg ./hack/govulncheck.sh ./...
# runs unit-tests with race detector
FROM base AS unit-tests-race

View File

@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2025-08-12T16:47:33Z by kres 79636f7-dirty.
# Generated on 2025-08-14T08:53:14Z by kres 9f63e23-dirty.
# common variables
@@ -169,7 +169,7 @@ generate: ## Generate .proto definitions.
lint-golangci-lint: ## Runs golangci-lint linter.
@$(MAKE) target-$@
lint-golangci-lint-fmt: ## Runs golangci-lint formatter.
lint-golangci-lint-fmt: ## Runs golangci-lint formatter and tries to fix issues automatically.
@$(MAKE) local-$@ DEST=.
lint-gofumpt: ## Runs gofumpt linter.

86
hack/govulncheck.sh Normal file
View File

@@ -0,0 +1,86 @@
#!/bin/bash
# Source: https://github.com/tianon/gosu/blob/e157efb/govulncheck-with-excludes.sh
# Licensed under the Apache License, Version 2.0
# Copyright Tianon Gravi
set -Eeuo pipefail
exclude_arg=""
pass_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-exclude)
exclude_arg="$2"
shift 2
;;
*)
pass_args+=("$1")
shift
;;
esac
done
if [[ -n "$exclude_arg" ]]; then
excludeVulns="$(jq -nc --arg list "$exclude_arg" '$list | split(",")')"
else
excludeVulns="[]"
fi
export excludeVulns
# Debug print
echo "excludeVulns = $excludeVulns"
echo "Passing args: ${pass_args[*]}"
if ! command -v govulncheck > /dev/null; then
printf "govulncheck not installed"
exit 1
fi
if out="$(govulncheck "${pass_args[@]}")"; then
printf '%s\n' "$out"
exit 0
fi
json="$(govulncheck -json "${pass_args[@]}")"
vulns="$(jq <<<"$json" -cs '
(
map(
.osv // empty
| { key: .id, value: . }
)
| from_entries
) as $meta
# https://github.com/tianon/gosu/issues/144
| map(
.finding // empty
# https://github.com/golang/vuln/blob/3740f5cb12a3f93b18dbe200c4bcb6256f8586e2/internal/scan/template.go#L97-L104
| select((.trace[0].function // "") != "")
| .osv
)
| unique
| map($meta[.])
')"
if [ "$(jq <<<"$vulns" -r 'length')" -le 0 ]; then
printf '%s\n' "$out"
exit 1
fi
filtered="$(jq <<<"$vulns" -c '
(env.excludeVulns | fromjson) as $exclude
| map(select(
.id as $id
| $exclude | index($id) | not
))
')"
text="$(jq <<<"$filtered" -r 'map("- \(.id) (aka \(.aliases | join(", ")))\n\n\t\(.details | gsub("\n"; "\n\t"))") | join("\n\n")')"
if [ -z "$text" ]; then
printf 'No vulnerabilities found.\n'
exit 0
else
printf '%s\n' "$text"
exit 1
fi

View File

@@ -15,6 +15,7 @@ type CopyStep struct {
platform string
src string
dst string
chmod string
}
// Copy creates new CopyStep.
@@ -42,6 +43,13 @@ func (step *CopyStep) Platform(platform string) *CopyStep {
return step
}
// Platform sets --chmod argument.
func (step *CopyStep) Chmod(perm uint) *CopyStep {
step.chmod = fmt.Sprintf("%#o", perm)
return step
}
// Depends implements StageDependencies.
func (step *CopyStep) Depends() []string {
if step.from == "" {
@@ -62,6 +70,10 @@ func (step *CopyStep) Generate(w io.Writer) error {
fromClause = fmt.Sprintf("--platform=%s %s", step.platform, fromClause)
}
if step.chmod != "" {
fromClause = fmt.Sprintf("--chmod=%s %s", step.chmod, fromClause)
}
_, err := fmt.Fprintf(w, "COPY %s%s %s\n", fromClause, step.src, step.dst)
return err

View File

@@ -400,6 +400,10 @@ func (generate *Generate) CompileTemplates(output *template.Output) error {
WithLicenseText(generate.LicenseText).
NoOverwrite()
output.Define(govulncheckPath, templates.GoVulnCheck).
NoPreamble().
NoOverwrite()
return nil
}

View File

@@ -5,20 +5,26 @@
package golang
import (
"fmt"
"path/filepath"
"strings"
"github.com/siderolabs/kres/internal/dag"
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/dockerfile/step"
"github.com/siderolabs/kres/internal/output/dockerignore"
"github.com/siderolabs/kres/internal/output/makefile"
"github.com/siderolabs/kres/internal/project/meta"
)
const govulncheckPath = "hack/govulncheck.sh"
// GoVulnCheck provides GoVulnCheck linter.
type GoVulnCheck struct { //nolint:govet
dag.BaseNode
Disabled bool `yaml:"disabled"`
Disabled bool `yaml:"disabled"`
Ignore []string `yaml:"ignore,omitempty"`
meta *meta.Options
projectPath string
@@ -34,6 +40,13 @@ func NewGoVulnCheck(meta *meta.Options, projectPath string) *GoVulnCheck {
}
}
// CompileDockerignore implements dockerignore.Compiler.
func (lint *GoVulnCheck) CompileDockerignore(output *dockerignore.Output) error {
output.AllowLocalPath(govulncheckPath)
return nil
}
// CompileMakefile implements makefile.Compiler.
func (lint *GoVulnCheck) CompileMakefile(output *makefile.Output) error {
if lint.Disabled {
@@ -54,12 +67,18 @@ func (lint *GoVulnCheck) CompileDockerfile(output *dockerfile.Output) error {
return nil
}
script := "./hack/govulncheck.sh ./..."
if len(lint.Ignore) != 0 {
script = fmt.Sprintf("./hack/govulncheck.sh -exclude '%s' ./...", strings.Join(lint.Ignore, ","))
}
output.Stage(lint.Name()).
Description("runs govulncheck").
From("base").
Step(step.Copy(govulncheckPath, "./hack/govulncheck.sh").Chmod(0o755)).
Step(step.WorkDir(filepath.Join("/src", lint.projectPath))).
Step(step.Script(
`govulncheck ./...`,
script,
).
MountCache(filepath.Join(lint.meta.CachePath, "go-build"), lint.meta.GitHubRepository).
MountCache(filepath.Join(lint.meta.GoPath, "pkg"), lint.meta.GitHubRepository),

View File

@@ -0,0 +1,86 @@
#!/bin/bash
# Source: https://github.com/tianon/gosu/blob/e157efb/govulncheck-with-excludes.sh
# Licensed under the Apache License, Version 2.0
# Copyright Tianon Gravi
set -Eeuo pipefail
exclude_arg=""
pass_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-exclude)
exclude_arg="$2"
shift 2
;;
*)
pass_args+=("$1")
shift
;;
esac
done
if [[ -n "$exclude_arg" ]]; then
excludeVulns="$(jq -nc --arg list "$exclude_arg" '$list | split(",")')"
else
excludeVulns="[]"
fi
export excludeVulns
# Debug print
echo "excludeVulns = $excludeVulns"
echo "Passing args: ${pass_args[*]}"
if ! command -v govulncheck > /dev/null; then
printf "govulncheck not installed"
exit 1
fi
if out="$(govulncheck "${pass_args[@]}")"; then
printf '%s\n' "$out"
exit 0
fi
json="$(govulncheck -json "${pass_args[@]}")"
vulns="$(jq <<<"$json" -cs '
(
map(
.osv // empty
| { key: .id, value: . }
)
| from_entries
) as $meta
# https://github.com/tianon/gosu/issues/144
| map(
.finding // empty
# https://github.com/golang/vuln/blob/3740f5cb12a3f93b18dbe200c4bcb6256f8586e2/internal/scan/template.go#L97-L104
| select((.trace[0].function // "") != "")
| .osv
)
| unique
| map($meta[.])
')"
if [ "$(jq <<<"$vulns" -r 'length')" -le 0 ]; then
printf '%s\n' "$out"
exit 1
fi
filtered="$(jq <<<"$vulns" -c '
(env.excludeVulns | fromjson) as $exclude
| map(select(
.id as $id
| $exclude | index($id) | not
))
')"
text="$(jq <<<"$filtered" -r 'map("- \(.id) (aka \(.aliases | join(", ")))\n\n\t\(.details | gsub("\n"; "\n\t"))") | join("\n\n")')"
if [ -z "$text" ]; then
printf 'No vulnerabilities found.\n'
exit 0
else
printf '%s\n' "$text"
exit 1
fi

View File

@@ -11,3 +11,8 @@ import _ "embed"
//
//go:embed version_go
var VersionGo string
// GoVulnCheck govulncheck.sh
//
//go:embed govulncheck.sh
var GoVulnCheck string

View File

@@ -202,7 +202,7 @@ func (toolchain *Toolchain) CompileDockerfile(output *dockerfile.Output) error {
From("--platform=${BUILDPLATFORM} ${TOOLCHAIN}")
if toolchain.Kind == ToolchainOfficial {
packages := []string{"add", "bash", "curl", "build-base", "protoc", "protobuf-dev"}
packages := []string{"add", "bash", "build-base", "curl", "jq", "protoc", "protobuf-dev"}
packages = append(packages, toolchain.ExtraPackages...)
// automatically add git if we know we're going to have to deal with private repos