theonedev/onedev · error · IllegalArgumentException
Invalid attachment parameter
Error message
Invalid attachment parameter
What it means
AttachmentResource rejects an 'attachment' parameter containing ".." with IllegalArgumentException("Invalid attachment parameter"). This is a path-traversal guard: the attachment name must be a plain file name, never a relative path escaping the attachment storage directory.
Source
Thrown at server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java:98
if (!SecurityUtils.canReadCode(project))
throw new UnauthorizedException();
} else if ((issue = OneDev.getInstance(IssueService.class).find(attachmentGroup)) != null) {
if (!SecurityUtils.canAccessIssue(issue))
throw new UnauthorizedException();
} else if ((build = OneDev.getInstance(BuildService.class).find(attachmentGroup)) != null) {
if (!SecurityUtils.canAccessProject(build.getProject()))
throw new UnauthorizedException();
} else if (!SecurityUtils.canAccessProject(project)) {
throw new UnauthorizedException();
}
}
}
String attachment = params.get(PARAM_ATTACHMENT).toString();
if (StringUtils.isBlank(attachment))
throw new IllegalArgumentException("attachment parameter has to be specified");
else if (attachment.contains(".."))
throw new IllegalArgumentException("Invalid attachment parameter");
ResourceResponse response = new ResourceResponse();
response.setContentLength(getAttachmentService().getAttachmentInfo(projectId, attachmentGroup, attachment).getLength());
response.getHeaders().addHeader("X-Content-Type-Options", "nosniff");
response.setContentType(MimeTypes.OCTET_STREAM);
response.setFileName(URLEncoder.encode(attachment, UTF_8));
response.setWriteCallback(new WriteCallback() {
@Override
public void writeData(Attributes attributes) throws IOException {
String activeServer = getProjectService().getActiveServer(projectId, true);
ClusterService clusterService = OneDev.getInstance(ClusterService.class);
if (activeServer.equals(clusterService.getLocalServerAddress())) {
read(getAttachmentService().getAttachmentLockName(projectId, attachmentGroup), () -> {
File attachmentFile = new File(getAttachmentService().getAttachmentGroupDir(projectId, attachmentGroup), attachment);View on GitHub (pinned to d44925c47c)
Solutions
- Pass only the bare attachment file name as returned when it was uploaded (no slashes, no "..").
- If a file name genuinely contains "..", rename it on upload and use the new name.
- If this appears in logs as probing, treat it as an attack attempt; block or rate-limit the source.
Example fix
// before GET /~resource/attachments/1/issue-42?attachment=../../secrets.txt -> 400 Invalid attachment parameter // after GET /~resource/attachments/1/issue-42?attachment=secrets.txt
Defensive patterns
Strategy: validation
Validate before calling
if (attachmentName.includes('..') || attachmentName.includes('/') || attachmentName.includes('\\')) {
throw new Error('attachment must be a bare file name without path segments');
} Type guard
function isSafeAttachmentName(name) {
return typeof name === 'string' && /^[^/\\]+$/.test(name) && !name.includes('..');
} Try / catch
try { await fetch(urlWith(attachment)); } catch (e) { if (String(e.message).includes('Invalid attachment parameter')) sanitizeAndRetry(attachment); else throw e; } Prevention
- Never pass user-controlled paths directly into the attachment parameter
- Store and echo back the exact name returned at upload time
- Sanitize names at upload (strip path separators and '..')
When it happens
Trigger: GET with ?attachment=../../etc/passwd or any value containing the ".." substring — including legitimate-looking names like "my..file.txt".
Common situations: Malicious/scanner probes against the endpoint; scripts that pass full paths (e.g. "dir/file.txt") or names containing ".." instead of the bare file name returned by the attachment listing.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/5a060ee1d6e4e422.
Report an issue: GitHub.