-
Notifications
You must be signed in to change notification settings - Fork 0
ci(security): add govulncheck, gosec and diff-only golangci-lint #324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| name: Gosec Security Scan | ||
|
|
||
| # Port of the Lumera chain repo's `gosec.yml` (LumeraProtocol/lumera#213). | ||
| # | ||
| # Same tool, same policy: HIGH severity + HIGH confidence, suppressions must | ||
| # carry both a rule ID and a justification (`-nosec-require-rules`, | ||
| # `-nosec-require-justification`), results published to GitHub code scanning | ||
| # as SARIF. | ||
| # | ||
| # Difference from the chain repo: supernode has four Go modules instead of | ||
| # three. gosec's package loader walks a single module at a time, so each is | ||
| # scanned from its own working directory. A root `./...` run does NOT cover | ||
| # the nested modules. | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths-ignore: | ||
| - "**.md" | ||
| - "docs/**" | ||
| - ".gitignore" | ||
| push: | ||
| branches: [master] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| security-events: write | ||
|
|
||
| jobs: | ||
| gosec: | ||
| name: gosec (${{ matrix.name }}) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 25 | ||
| strategy: | ||
| # Report every module; one module's findings must not hide another's. | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - name: supernode | ||
| module: . | ||
| - name: sncli | ||
| module: cmd/sncli | ||
| - name: sn-manager | ||
| module: sn-manager | ||
| - name: systemtests | ||
| module: tests/system | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6.0.1 | ||
|
|
||
| # Installs Go from go.mod, libwebp-dev (required for the root module to | ||
| # type-check — without it gosec fails SSA construction rather than | ||
| # reporting a clean scan), and GOPRIVATE for the Lumera module. | ||
| - name: Setup Go and system deps | ||
| uses: ./.github/actions/setup-env | ||
|
|
||
| - name: Download Go modules | ||
| working-directory: ${{ matrix.module }} | ||
| run: go mod download | ||
|
|
||
| - name: Install gosec | ||
| run: go install github.com/securego/gosec/v2/cmd/gosec@v2.29.0 | ||
|
|
||
| # No `continue-on-error` here: this is a blocking gate. `-stdout | ||
| # -verbose text` keeps findings readable in the job log even when the | ||
| # SARIF upload is skipped (fork PRs). | ||
| - name: Run gosec | ||
| working-directory: ${{ matrix.module }} | ||
| run: | | ||
| gosec \ | ||
| -severity high \ | ||
| -confidence high \ | ||
| -nosec-require-rules \ | ||
| -nosec-require-justification \ | ||
| -fmt sarif \ | ||
| -out gosec.sarif \ | ||
| -stdout \ | ||
| -verbose text \ | ||
| ./... | ||
|
|
||
| # gosec emits artifact URIs relative to the scanned module, so findings | ||
| # from nested modules would otherwise be attributed to paths that do not | ||
| # exist at the repository root. Prefix them with the module directory. | ||
| - name: Normalize SARIF paths | ||
| if: ${{ always() && matrix.module != '.' }} | ||
| env: | ||
| GOSEC_MODULE: ${{ matrix.module }} | ||
| GOSEC_SARIF: ${{ matrix.module }}/gosec.sarif | ||
| run: | | ||
| python3 - <<'PY' | ||
| import json | ||
| import os | ||
| from pathlib import PurePosixPath | ||
| from urllib.parse import urlparse | ||
|
|
||
| sarif_path = os.environ["GOSEC_SARIF"] | ||
| module = os.environ["GOSEC_MODULE"].strip("/") | ||
| with open(sarif_path, encoding="utf-8") as sarif_file: | ||
| sarif = json.load(sarif_file) | ||
|
|
||
| def prefix_artifact_uris(value): | ||
| if isinstance(value, dict): | ||
| artifact = value.get("artifactLocation") | ||
| if isinstance(artifact, dict): | ||
| uri = artifact.get("uri") | ||
| if isinstance(uri, str) and uri and not urlparse(uri).scheme and not uri.startswith("/"): | ||
| if not uri.startswith(module + "/"): | ||
| artifact["uri"] = str(PurePosixPath(module) / uri) | ||
| for child in value.values(): | ||
| prefix_artifact_uris(child) | ||
| elif isinstance(value, list): | ||
| for child in value: | ||
| prefix_artifact_uris(child) | ||
|
|
||
| prefix_artifact_uris(sarif) | ||
| with open(sarif_path, "w", encoding="utf-8") as sarif_file: | ||
| json.dump(sarif, sarif_file, separators=(",", ":")) | ||
| PY | ||
|
|
||
| # `security-events: write` is not granted to fork PRs, and dependabot | ||
| # runs with a read-only token, so skip only the upload for those. The | ||
| # scan itself still runs and still blocks. | ||
| - name: Upload SARIF | ||
| if: ${{ always() && (github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]')) }} | ||
| uses: github/codeql-action/upload-sarif@v4 | ||
| with: | ||
| sarif_file: ${{ matrix.module }}/gosec.sarif | ||
| category: gosec-${{ matrix.name }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| name: Go Vulnerability Scan | ||
|
|
||
| # Port of the Lumera chain repo's `govulncheck.yml` (LumeraProtocol/lumera#212). | ||
| # | ||
| # Same tool, same goal: fail a PR when it introduces (or is built on) a | ||
| # *reachable* known vulnerability. govulncheck's default mode only reports | ||
| # vulnerabilities whose vulnerable symbols are actually called, so unused | ||
| # transitive dependencies do not create noise. | ||
| # | ||
| # Difference from the chain repo: supernode is a multi-module repository | ||
| # (root, cmd/sncli, sn-manager, tests/system). The chain repo scans a single | ||
| # module; here each module is scanned independently via a matrix so a | ||
| # vulnerability in sn-manager cannot hide behind a clean root scan. | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths-ignore: | ||
| - "**.md" | ||
| - "docs/**" | ||
| - ".gitignore" | ||
| push: | ||
| branches: [master] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| govulncheck: | ||
| name: govulncheck (${{ matrix.name }}) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
| strategy: | ||
| # Surface every module's result; do not let the first failure mask others. | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - name: supernode | ||
| module: . | ||
| - name: sncli | ||
| module: cmd/sncli | ||
| - name: sn-manager | ||
| module: sn-manager | ||
| - name: systemtests | ||
| module: tests/system | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6.0.1 | ||
|
|
||
| # Installs Go from go.mod, libwebp-dev (required to build the root | ||
| # module), and sets GOPRIVATE for the Lumera module. | ||
| - name: Setup Go and system deps | ||
| uses: ./.github/actions/setup-env | ||
|
|
||
| # Pinned rather than @latest: an unpinned scanner turns an unrelated | ||
| # upstream release into a surprise CI break, and makes a red run | ||
| # non-reproducible after the fact. | ||
| - name: Install govulncheck | ||
| run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 | ||
|
|
||
| - name: Run govulncheck | ||
| working-directory: ${{ matrix.module }} | ||
| run: govulncheck ./... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| name: lint-pr | ||
|
|
||
| # Port of the Lumera chain repo's `lint-pr.yml` (LumeraProtocol/lumera#126). | ||
| # | ||
| # Runs golangci-lint through reviewdog with `filter_mode: added`, so findings | ||
| # are posted as inline review comments on exactly the lines a PR changed and | ||
| # the pre-existing baseline stays invisible. | ||
| # | ||
| # IMPORTANT DIFFERENCE FROM THE CHAIN REPO: | ||
| # The chain repo has a separate full-scan gate (`lint.yml`) that is the | ||
| # authoritative merge blocker, which lets its reviewdog layer be purely | ||
| # additive. Supernode has NO existing lint gate and a large legacy baseline, | ||
| # so a full-repo hard gate would be red on day one. This workflow is therefore | ||
| # the only lint enforcement, and it is intentionally scoped to the diff: | ||
| # new code must be clean, legacy debt is not retroactively blocking. | ||
| # | ||
| # Version pinning (see below) is not cosmetic — go.mod declares go 1.26.2 and | ||
| # golangci-lint must be BUILT with a Go >= that language version or it exits | ||
| # with a hard "the Go language version used to build golangci-lint is lower | ||
| # than the targeted Go version" error. v2.13.2 is built with go1.27.0. | ||
| # reviewdog/action-golangci-lint must be >= v2.8.0 to honour a v2.x pin. | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths-ignore: | ||
| - "**.md" | ||
| - "docs/**" | ||
| - ".gitignore" | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write # reviewdog needs this to post inline review comments (same-repo PRs only) | ||
| checks: write # for the check-run summary | ||
|
|
||
| jobs: | ||
| golangci-lint-diff: | ||
| name: golangci-lint (diff-only, inline) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6.0.1 | ||
| with: | ||
| # reviewdog needs the merge-base to compute the PR diff. | ||
| fetch-depth: 0 | ||
|
|
||
| # Installs Go from go.mod plus libwebp-dev, which the root module | ||
| # requires in order to type-check. | ||
| - name: Setup Go and system deps | ||
| uses: ./.github/actions/setup-env | ||
|
|
||
| # Same-repo PRs: post findings as inline review comments. The default | ||
| # GITHUB_TOKEN has the `pull-requests: write` scope declared above. | ||
| # | ||
| # We deliberately do NOT set `level` here: reviewdog's `level` input | ||
| # REWRITES every finding's severity rather than defaulting it, so | ||
| # `level: warning` combined with `fail_level: error` would silently | ||
| # neuter the gate. Leaving it unset preserves native severities so | ||
| # govet/staticcheck errors actually trip `fail_level: error`. | ||
| - name: golangci-lint via reviewdog (same-repo PR — inline comments) | ||
| if: github.event.pull_request.head.repo.full_name == github.repository | ||
| uses: reviewdog/action-golangci-lint@v2.10.0 | ||
| with: | ||
| go_version_file: go.mod | ||
| golangci_lint_version: v2.13.2 | ||
| golangci_lint_flags: "--config=.golangci.yml --timeout=15m" | ||
| workdir: . | ||
| # Only annotate lines actually changed by the PR. | ||
|
mateeullahmalik marked this conversation as resolved.
|
||
| filter_mode: added | ||
| reporter: github-pr-review | ||
| fail_level: error | ||
| env: | ||
| REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # Fork PRs: GITHUB_TOKEN is strictly read-only regardless of the | ||
| # workflow-level permissions block, so neither `github-pr-review` | ||
| # (needs pull-requests: write) nor `github-pr-check` (needs | ||
| # checks: write) can post results. The `local` reporter writes findings | ||
| # to the job log instead. `fail_level: error` still fails the job, so | ||
| # the check continues to block merge for external contributors. | ||
| - name: golangci-lint via reviewdog (fork PR — log-only) | ||
| if: github.event.pull_request.head.repo.full_name != github.repository | ||
| uses: reviewdog/action-golangci-lint@v2.10.0 | ||
| with: | ||
| go_version_file: go.mod | ||
| golangci_lint_version: v2.13.2 | ||
| golangci_lint_flags: "--config=.golangci.yml --timeout=15m" | ||
| workdir: . | ||
| filter_mode: added | ||
| reporter: local | ||
| fail_level: error | ||
| env: | ||
| REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| version: "2" | ||
|
|
||
| # Linter policy for the supernode repo. | ||
| # | ||
| # Mirrors the Lumera chain repo's `.golangci.yml` (same linter set, same | ||
| # v2 schema) so both repos enforce a consistent bar. The exclusion list is | ||
| # supernode-specific: it covers generated protobuf/mock surfaces and test | ||
| # helpers rather than the chain's Cosmos SDK deprecations. | ||
| # | ||
| # Enforcement is diff-only via reviewdog (`.github/workflows/lint-pr.yml`), | ||
| # so the pre-existing baseline is not a merge blocker; only findings on | ||
| # lines a PR actually touches are reported. | ||
|
|
||
| linters: | ||
| default: none | ||
| enable: | ||
| - errcheck | ||
| - staticcheck | ||
| - unused | ||
| - ineffassign | ||
| - govet | ||
| - nolintlint | ||
|
|
||
| exclusions: | ||
| generated: strict | ||
| rules: | ||
| # Test files: setup helpers routinely ignore errors from fixture | ||
| # construction where a failure would surface as a test failure anyway. | ||
| - path: _test\.go | ||
| linters: | ||
| - errcheck | ||
|
|
||
| # Generated gRPC/protobuf gateway + mock surfaces. | ||
| - path: (gen/|/mocks?/|\.pb\.go|\.pb\.gw\.go) | ||
| linters: | ||
| - errcheck | ||
| - unused | ||
| - staticcheck | ||
|
|
||
| # SDK deprecated APIs — cannot be removed until upstream drops them. | ||
| - linters: | ||
| - staticcheck | ||
| text: "SA1019" | ||
|
|
||
| # Cosmetic quick-fix suggestions (QF1003/QF1007/QF1008/QF1011). | ||
| - linters: | ||
| - staticcheck | ||
| text: "QF10(0[378]|11)" | ||
|
|
||
| # S1001 (use copy), S1009 (nil check before len), S1011 (use append), | ||
| # S1021 (merge var) — cosmetic, not correctness issues. | ||
| - linters: | ||
| - staticcheck | ||
| text: "S100[19]|S101[1]|S1021" | ||
|
|
||
| # ST1005 (error string capitalization), ST1019 (duplicate import), | ||
| # ST1023 (omit type from declaration) — style, not correctness. | ||
| - linters: | ||
| - staticcheck | ||
| text: "ST10(05|19|23)" | ||
|
|
||
| # SA4031 (nil check on make result), SA9003 (empty branch) — | ||
| # intentional patterns in existing code. | ||
| - linters: | ||
| - staticcheck | ||
| text: "SA(4031|9003)" | ||
|
|
||
| # SA1029 (built-in type as context key) — existing pattern. | ||
| - linters: | ||
| - staticcheck | ||
| text: "SA1029" | ||
|
|
||
| formatters: | ||
| enable: | ||
| - gofmt | ||
| exclusions: | ||
| generated: strict |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.