{"record":{"id":"f06a6cb3f476edc6","repo":"theonedev/onedev","slug":"unauthorized-f06a6c","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/AttachmentResource.java","lineNumber":81,"sourceCode":"\t\tString attachmentGroup = params.get(PARAM_ATTACHMENT_GROUP).toString();\n\t\t\n\t\tif (StringUtils.isBlank(attachmentGroup))\n\t\t\tthrow new IllegalArgumentException(\"Parameter 'attachment-group' has to be specified\");\n\t\telse if (attachmentGroup.contains(\"..\"))\n\t\t\tthrow new IllegalArgumentException(\"Invalid parameter 'attachment-group'\");\n\n\t\tif (!SecurityUtils.isSystem()) {\n\t\t\tProject project = OneDev.getInstance(ProjectService.class).load(projectId);\n\t\t\t\n\t\t\tString authorization = params.get(PARAM_AUTHORIZATION).toOptionalString();\n\t\t\tif (authorization == null \n\t\t\t\t\t|| !new String(CryptoUtils.decrypt(Base64.decodeBase64(authorization)), UTF_8).equals(attachmentGroup)) {\n\t\t\t\tIssue issue;\n\t\t\t\tBuild build;\n\t\t\t\tif (OneDev.getInstance(PullRequestService.class).find(attachmentGroup) != null\n\t\t\t\t\t\t|| OneDev.getInstance(CodeCommentService.class).findByUUID(attachmentGroup) != null) {\n\t\t\t\t\tif (!SecurityUtils.canReadCode(project))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if ((issue = OneDev.getInstance(IssueService.class).find(attachmentGroup)) != null) {\n\t\t\t\t\tif (!SecurityUtils.canAccessIssue(issue))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if ((build = OneDev.getInstance(BuildService.class).find(attachmentGroup)) != null) {\n\t\t\t\t\tif (!SecurityUtils.canAccessProject(build.getProject()))\n\t\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t} else if (!SecurityUtils.canAccessProject(project)) {\n\t\t\t\t\tthrow new UnauthorizedException();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tString attachment = params.get(PARAM_ATTACHMENT).toString();\n\t\tif (StringUtils.isBlank(attachment))\n\t\t\tthrow new IllegalArgumentException(\"attachment parameter has to be specified\");\n\t\telse if (attachment.contains(\"..\"))\n\t\t\tthrow new IllegalArgumentException(\"Invalid attachment parameter\");\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java#L63-L99","documentation":"For non-system callers lacking a valid encrypted 'authorization' parameter, AttachmentResource authorizes the attachment group by entity type: pull request/code comment groups require canReadCode(project), issue groups require canAccessIssue(issue), build groups require canAccessProject(build.getProject()), and unknown groups fall back to canAccessProject(project). Any failed check throws Shiro UnauthorizedException.","triggerScenarios":"An authenticated user without the needed permission requests an attachment: e.g. an issue attachment the user cannot access, a build attachment in a project they cannot access, or a group the user cannot resolve at all.","commonSituations":"Sharing attachment links with users lacking issue/project access; job attachments fetched with a foreign project's token; code comment attachments fetched by users without code read; stale links after permissions were tightened.","solutions":["Request access: ask an admin to grant code-read (for PR/comment attachments), issue access, or project access as appropriate to the attachment's origin","For CI, use the same project's job token or pass a valid 'authorization' parameter (the encrypted, Base64 token OneDev generates for the group)","Verify the attachment-group value actually refers to the entity you intend; an unknown group falls back to plain project access check","Re-obtain the link from the OneDev UI as the current user to confirm the entity and your access"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const canRead = await onedevApi.get(`/projects/${projectId}/permissions`);\nif (!canRead.canReadCode && !canRead.canAccess) throw new Error('insufficient permissions for this attachment group; request code read / issue access / project access as appropriate');","typeGuard":null,"tryCatchPattern":"try { await fetch(attachmentUrl); } catch (e) { if (e.status === 403) console.error('Unauthorized for attachment group: acquire the proper entity/project access or a valid encrypted authorization token'); }","preventionTips":["Ensure users have access to the entity backing the attachment group (issue, PR, comment, build)","Use OneDev-generated authorization tokens for programmatic group access","Re-verify links after permission tightening; use project-scoped tokens in CI"],"tags":["security","authorization","attachment"],"backgroundTag":"permission-denied","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}