{"record":{"id":"bb6bc14ef82160c8","repo":"theonedev/onedev","slug":"unauthorized-bb6bc1","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/web/resource/ArchiveResource.java","lineNumber":76,"sourceCode":"\tprotected ResourceResponse newResourceResponse(Attributes attributes) {\n\t\tPageParameters params = attributes.getParameters();\n\n\t\tLong projectId = params.get(PARAM_PROJECT).toLong();\n\t\t\n\t\tString revision = params.get(PARAM_REVISION).toString();\n\t\tif (StringUtils.isBlank(revision))\n\t\t\tthrow new IllegalArgumentException(\"revision parameter has to be specified\");\n\t\t\n\t\tString format = params.get(PARAM_FORMAT).toString();\n\t\tif (!FORMAT_ZIP.equals(format) && !FORMAT_TGZ.equals(format)) {\n\t\t\tthrow new IllegalArgumentException(\"format parameter should be specified either zip or tar.gz\");\n\t\t}\n\t\t\n\t\tif (!SecurityUtils.isSystem()) {\n\t\t\t// Perform database operations only if it is not a cluster access to avoid possible deadlocks\n\t\t\tProject project = OneDev.getInstance(ProjectService.class).load(projectId);\n\t\t\tif (!SecurityUtils.canReadCode(project)) \n\t\t\t\tthrow new UnauthorizedException();\n\t\t}\n\t\t\n\t\tResourceResponse response = new ResourceResponse();\n\t\tresponse.setContentType(MimeTypes.OCTET_STREAM);\n\t\t\n\t\tresponse.disableCaching();\n\t\t\n\t\ttry {\n\t\t\tString fileName;\n\t\t\tif (GitUtils.ref2branch(revision) != null)\n\t\t\t\tfileName = GitUtils.ref2branch(revision);\n\t\t\telse if (GitUtils.ref2tag(revision) != null)\n\t\t\t\tfileName = GitUtils.ref2tag(revision);\n\t\t\telse \n\t\t\t\tfileName = revision;\n\t\t\tif (FORMAT_ZIP.equals(format))\n\t\t\t\tfileName += \".zip\";\n\t\t\telse","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/web/resource/ArchiveResource.java#L58-L94","documentation":"ArchiveResource performs an authorization check: unless the caller is the internal system/cluster identity (SecurityUtils.isSystem()), it loads the project and throws Shiro's UnauthorizedException when SecurityUtils.canReadCode(project) is false. It means the authenticated user is not allowed to read code in the target project.","triggerScenarios":"An authenticated (or anonymous) user requests a project archive for a project where their account lacks code-read permission; a job token without code access calls the archive endpoint.","commonSituations":"Sharing archive URLs with users who are not project members; CI job secrets belonging to another project; permission changes after role removal; anonymous access when the project is private.","solutions":["Log in as a user with at least code read permission on the project, or request the project owner to grant 'Read code' via project/role permissions","If the request is from a CI job, use that project's own job token/secret rather than one from another project","Verify you are hitting the correct project id in the URL (wrong project id can point to a project you cannot read)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const me = await onedevApi.get('/me'); // ensure user/token has code read on the project\nconst perm = await onedevApi.get(`/projects/${projectId}/permissions`);\nif (!perm.canReadCode) throw new Error('user lacks code read permission on project ' + projectId);","typeGuard":null,"tryCatchPattern":"try { await fetch(archiveUrl); } catch (e) { if (e.status === 403 || /unauthorized/i.test(e.message)) console.error('Grant code read permission for this project or use a project member/token'); }","preventionTips":["Confirm project membership/role before hitting archive endpoints","Use per-project CI job tokens, not cross-project secrets","Re-test links after permission changes"],"tags":["security","authorization","archive"],"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"}