Skip to content

feat(scorecard): Add SonarQube metric providers#2576

Open
christoph-jerolimov wants to merge 8 commits intoredhat-developer:mainfrom
christoph-jerolimov:scorecard/sonarqube
Open

feat(scorecard): Add SonarQube metric providers#2576
christoph-jerolimov wants to merge 8 commits intoredhat-developer:mainfrom
christoph-jerolimov:scorecard/sonarqube

Conversation

@christoph-jerolimov
Copy link
Member

@christoph-jerolimov christoph-jerolimov commented Mar 19, 2026

Hey, I just made a Pull Request!

New module with new metrics from sonarqube/sonarcloud; agentic-eng. by @imykhno, myself and Claude. 😀

image

TODOs

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

christoph-jerolimov and others added 2 commits March 19, 2026 12:08
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
…ues, and security

Add four metric providers to the scorecard-backend-module-sonarqube plugin:
- Quality gate status (boolean)
- Open issues count (number)
- Security rating (number, A=1 to E=5)
- Security issues/vulnerabilities count (number)

Includes SonarQubeClient, config, factory, example catalog entity, and unit tests.
SonarQube baseUrl defaults to https://sonarcloud.io; token is optional for public projects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@rhdh-gh-app
Copy link

rhdh-gh-app bot commented Mar 19, 2026

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app workspaces/scorecard/packages/app none v0.0.0
backend workspaces/scorecard/packages/backend none v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-sonarqube workspaces/scorecard/plugins/scorecard-backend-module-sonarqube minor v0.0.0

christoph-jerolimov and others added 3 commits March 19, 2026 13:12
…config schema

- Add config.d.ts with typed config schema supporting default + named instances
- Refactor SonarQubeClient to resolve instance by name from sonarqube.instances[]
- Parse sonarqube.org/project-key annotation for optional instance prefix (instance/project-key)
- Use apiKey + authType (Basic/Bearer) from config.d.ts instead of token
- Falls back to default instance when no instance prefix in annotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
SonarQube expects Basic auth as base64(apiKey:) with an appended colon.
Bearer auth passes the apiKey directly without encoding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@rhdh-qodo-merge
Copy link

Review Summary by Qodo

Add SonarQube metric providers for scorecard with multi-instance support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add SonarQube backend module with four metric providers
  - Quality gate status (boolean)
  - Open issues count (number)
  - Security rating (number, A=1 to E=5)
  - Security issues/vulnerabilities count (number)
• Support multiple SonarQube instances with named configuration
• Implement SonarQubeClient with Basic/Bearer auth and base64 encoding
• Include comprehensive unit tests and configuration schema
• Add README documentation and example catalog entity
Diagram
flowchart LR
  Config["Config Schema<br/>config.d.ts"]
  Client["SonarQubeClient<br/>API Integration"]
  BoolProvider["BooleanMetricProvider<br/>Quality Gate"]
  NumProvider["NumberMetricProvider<br/>Issues/Rating"]
  Factory["MetricProviderFactory<br/>Provider Creation"]
  Module["Backend Module<br/>Registration"]
  
  Config -->|"Instance Config"| Client
  Client -->|"API Calls"| BoolProvider
  Client -->|"API Calls"| NumProvider
  Factory -->|"Creates"| BoolProvider
  Factory -->|"Creates"| NumProvider
  Module -->|"Registers"| Factory
Loading

Grey Divider

File Changes

1. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts ⚙️ Configuration changes +77/-0

TypeScript configuration schema for SonarQube

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts


2. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts ✨ Enhancement +141/-0

SonarQube API client with multi-instance support

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts


3. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts 🧪 Tests +289/-0

Comprehensive unit tests for SonarQubeClient

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts


View more (19)
4. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts ✨ Enhancement +97/-0

Boolean metric provider for quality gate status

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts


5. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.test.ts 🧪 Tests +189/-0

Unit tests for boolean metric provider

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.test.ts


6. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts ✨ Enhancement +134/-0

Number metric provider for issues and ratings

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts


7. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.test.ts 🧪 Tests +245/-0

Unit tests for number metric provider

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.test.ts


8. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts ✨ Enhancement +127/-0

Metric configuration and annotation parsing utilities

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts


9. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.ts ✨ Enhancement +51/-0

Factory for creating metric provider instances

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.ts


10. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.test.ts 🧪 Tests +94/-0

Unit tests for metric provider factory

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeMetricProviderFactory.test.ts


11. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/module.ts ✨ Enhancement +42/-0

