{"record":{"id":"bd8df405d525abc8","repo":"SonarSource/sonarqube","slug":"insufficient-privileges-bd8df4","errorCode":null,"errorMessage":"Insufficient privileges","messagePattern":"Insufficient privileges","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeMonitoringMetricAction.java","lineNumber":72,"sourceCode":"  public void define(WebService.NewController context) {\n    context.createAction(\"metrics\")\n      .setSince(\"9.3\")\n      .setDescription(\"\"\"\n        Return monitoring metrics in Prometheus format. \\s\n        Support content type 'text/plain' (default) and 'application/openmetrics-text'.\n        This endpoint can be accessed using a Bearer token, which needs to be defined in sonar.properties with the 'sonar.web.systemPasscode' key.\"\"\")\n      .setChangelog(\n        new Change(\"2026.3\", \"Added 'sonarqube_elasticsearch_read_only_indices_total' and 'sonarqube_elasticsearch_disk_usage_percent' metrics\"))\n      .setResponseExample(Resources.getResource(SafeModeMonitoringMetricAction.class, \"monitoring-metrics.txt\"))\n      .setHandler(this);\n    isWebUpGauge.set(1D);\n  }\n\n  @Override\n  public void handle(Request request, Response response) throws Exception {\n\n    if (!systemPasscode.isValid(request) && !isSystemAdmin() && !bearerPasscode.isValid(request)) {\n      throw new ForbiddenException(\"Insufficient privileges\");\n    }\n\n    String requestContentType = request.header(\"accept\").orElse(null);\n    String contentType = TextFormat.chooseContentType(requestContentType);\n\n    response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);\n    response.stream().setStatus(200);\n\n    try (Writer writer = new OutputStreamWriter(response.stream().output(), UTF_8)) {\n      TextFormat.writeFormat(contentType, writer, CollectorRegistry.defaultRegistry.metricFamilySamples());\n      writer.flush();\n    }\n  }\n\n  public boolean isSystemAdmin() {\n    // No authenticated user in safe mode\n    return false;\n  }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeMonitoringMetricAction.java#L54-L90","documentation":"SafeModeMonitoringMetricAction exposes Prometheus-format monitoring metrics in safe mode. Access requires one of: the system passcode, system administrator authentication, or the monitoring bearer passcode; if none validates, it throws ForbiddenException 'Insufficient privileges'.","triggerScenarios":"Scraping the safe-mode metrics endpoint without X-Sonar-Passcode, without admin user credentials, and without a valid sonar.monitoringPasscode bearer token — e.g. Prometheus scrape config missing the authorization header.","commonSituations":"Prometheus/Grafana setups where sonar.monitoringPasscode was introduced or rotated but the scrape job's bearer_token was not updated; metrics scraped from a clustered node in safe mode with only anonymous access.","solutions":["Add the correct bearer token (sonar.monitoringPasscode value) to the Prometheus scrape config's authorization credentials.","Alternatively pass the system passcode via X-Sonar-Passcode, or authenticate the scrape as a system administrator.","Confirm the passcode properties on the server match the scraper secret and reload the scraper after rotation."],"exampleFix":"// before\nscrape_configs: [{ job_name: 'sonarqube', static_configs: [{ targets: ['sonarqube:9000'] }] }]\n// after\nscrape_configs: [{ job_name: 'sonarqube', static_configs: [{ targets: ['sonarqube:9000'] }], authorization: { credentials: '${SONAR_MONITORING_PASSCODE}' } }]","handlingStrategy":"validation","validationCode":"const auth = monitoringPasscode ?? systemPasscode ?? adminToken;\nif (!auth) throw new Error('no valid credential available for metrics endpoint');","typeGuard":null,"tryCatchPattern":"try {\n  await scrapeMetrics({ authorization: `Bearer ${monitoringPasscode}` });\n} catch (e) {\n  if (e.status === 403) reloadPasscodeAndRetryOnce();\n  else throw e;\n}","preventionTips":["Set sonar.monitoringPasscode and mirror it in the Prometheus scrape config.","Alternatively authenticate as a system administrator if passcode scraping is not desired.","Update scrape jobs immediately after passcode rotation."],"tags":["sonarqube","webapi","prometheus","passcode"],"backgroundTag":"insufficient-permissions","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}