theonedev/onedev · error · IllegalArgumentException

Invalid parameter 'attachment-group'

Error message

Invalid parameter 'attachment-group'

What it means

AttachmentResource rejects an 'attachment-group' value containing '..' with IllegalArgumentException 'Invalid parameter attachment-group'. Like the artifact resource, this is a path-safety check preventing the group value from being used for traversal when resolved later.

Solutions

  1. Pass the plain group identifier (issue number, PR key, UUID, build number) without any path characters
  2. Normalize/validate the group value in your script before building the URL
  3. If a scanner flagged this, treat it as expected: the server blocks traversal by design

Example fix

// before
GET /~resources/attachment?project=1&attachment-group=../42&attachment=f.txt
// after
GET /~resources/attachment?project=1&attachment-group=42&attachment=f.txt
Defensive patterns

Strategy: validation

Validate before calling

if (String(attachmentGroup).includes('..')) throw new Error('attachment-group must be a plain identifier, not a path: ' + attachmentGroup);

Type guard

function isSafeAttachmentGroup(g) { return typeof g === 'string' && g.trim().length > 0 && !g.includes('..'); }

Prevention

When it happens

Trigger: Calling the attachment resource with attachment-group containing a '..' substring, e.g. attachment-group=../other.

Common situations: Malformed or attacker-crafted URLs (often security scanners); scripts concatenating path components into the group parameter; copy errors where directory separators leaked into the value.

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/4f57269368e2a7b3. Report an issue: GitHub.

Appendix: source

Thrown at server-core/src/main/java/io/onedev/server/web/resource/AttachmentResource.java:68

	private static final String PARAM_PROJECT = "project";
	
	private static final String PARAM_ATTACHMENT_GROUP = "attachment-group";
	
	private static final String PARAM_ATTACHMENT = "attachment";
	
	public static final String PARAM_AUTHORIZATION = "authorization";
	
	@Override
	protected ResourceResponse newResourceResponse(Attributes attributes) {
		PageParameters params = attributes.getParameters();
		
		Long projectId = params.get(PARAM_PROJECT).toLong();
		String attachmentGroup = params.get(PARAM_ATTACHMENT_GROUP).toString();
		
		if (StringUtils.isBlank(attachmentGroup))
			throw new IllegalArgumentException("Parameter 'attachment-group' has to be specified");
		else if (attachmentGroup.contains(".."))
			throw new IllegalArgumentException("Invalid parameter 'attachment-group'");

		if (!SecurityUtils.isSystem()) {
			Project project = OneDev.getInstance(ProjectService.class).load(projectId);
			
			String authorization = params.get(PARAM_AUTHORIZATION).toOptionalString();
			if (authorization == null 
					|| !new String(CryptoUtils.decrypt(Base64.decodeBase64(authorization)), UTF_8).equals(attachmentGroup)) {
				Issue issue;
				Build build;
				if (OneDev.getInstance(PullRequestService.class).find(attachmentGroup) != null
						|| OneDev.getInstance(CodeCommentService.class).findByUUID(attachmentGroup) != null) {
					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()))

View on GitHub (pinned to d44925c47c)