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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
4
Makefile
4
Makefile
@@ -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
86
hack/govulncheck.sh
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
86
internal/project/golang/templates/govulncheck.sh
Normal file
86
internal/project/golang/templates/govulncheck.sh
Normal 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
|
||||
@@ -11,3 +11,8 @@ import _ "embed"
|
||||
//
|
||||
//go:embed version_go
|
||||
var VersionGo string
|
||||
|
||||
// GoVulnCheck govulncheck.sh
|
||||
//
|
||||
//go:embed govulncheck.sh
|
||||
var GoVulnCheck string
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user