{"record":{"id":"5b5b7eb521ac4bad","repo":"theonedev/onedev","slug":"unauthorized-5b5b7e","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/rest/resource/BuildResource.java","lineNumber":65,"sourceCode":"public class BuildResource {\n\n\tprivate final BuildService buildService;\n\t\n\tprivate final AuditService auditService;\n\t\n\t@Inject\n\tpublic BuildResource(BuildService buildService, AuditService auditService) {\n\t\tthis.buildService = buildService;\n\t\tthis.auditService = auditService;\n\t}\n\n\t@Api(order=100)\n\t@Path(\"/{buildId}\")\n    @GET\n    public Build getBuild(@PathParam(\"buildId\") Long buildId) {\n\t\tBuild build = buildService.load(buildId);\n    \tif (!SecurityUtils.canAccessProject(build.getProject())) \n\t\t\tthrow new UnauthorizedException();\n    \treturn build;\n    }\n\n\t@Api(order=150, description = \"Get list of <a href='/~help/api/io.onedev.server.rest.BuildLabelResource'>labels</a>\")\n\t@Path(\"/{buildId}/labels\")\n\t@GET\n\tpublic Collection<BuildLabel> getLabels(@PathParam(\"buildId\") Long buildId) {\n\t\tBuild build = buildService.load(buildId);\n\t\tif (!SecurityUtils.canAccessProject(build.getProject()))\n\t\t\tthrow new UnauthorizedException();\n\t\treturn build.getLabels();\n\t}\n\t\n\t@Api(order=200)\n\t@Path(\"/{buildId}/params\")\n    @GET\n    public Collection<BuildParam> getParams(@PathParam(\"buildId\") Long buildId) {\n\t\tBuild build = buildService.load(buildId);","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/rest/resource/BuildResource.java#L47-L83","documentation":"BuildResource.getBuild loads a build by id and throws UnauthorizedException unless SecurityUtils.canAccessProject(build.getProject()) passes. Even a valid build id returns 401/403 if the caller cannot access the containing project. This enforces project-level access control on the REST build API.","triggerScenarios":"GET /rest/builds/{buildId} with a build in a project the user cannot see — private project membership missing, anonymous request, API token role without project read access, or simply a wrong guess of buildId pointing into another project.","commonSituations":"Scripts using a token created under a different account than expected; users removed from the project but caching old build ids; guessing/incrementing build ids; forks where the caller only has access to the upstream project.","solutions":["Request access (read) to the project owning the build, or use a token from a member account.","Verify the buildId belongs to a project the token can access — list accessible builds first via project-scoped endpoints.","Check the Authorization header is present and the token is valid/not expired.","As admin, add the user or token's role to the project with at least read permission."],"exampleFix":"// before: fetch build directly by id\nBuild b = client.path(\"/rest/builds/\" + id).get(Build.class);\n// after: scope to a project you can access\nList<Build> builds = client.path(\"/rest/projects/\" + projectPath + \"/builds\").get();\nBuild b = builds.stream().filter(x -> x.getId().equals(id)).findFirst().orElseThrow();","handlingStrategy":"validation","validationCode":"// before calling, ensure project access via a lightweight GET\nResponse r = client.path(\"/rest/projects/\" + projectPath).get();\nif (r.getStatus() == 401 || r.getStatus() == 403) {\n    throw new SecurityException(\"Account cannot access project \" + projectPath + \" — request membership\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    Build b = client.path(\"/rest/builds/\" + id).get(Build.class);\n} catch (NotAuthorizedException e) {\n    throw new AccessDeniedException(\"No access to project owning build \" + id, e);\n}","preventionTips":["Always resolve builds through a project you can access rather than raw ids.","Keep service-account project memberships in sync with the projects automation touches.","Check token expiry — an expired token looks like a permission failure.","Cache the list of accessible projects and filter target build ids against it."],"tags":["rest","authorization","access-control"],"backgroundTag":"permission-denied","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}