{"record":{"id":"a721ef4f6bbe9271","repo":"SonarSource/sonarqube","slug":"insufficient-privileges-a721ef","errorCode":null,"errorMessage":"Insufficient privileges","messagePattern":"Insufficient privileges","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java","lineNumber":298,"sourceCode":"    }\n  }\n\n  private static ComponentRequest toComponentWsRequest(Request request) {\n    ComponentRequest componentRequest = new ComponentRequest()\n      .setComponent(request.mandatoryParam(PARAM_COMPONENT))\n      .setBranch(request.param(PARAM_BRANCH))\n      .setPullRequest(request.param(PARAM_PULL_REQUEST))\n      .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))\n      .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS));\n    checkRequest(!componentRequest.getMetricKeys().isEmpty(), \"At least one metric key must be provided\");\n    return componentRequest;\n  }\n\n  private void checkPermissions(ComponentDto baseComponent) {\n    if (!userSession.hasComponentPermission(ProjectPermission.USER, baseComponent) &&\n      !userSession.hasComponentPermission(ProjectPermission.SCAN, baseComponent) &&\n      !userSession.hasPermission(GlobalPermission.SCAN)) {\n      throw insufficientPrivilegesException();\n    }\n  }\n\n  private static class ComponentRequest {\n    private String component = null;\n    private String branch = null;\n    private String pullRequest = null;\n    private List<String> metricKeys = null;\n    private List<String> additionalFields = null;\n\n    private String getComponent() {\n      return component;\n    }\n\n    private ComponentRequest setComponent(@Nullable String component) {\n      this.component = component;\n      return this;\n    }","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java#L280-L316","documentation":"The api/measures/component endpoint requires the caller to have at least one of: User (Browse) permission on the component, Scan (Execute Analysis) permission on the component, or global Execute Analysis permission. checkPermissions throws 'Insufficient privileges' when none of the three holds, hiding whether the component exists.","triggerScenarios":"GET api/measures/component with component/branch keys referencing a project the user cannot browse and has no scan permission; anonymous request on a server where anonymous access is disabled.","commonSituations":"CI token (scan-only, no browse) querying measures of another team's project without global scan; dashboards using a personal token that lost project access after group membership changes; measures pulled for a branch the user cannot see.","solutions":["Grant the user Browse (User) permission on the component/project, or include the component in a group with Browse.","Alternatively grant global 'Execute Analysis' permission if the caller is a scanning account.","Check you are querying the right branch/component key; access is per-component, so a valid key on an unauthorized project still fails.","Confirm the token is not expired or belonging to a deactivated user."],"exampleFix":"// before\ncurl -u \"$TOKEN:\" \"$SONAR/api/measures/component?component=com.acme:app&metricKeys=ncloc\"\n// after: grant 'Browse' to the token's user on com.acme:app, or use an account with global Execute Analysis","handlingStrategy":"validation","validationCode":"const me = await get('/api/authentication/validate', { auth: token });\nconst perms = await get(`/api/permissions/authorization?projectKey=${encodeURIComponent(component)}`, { auth: token });\nif (!perms.permissions.includes('user') && !perms.permissions.includes('scan') && !perms.globalPermissions.includes('scan')) {\n  throw new Error(`No Browse/Scan permission on ${component}`);\n}","typeGuard":"function canReadComponent(authz) {\n  return Boolean(authz && (authz.permissions.includes('user') || authz.permissions.includes('scan') || (authz.globalPermissions || []).includes('scan')));\n}","tryCatchPattern":"try {\n  return await getMeasures(component);\n} catch (e) {\n  if (e.response && e.response.status === 403) return null; // skip inaccessible components in dashboards\n  throw e;\n}","preventionTips":["For CI tokens, prefer granting global Execute Analysis plus Browse on the projects queried.","Handle 403 by skipping components rather than failing whole dashboards.","Re-check permissions after group membership or project-access changes."],"tags":["sonarqube","webapi","measures","authorization"],"backgroundTag":"insufficient-permissions","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}