Skip to content

ci(sonar): scan on SonarQube Cloud, and fix what its first scan found - #45

Merged
ftarasenko merged 8 commits into
masterfrom
claude/dazzling-meitner-v47jav
Oct 21, 2026
Merged

ftarasenko merged 8 commits into
masterfrom
claude/dazzling-meitner-v47jav

Conversation

@ftarasenko

@ftarasenko ftarasenko commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

What

SonarQube Cloud from CI (b628a45, 933fde8)

  • .github/workflows/sonar.yml — SonarQube Cloud scan on every push to master and claude/**, on same-repo pull requests, and on workflow_dispatch. It is network-allowed and never a dependency of the offline ci.yml jobs.
    • Generates coverage.out on the checkout it scans (make cover, run offline from vendor/).
    • Passes sonar.projectVersion from the latest v* tag and fails without one. Checks out full history, so tags and blame are present.
    • A token job skips the scan with a notice when SONAR_TOKEN is unavailable (fork and Dependabot PRs), instead of failing.
    • The scan action is pinned to a full SHA (v8.3.0).
  • make cover now works on toolchains fetched by GOTOOLCHAIN. cmd/koc had no test, and -coverpkg sent it through go tool covdata, which those toolchains do not ship, so the run exited 1. cmd/koc/main_test.go fixes that.
    • The target also merges the blocks -coverpkg repeats once per test binary: 818k lines become 23k, and the total stays at 76.3%.
  • make sonar (the local podman scan against the internal server) is removed. sonar-project.properties now names the SonarQube Cloud organization and key.
  • AGENTS.md / CLAUDE.md
    • The "SonarQube Cloud" section is rewritten, including the one-time setup.
    • New section "Where checks run (agent sessions)", with measured costs:
      • In the session: vet, the changed packages' tests, the full unit suite (~45 s) and Go 1.27's gofmt.
      • On GitHub: lint, race, crossbuild, coverage and Sonar.

Fixes for the first scan's findings (ecff572..3f1c2e3)

Commit Rule Change
fix(kochome): refuse an embedded home another local user could control go:S5445 (critical) The embedded profile's default $TMPDIR/koc-home is now created by koc, 0700. A symlink, a file, another user's directory, or a group/world-writable one is refused with a pointer to KOC_HOME. On a shared /tmp another user could otherwise plant the scope file or redirect the audit log. Unit tests, plus an end-to-end run of the binary for each case; docs/shell.md updated.
test: cancel the contexts three tests create godre:S8188 ×3 Adds defer cancel().
ci(lint): refuse a plain-HTTP redirect when downloading golangci-lint githubactions:S6506 ×2 Adds --proto '=https' --tlsv1.2.
build(scripts): use [[ ]] for the tests in release-notes.sh and api-compat.sh shelldre:S7688 ×9 Release notes for v0.41.0, v0.42.0 and an unreleased HEAD are byte-identical before and after.
ci(devstack): install pip from a wheel only on the runner shell:S8541 Adds --only-binary :all:. This push triggers the devstack functional run.

Not changed, to be accepted in the SonarQube Cloud UI:

  • go:S4507 — --debug is an opt-in operator flag that already redacts secrets.
  • githubactions:S8545 ×2 — go install …@<exact version> is verified against the checksum database and resolved from the tool's own go.mod.
  • shell:S5332 — plain http:// to swift on a local devstack, which has no TLS.

The 14 findings under vendor/ came from the earlier Automatic Analysis, which ignored sonar-project.properties. The CI scan excludes vendor/, so they disappear from master once this merges.

Verified

  • Every check passes on 933fde8: ci, supply-chain, sonar, CodeQL, and the SonarQube Cloud PR analysis.
  • The first sonar run (re-run after Automatic Analysis was turned off) saw the token, resolved v0.42.0, loaded coverage.out with no unresolved files, and the scan succeeded.
  • Locally on 3f1c2e3:
    • go vet ./... and the full go test ./... pass.
    • The offline functional tests pass.
    • darwin and windows builds pass.
    • Go 1.27's gofmt is clean.
    • make cover exits 0, 76.3%.

Merge

Please use rebase or fast-forward rather than squash, so each Conventional Commit subject becomes its own release-notes bullet. Merging to master gives SonarQube Cloud its first CI-based main-branch analysis, the baseline later branch and PR analyses compare against.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY

The scan ran only when someone remembered to run `make sonar` against the
internal server, and both of its inputs failed silently when forgotten. It
now runs from .github/workflows/sonar.yml on every push to master and every
same-repo pull request, generating coverage on the scanned checkout and
passing sonar.projectVersion from the latest v* tag (failing without one). It
uses a full-depth checkout so tags and blame are present. A token job skips
the scan when SONAR_TOKEN is unavailable (fork and Dependabot PRs, or before
the secret is configured) instead of failing it. The scan action is pinned to
a full SHA.

make cover now succeeds on toolchains fetched by GOTOOLCHAIN. cmd/koc, the
one package without a test, sent -coverpkg through `go tool covdata`, which
those toolchains do not ship, so the run exited 1 and the old sonar target
never started. It also merges the repeats -coverpkg writes: each block once
per test binary, 818k lines collapsed to 23k. The total is unchanged at
76.3%.

AGENTS.md documents the one-time SonarQube Cloud setup and a new "Where
checks run (agent sessions)" section with measured costs. Vet, the changed
packages' tests, the full unit suite (~45 s) and Go 1.27's gofmt run in the
session. Lint, race, crossbuild, coverage and the scan run on GitHub. This
is because a cloud session's own golangci-lint and gofmt are older than the
pinned ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
An agent reads the result of its own push before moving on, and a change
to sonar.yml can only be proved on a branch: workflow_dispatch reaches a
workflow only once it is on master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
In the embedded profile without KOC_HOME, koc uses the fixed
$TMPDIR/koc-home. A relative KOC_SCOPE_FILE, KOC_HISTORY_FILE or
KOC_AUDIT_FILE resolves inside it, and the audit log is opened with
O_CREATE|O_APPEND. On a host with a shared /tmp, another user could
create that directory first and plant the scope file koc reads (a token
and an endpoint), or symlink the audit log at a file the victim owns.

koc now creates the directory itself, 0700, before any command runs. It
refuses one that is not a real directory (a symlink or a file), belongs
to another user, or is writable by group or others, and names KOC_HOME
as the way out. A KOC_HOME the host names is trusted as before. Outside
Unix the temporary directory is already per-user, so only the
symlink/file check applies.

Found by SonarQube Cloud (go:S5445).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
TestRunStopsOnContextCancel, TestRepaintEntersAndLeavesTheAlternateScreenExactlyOnce
and TestDoStopsOnContextCancellation cancel only from inside a callback,
so a run that fails before the callback fires leaks the context. Defer
the cancel as well; calling it twice is harmless.

Found by SonarQube Cloud (godre:S8188).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
The checksum check already catches a swapped tarball, but only because
checksums.txt comes over the same connection; --proto '=https' and
--tlsv1.2 keep a redirect from moving either download off TLS.

Found by SonarQube Cloud (githubactions:S6506).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
…ompat.sh

Both are bash scripts. Every converted test is -n, -z, -d or -eq, where [[
behaves the same as [ but cannot be broken by word splitting.
release-notes.sh prints byte-identical notes for v0.41.0, v0.42.0 and an
unreleased HEAD before and after.

Found by SonarQube Cloud (shelldre:S7688).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
--only-binary :all: keeps the GitHub-runner workaround from ever running
a source distribution's setup script while upgrading pip.

Found by SonarQube Cloud (shell:S8541).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
@ftarasenko ftarasenko changed the title ci(sonar): scan on SonarQube Cloud from a workflow instead of by hand ci(sonar): scan on SonarQube Cloud, and fix what its first scan found Oct 21, 2026
The quality gate measured 69% coverage on the new code against its 80%
threshold. Add the paths the first tests skipped: checkEmbedded returning
the refusal, a home that cannot be created, and a directory owned by
another user (through a FileInfo whose Stat_t names a different uid, so no
root is needed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTGXtL5ssZ9bTDz3dHc2PY
@sonarqubecloud

Copy link
Copy Markdown

@ftarasenko
ftarasenko merged commit 8cd0cae into master Oct 21, 2026
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants