diff --git a/.github/workflows/flow-build-application.yaml b/.github/workflows/flow-build-application.yaml index 625ceec1a..341b536a1 100644 --- a/.github/workflows/flow-build-application.yaml +++ b/.github/workflows/flow-build-application.yaml @@ -29,6 +29,10 @@ defaults: run: shell: bash +permissions: + contents: read + actions: read + jobs: code: name: Code diff --git a/.github/workflows/flow-pull-request-checks.yaml b/.github/workflows/flow-pull-request-checks.yaml index 1a4a45708..49e387ffe 100644 --- a/.github/workflows/flow-pull-request-checks.yaml +++ b/.github/workflows/flow-pull-request-checks.yaml @@ -18,6 +18,10 @@ concurrency: group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + actions: read + jobs: build: name: Code @@ -32,6 +36,15 @@ jobs: custom-job-label: Check enable-lint-check: true + vuln-check: + name: Vulnerability Check + uses: ./.github/workflows/zxc-compile-code.yaml + needs: + - build + with: + custom-job-label: Govulncheck + enable-vuln-check: true + unit-tests: name: Unit Tests uses: ./.github/workflows/zxc-compile-code.yaml diff --git a/.github/workflows/zxc-code-analysis.yaml b/.github/workflows/zxc-code-analysis.yaml index 859bc5f79..160f07db9 100644 --- a/.github/workflows/zxc-code-analysis.yaml +++ b/.github/workflows/zxc-code-analysis.yaml @@ -55,9 +55,6 @@ defaults: permissions: contents: read actions: read - pull-requests: write - checks: write - statuses: write jobs: analyze: diff --git a/.github/workflows/zxc-compile-code.yaml b/.github/workflows/zxc-compile-code.yaml index 5dc7d1772..728b74f51 100644 --- a/.github/workflows/zxc-compile-code.yaml +++ b/.github/workflows/zxc-compile-code.yaml @@ -25,6 +25,11 @@ on: type: boolean required: false default: false + enable-vuln-check: + description: "Go Vulnerability Check Enabled" + type: boolean + required: false + default: false enable-docker-build: description: "Docker Build Enabled" type: boolean @@ -41,12 +46,8 @@ defaults: shell: bash permissions: - id-token: write contents: read actions: read - pull-requests: write - checks: write - statuses: write env: KIND_NODE_IMAGE: kindest/node:v1.32.2@sha256:142f543559cc55d64e1ab9341df08e5ced84bd2e893736da8f51320f26f5950b @@ -93,6 +94,10 @@ jobs: if: ${{ inputs.enable-lint-check && !cancelled() && !failure() }} run: task lint + - name: Vulnerability Check + if: ${{ inputs.enable-vuln-check && !cancelled() && !failure() }} + run: task vuln + - name: Unit Tests if: ${{ inputs.enable-unit-tests && !cancelled() && !failure() }} run: task test:unit diff --git a/.golangci.yml b/.golangci.yml index 729264773..4afb2e8c5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,12 +16,32 @@ linters: - misspell - unconvert - gocritic + - gosec exclusions: rules: - path: _test\.go linters: - errcheck - unused + - gosec + - path: ^test/ + linters: + - gosec + # Operator-supplied CLI/config paths; UC reads are O_NOFOLLOW-hardened + - path: ^internal/(beacon|uc)/ + linters: + - gosec + text: "G304" + # math/rand only orders registry candidates + - path: ^internal/controller/ + linters: + - gosec + text: "G404" + # Reason code name, not a credential + - path: ^internal/registry/reasons\.go + linters: + - gosec + text: "G101" # Ignore deprecated Requeue field - it's still commonly used in controller-runtime - linters: - staticcheck diff --git a/Taskfile.yml b/Taskfile.yml index 3d5c1c7b2..cbf6e7fbd 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -162,6 +162,12 @@ tasks: cmds: - golangci-lint run --timeout=5m + vuln: + desc: Scan for reachable Go vulnerabilities (govulncheck) + deps: [ tools:govulncheck ] + cmds: + - govulncheck ./... + lint:fix: desc: Run linters and fix issues deps: [ tools:golangci-lint ] @@ -522,6 +528,7 @@ tasks: - task: tools:controller-gen - task: tools:setup-envtest - task: tools:golangci-lint + - task: tools:govulncheck - task: tools:yq # Pinned (not @latest): controller-gen output must be deterministic so @@ -546,6 +553,12 @@ tasks: - task: tools:install vars: { BIN: golangci-lint, PKG: github.com/golangci/golangci-lint/v2/cmd/golangci-lint, MOD: github.com/golangci/golangci-lint/v2, VER: v2.14.0 } + tools:govulncheck: + internal: true + cmds: + - task: tools:install + vars: { BIN: govulncheck, PKG: golang.org/x/vuln/cmd/govulncheck, MOD: golang.org/x/vuln, VER: v1.9.0 } + tools:yq: internal: true cmds: diff --git a/internal/controller/networkupgradeprepare_controller.go b/internal/controller/networkupgradeprepare_controller.go index aa60677eb..0769a5390 100644 --- a/internal/controller/networkupgradeprepare_controller.go +++ b/internal/controller/networkupgradeprepare_controller.go @@ -561,7 +561,7 @@ func (r *NetworkUpgradePrepareReconciler) targetsBySelector( common.Debug(ctx, operatorv1alpha1.ReasonPrefetchCreated). Msg("Using pod-node targeting: consensus nodeSelector matched no nodes") return nil, false - case int32(len(candidateNodes)) > maxNodes: + case len(candidateNodes) > int(maxNodes): // Genuine degradation: the matched pool is larger than the cap, so we // cannot cache on every node the pod could reschedule onto. Surface it. common.Warn(ctx, operatorv1alpha1.ReasonPrefetchCreated). diff --git a/internal/uc/cr_store.go b/internal/uc/cr_store.go index 2aa02bff4..d92627da5 100644 --- a/internal/uc/cr_store.go +++ b/internal/uc/cr_store.go @@ -91,7 +91,7 @@ func (s *CRStore) Persist(operationID, step string, cr *unstructured.Unstructure // Write atomically: write a temp file then rename, so a crash mid-write can // never leave a truncated CR on disk that recovery would try to re-send. tmp := path + ".tmp" - if err := os.WriteFile(tmp, data, 0o640); err != nil { + if err := os.WriteFile(tmp, data, 0o600); err != nil { return "", errx.WithHints( errx.WithReason(errPersist.Wrap(err, "write CR %s", tmp), ReasonCRPersistWriteFailed), "Confirm --cr-persist-path is on a writable volume with free space", diff --git a/internal/uc/log_monitor.go b/internal/uc/log_monitor.go index 84d0d5354..e028e0799 100644 --- a/internal/uc/log_monitor.go +++ b/internal/uc/log_monitor.go @@ -279,7 +279,7 @@ func (m *LogMonitor) persist(code int32, name string) { if err != nil { return } - _ = os.WriteFile(m.persistPath, data, 0o644) + _ = os.WriteFile(m.persistPath, data, 0o600) } // LogFileMtime returns the mtime of the configured consensus log file in unix