Backend module registration and initialization

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/module.ts


12. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/index.ts ✨ Enhancement +23/-0

Module entry point and public exports

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/index.ts


13. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md 📝 Documentation +79/-0

Documentation for SonarQube module usage

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md


14. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json ⚙️ Configuration changes +44/-0

Package configuration and dependencies

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json


15. workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/.eslintrc.js ⚙️ Configuration changes +1/-0

ESLint configuration for module

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/.eslintrc.js


16. workspaces/scorecard/packages/backend/src/index.ts ✨ Enhancement +5/-0

Register SonarQube module in backend

workspaces/scorecard/packages/backend/src/index.ts


17. workspaces/scorecard/packages/backend/package.json Dependencies +1/-0

Add SonarQube module dependency

workspaces/scorecard/packages/backend/package.json


18. workspaces/scorecard/app-config.yaml ⚙️ Configuration changes +7/-2

Add SonarQube config example and adjust schedules

workspaces/scorecard/app-config.yaml


19. workspaces/scorecard/examples/all-scorecards-location.yaml 📝 Documentation +1/-0

Add SonarQube example component to location

workspaces/scorecard/examples/all-scorecards-location.yaml


20. workspaces/scorecard/examples/components/sonarqube-scorecard-only.yaml 📝 Documentation +12/-0

Example component with SonarQube annotation

workspaces/scorecard/examples/components/sonarqube-scorecard-only.yaml


21. workspaces/scorecard/.changeset/sonarqube-metric-providers.md 📝 Documentation +5/-0

Changeset for metric providers feature

workspaces/scorecard/.changeset/sonarqube-metric-providers.md


22. workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md 📝 Documentation +5/-0

Changeset for Basic auth fix

workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link

rhdh-qodo-merge bot commented Mar 19, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. Instance apiKey wrongly required 🐞 Bug ✓ Correctness
Description
The config schema requires sonarqube.instances[].apiKey, but the README/tests/implementation
support instances without an apiKey for public projects. This mismatch will cause config schema
validation to reject documented configurations.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[R63-68]

+      /**
+       * The api key to access the sonarqube instance.
+       * @visibility secret
+       */
+      apiKey: string;
+
Evidence
The schema declares apiKey required for each instance, but the README includes an instance without
apiKey, tests configure an instance without apiKey, and the runtime code reads it via
getOptionalString('apiKey').

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[39-75]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[65-79]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[206-223]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[273-287]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[46-55]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`config.d.ts` requires `sonarqube.instances[].apiKey`, but the implementation and documentation allow instances without an apiKey (for public projects). This will cause Backstage config schema validation to fail for valid configs.

### Issue Context
README and tests demonstrate an instance without `apiKey`.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[39-75]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[65-79]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[206-223]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[46-55]

