# CI Identity — SonarQube **Status:** Not done. Tracked as its own issue. **Applies to:** the `SONAR_TOKEN` Gitea Actions secret, and the SonarQube account behind it ## Current state Gitea Actions authenticates to SonarQube using a token belonging to the **`admin`** account. This was flagged early in the project and never revisited. The token reaches the scanner only through the `SONAR_TOKEN` environment variable — #67 removed the `-Dsonar.login=` command-line copy, so it is no longer passed on a command line where it could reach a log. That part is already fixed. What remains is *whose* token it is. ## Why it should change An analysis job needs one permission: submit an analysis report for one project. The `admin` token carries every permission the server has — creating and deleting projects, changing quality gates and profiles, managing users, reading every project including `sql-utilities`. That gap matters in three ordinary situations, none of which require anyone to be malicious: - **A leaked token is a leaked server.** CI secrets end up in more places than intended: a debug run with `set -x`, a third-party action, a fork's workflow. The blast radius of an analysis token is one project's analysis history. The blast radius of this one is everything. - **A misconfigured scan can destroy history.** `sonar.projectKey` is a string in a properties file. A wrong value plus admin rights silently creates projects; other admin endpoints can delete them. A restricted token simply fails. - **Rotation is currently painful.** Rotating `admin`'s token means finding every other place that account is used. A dedicated account can be rotated on its own. There is also a plainer reason: when CI shows up as `admin` in the analysis history, the audit trail cannot distinguish an automated scan from a person making a change. ## The change 1. On SonarQube, create a user — `gitea-ci` — with **no** global permissions. 2. Grant it **Execute Analysis** on the `redefined-designs` project only. That is the single permission a scan needs. 3. Generate a **user token** for that account. Not a project or global analysis token: SonarSource's own MCP server, and other tooling, require the user type, and this server is old enough that the distinction matters. 4. Update the `SONAR_TOKEN` secret in the repository's Actions settings. 5. Run the SonarQube workflow and confirm it still passes. 6. **Revoke the `admin` token** that CI was using. Skipping this leaves the old credential valid and the change cosmetic. Step 6 is the one worth naming explicitly, because the workflow will already be green after step 5 and it is easy to stop there. ## Also worth deciding at the same time The scratch project `redefined-designs-local`, used by `scripts/scan-local.sh` so local scans do not overwrite CI's analysis of `main`, is currently written by a personal token from the developer's environment. If local scanning becomes a habit rather than an occasional check, it deserves the same treatment: its own restricted account rather than whichever token is to hand. ## Constraints on this server SonarQube here is **9.9.8 LTA, Community edition**. Two consequences: - It has no branch analysis, so every scan overwrites the single `main` analysis for whichever project key it is given. This is why the scratch project exists. - It does not accept `Authorization: Bearer` — a token is supplied as the basic-auth username. Any script checking the new account's permissions must use `curl -u "$TOKEN:"`, not a bearer header, or it will look like the permissions are wrong when the auth scheme is. ## Related - `docs/ci/coverage-pipeline-contract.md` — what the pipeline must do for coverage to stay honest - #61 raised this as a "Related" note; it was split out so a permissions change would not be buried inside a CI-config commit