### Proposed fix
- Change `instances[].apiKey: string` to `instances[].apiKey?: string`.
- Update the JSDoc comment to state apiKey is optional for public projects (matching top-level `apiKey?: string`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Instance prefix ignored silently 🐞 Bug ✓ Correctness
Description
If an entity uses an instance-prefixed project key (e.g. internal/my-project) but the sonarqube
config block is missing, resolveInstance ignores the instanceName and falls back to the default
instance. This can query the wrong SonarQube/SonarCloud instance without any explicit error about
the missing instance configuration.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[R37-69]

+  private resolveInstance(instanceName?: string): SonarQubeInstance {
+    const sonarqubeConfig = this.config.getOptionalConfig('sonarqube');
+
+    if (instanceName && sonarqubeConfig) {
+      const instances =
+        sonarqubeConfig.getOptionalConfigArray('instances') ?? [];
+      const instance = instances.find(
+        i => i.getString('name') === instanceName,
+      );
+      if (instance) {
+        return {
+          baseUrl: instance.getString('baseUrl').replace(/\/$/, ''),
+          apiKey: instance.getOptionalString('apiKey'),
+          authType:
+            (instance.getOptionalString('authType') as
+              | 'Bearer'
+              | 'Basic'
+              | undefined) ?? 'Basic',
+        };
+      }
+      throw new Error(
+        `SonarQube instance '${instanceName}' not found in configuration`,
+      );
+    }
+
+    return {
+      baseUrl: (
+        sonarqubeConfig?.getOptionalString('baseUrl') ?? DEFAULT_BASE_URL
+      ).replace(/\/$/, ''),
+      apiKey: sonarqubeConfig?.getOptionalString('apiKey'),
+      authType: 'Basic',
+    };
+  }
Evidence
Metric providers always pass instanceName when the annotation contains a prefix, but
resolveInstance only honors instanceName when sonarqubeConfig exists. When sonarqubeConfig
is undefined, the function returns the default instance configuration and ignores the provided
instanceName.

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[108-119]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[37-69]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SonarQubeClient.resolveInstance(instanceName)` ignores a provided `instanceName` when the `sonarqube` config block is missing, silently falling back to the default instance. This can cause requests to go to the wrong SonarQube/SonarCloud instance.

### Issue Context
Instance names come from entity annotations (`sonarqube.org/project-key`) via `parseProjectKeyAnnotation`, and metric providers pass `instanceName` through to the client.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[37-69]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]

### Proposed fix
- If `instanceName` is provided but `sonarqubeConfig` is missing, throw a clear error like:
 `SonarQube instance &#x27;&lt;name&gt;&#x27; requested but no &#x27;sonarqube&#x27; configuration found`.
- (Optional) Add a unit test covering `instanceName` + empty root config to prevent regressions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing measures return undefined 🐞 Bug ⛯ Reliability
Description
SonarQubeNumberMetricProvider returns measures.security_rating/measures.vulnerabilities
without verifying they exist in the response. If SonarQube omits a measure, calculateMetric
returns undefined, violating the MetricProvider contract and resulting in null/undefined metric
values stored without an error.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[R114-129]

+      case 'security_rating': {
+        const measures = await this.client.getMeasures(
+          projectKey,
+          SONARQUBE_API_METRIC_KEYS.security_rating,
+          instanceName,
+        );
+        return measures.security_rating;
+      }
+      case 'security_issues': {
+        const measures = await this.client.getMeasures(
+          projectKey,
+          SONARQUBE_API_METRIC_KEYS.security_issues,
+          instanceName,
+        );
+        return measures.vulnerabilities;
+      }
Evidence
The MetricProvider interface requires calculateMetric to return a MetricValue (for number
metrics: a number), but the client only populates keys present in data.component.measures. The
provider then directly returns a possibly-missing key from the measures record.

workspaces/scorecard/plugins/scorecard-node/src/api/MetricProvider.ts[25-65]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[120-140]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[114-129]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SonarQubeNumberMetricProvider.calculateMetric` can return `undefined` when a requested measure is missing from the SonarQube API response (e.g., `security_rating` or `vulnerabilities` not present). This violates the `MetricProvider` contract (must return a number) and leads to silent null/undefined values being stored.

### Issue Context
`SonarQubeClient.getMeasures` only adds metrics that appear in `data.component.measures`, and the provider accesses keys directly.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[120-140]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[114-129]
- workspaces/scorecard/plugins/scorecard-node/src/api/MetricProvider.ts[25-65]

### Proposed fix
Choose one consistent behavior:
- **Preferred (fail fast):** After fetching measures, check for the requested key(s); if missing, throw an Error like `Missing SonarQube measure &#x27;security_rating&#x27; for project &#x27;&lt;key&gt;&#x27;` so the scheduler stores `error_message`.
- **Alternative (default value):** Return `0` when a measure is absent, if that better matches scorecard semantics.

Also consider guarding against `data.component?.measures` being absent and throwing a clearer error message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Package marked private🐞 Bug ⚙ Maintainability
Description
The new module package is set to "private": true, which prevents publishing even though this PR adds
changesets for that package. This will break release automation/consumption of the new module as a
published package.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[5]

+  "private": true,
Evidence
The sonarqube module is explicitly private, while the PR adds changesets for it (indicating intent
to release/publish) and other scorecard backend modules are not private.

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[1-18]
workspaces/scorecard/.changeset/sonarqube-metric-providers.md[1-5]
workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md[1-5]
workspaces/scorecard/plugins/scorecard-backend-module-dependabot/package.json[1-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-sonarqube` is marked `&quot;private&quot;: true`, which prevents publishing even though the PR adds changesets for it.

### Issue Context
Other scorecard backend modules (e.g. dependabot) are publishable and include standard Backstage package metadata.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/package.json[1-18]
- workspaces/scorecard/.changeset/sonarqube-metric-providers.md[1-5]
- workspaces/scorecard/.changeset/sonarqube-basic-auth-encoding.md[1-5]
- workspaces/scorecard/plugins/scorecard-backend-module-dependabot/package.json[1-23]

### Proposed fix
- Remove `&quot;private&quot;: true`.
- (Optional but recommended for consistency) add `backstage.pluginPackage` and `repository` metadata similarly to other modules.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Invalid annotation not rejected 🐞 Bug ✓ Correctness
Description
parseProjectKeyAnnotation accepts values like internal/ or /my-project, producing an empty
instanceName or projectKey. This leads to confusing downstream API requests (e.g. projectKey=)
instead of a clear validation error at annotation parsing time.
Code

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[R32-42]

+export function parseProjectKeyAnnotation(
+  annotation: string,
+): SonarQubeAnnotation {
+  const slashIndex = annotation.indexOf('/');
+  if (slashIndex === -1) {
+    return { projectKey: annotation };
+  }
+  return {
+    instanceName: annotation.substring(0, slashIndex),
+    projectKey: annotation.substring(slashIndex + 1),
+  };
Evidence
The parsing logic splits on the first '/', but never validates that the substring before/after the
slash is non-empty. Other providers in this repo validate annotation format and throw early when
invalid (example: Dependabot project-slug parsing).

workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]
workspaces/scorecard/plugins/scorecard-backend-module-dependabot/src/metricProviders/DependabotMetricProvider.ts[94-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`parseProjectKeyAnnotation` does not validate empty parts around `/`, allowing invalid annotations like `internal/` or `/my-project`. This pushes failures to later API calls and makes errors harder to diagnose.

### Issue Context
The annotation is required for these providers and is user-authored, so early validation produces better error messages.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[92-106]

### Proposed fix
- Trim the annotation string.
- If a `/` is present, ensure both `instanceName` and `projectKey` are non-empty; otherwise throw an Error like:
 `Invalid &#x27;sonarqube.org/project-key&#x27; annotation &#x27;&lt;value&gt;&#x27; (expected &#x27;&lt;instance&gt;/&lt;projectKey&gt;&#x27; or &#x27;&lt;projectKey&gt;&#x27;)`.
- Add unit tests for `internal/`, `/my-project`, and `internal/my-project`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Config example uses wrong key 🐞 Bug ⚙ Maintainability
Description
The repo app-config example documents sonarqube.token, but the implementation and schema use
sonarqube.apiKey. Users copying this example will configure auth incorrectly and end up sending
unauthenticated requests.
Code

workspaces/scorecard/app-config.yaml[R121-125]

+# SonarQube configuration (optional - defaults to https://sonarcloud.io, no auth for public projects)
+# sonarqube:
+#   baseUrl: https://sonarcloud.io
+#   token: ${SONARQUBE_TOKEN}
+
Evidence
The app-config example uses token, while both the config schema and README define the field as
apiKey. The runtime client reads apiKey from config and will ignore token.

workspaces/scorecard/app-config.yaml[121-125]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[33-38]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[58-63]
workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[62-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The example configuration in `workspaces/scorecard/app-config.yaml` documents `sonarqube.token`, but the module reads `sonarqube.apiKey`. This can cause incorrect configurations when users copy/paste the example.

### Issue Context
Both `config.d.ts` and the module README document the key as `apiKey`.

### Fix Focus Areas
- workspaces/scorecard/app-config.yaml[121-125]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[33-38]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[58-63]

### Proposed fix
- Update the commented example to:
 `apiKey: ${SONARQUBE_API_KEY}` (or `${SONARQUBE_TOKEN}` if you prefer, but keep the config key name `apiKey`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
Comment on lines +63 to +68
/**
* The api key to access the sonarqube instance.
* @visibility secret
*/
apiKey: string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Instance apikey wrongly required 🐞 Bug ✓ Correctness

The config schema requires sonarqube.instances[].apiKey, but the README/tests/implementation
support instances without an apiKey for public projects. This mismatch will cause config schema
validation to reject documented configurations.
Agent Prompt
### Issue description
`config.d.ts` requires `sonarqube.instances[].apiKey`, but the implementation and documentation allow instances without an apiKey (for public projects). This will cause Backstage config schema validation to fail for valid configs.

### Issue Context
README and tests demonstrate an instance without `apiKey`.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/config.d.ts[39-75]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/README.md[65-79]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.test.ts[206-223]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[46-55]

### Proposed fix
- Change `instances[].apiKey: string` to `instances[].apiKey?: string`.
- Update the JSDoc comment to state apiKey is optional for public projects (matching top-level `apiKey?: string`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +37 to +69
private resolveInstance(instanceName?: string): SonarQubeInstance {
const sonarqubeConfig = this.config.getOptionalConfig('sonarqube');

if (instanceName && sonarqubeConfig) {
const instances =
sonarqubeConfig.getOptionalConfigArray('instances') ?? [];
const instance = instances.find(
i => i.getString('name') === instanceName,
);
if (instance) {
return {
baseUrl: instance.getString('baseUrl').replace(/\/$/, ''),
apiKey: instance.getOptionalString('apiKey'),
authType:
(instance.getOptionalString('authType') as
| 'Bearer'
| 'Basic'
| undefined) ?? 'Basic',
};
}
throw new Error(
`SonarQube instance '${instanceName}' not found in configuration`,
);
}

return {
baseUrl: (
sonarqubeConfig?.getOptionalString('baseUrl') ?? DEFAULT_BASE_URL
).replace(/\/$/, ''),
apiKey: sonarqubeConfig?.getOptionalString('apiKey'),
authType: 'Basic',
};
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Instance prefix ignored silently 🐞 Bug ✓ Correctness

If an entity uses an instance-prefixed project key (e.g. internal/my-project) but the sonarqube
config block is missing, resolveInstance ignores the instanceName and falls back to the default
instance. This can query the wrong SonarQube/SonarCloud instance without any explicit error about
the missing instance configuration.
Agent Prompt
### Issue description
`SonarQubeClient.resolveInstance(instanceName)` ignores a provided `instanceName` when the `sonarqube` config block is missing, silently falling back to the default instance. This can cause requests to go to the wrong SonarQube/SonarCloud instance.

### Issue Context
Instance names come from entity annotations (`sonarqube.org/project-key`) via `parseProjectKeyAnnotation`, and metric providers pass `instanceName` through to the client.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[37-69]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeBooleanMetricProvider.ts[84-96]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeConfig.ts[32-43]

### Proposed fix
- If `instanceName` is provided but `sonarqubeConfig` is missing, throw a clear error like:
  `SonarQube instance '<name>' requested but no 'sonarqube' configuration found`.
- (Optional) Add a unit test covering `instanceName` + empty root config to prevent regressions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +114 to +129
case 'security_rating': {
const measures = await this.client.getMeasures(
projectKey,
SONARQUBE_API_METRIC_KEYS.security_rating,
instanceName,
);
return measures.security_rating;
}
case 'security_issues': {
const measures = await this.client.getMeasures(
projectKey,
SONARQUBE_API_METRIC_KEYS.security_issues,
instanceName,
);
return measures.vulnerabilities;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Missing measures return undefined 🐞 Bug ⛯ Reliability

SonarQubeNumberMetricProvider returns measures.security_rating/measures.vulnerabilities
without verifying they exist in the response. If SonarQube omits a measure, calculateMetric
returns undefined, violating the MetricProvider contract and resulting in null/undefined metric
values stored without an error.
Agent Prompt
### Issue description
`SonarQubeNumberMetricProvider.calculateMetric` can return `undefined` when a requested measure is missing from the SonarQube API response (e.g., `security_rating` or `vulnerabilities` not present). This violates the `MetricProvider` contract (must return a number) and leads to silent null/undefined values being stored.

### Issue Context
`SonarQubeClient.getMeasures` only adds metrics that appear in `data.component.measures`, and the provider accesses keys directly.

### Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/clients/SonarQubeClient.ts[120-140]
- workspaces/scorecard/plugins/scorecard-backend-module-sonarqube/src/metricProviders/SonarQubeNumberMetricProvider.ts[114-129]
- workspaces/scorecard/plugins/scorecard-node/src/api/MetricProvider.ts[25-65]

### Proposed fix
Choose one consistent behavior:
- **Preferred (fail fast):** After fetching measures, check for the requested key(s); if missing, throw an Error like `Missing SonarQube measure 'security_rating' for project '<key>'` so the scheduler stores `error_message`.
- **Alternative (default value):** Return `0` when a measure is absent, if that better matches scorecard semantics.

Also consider guarding against `data.component?.measures` being absent and throwing a clearer error message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

…blic

Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@christoph-jerolimov christoph-jerolimov changed the title feat(scorecard): Add SonarQube metric providers for quality gate status, open issues, security rating, and security issues feat(scorecard): Add SonarQube metric providers Mar 19, 2026
Add metrics for code coverage, code duplications, security review rating,
security hotspots, reliability rating/issues, and maintainability rating/issues.

Refactors calculateMetric to use a data-driven API key mapping table instead
of a switch statement, and deduplicates rating thresholds into a shared constant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Christoph Jerolimov <jerolimov+git@redhat.com>
@sonarqubecloud
Copy link

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