theonedev/onedev · error · ExplicitException

Invalid request path

Error message

Invalid request path

What it means

UnitTestReport.downloadArtifact validates the requested artifact path before reading files from a build's unit test report directory. Any path that does not start with the 'artifacts/' prefix or that contains '..' (parent-directory traversal) is rejected with this ExplicitException. It is OneDev's first-line defense against path traversal when serving report artifacts over HTTP.

Source

Thrown at server-core/src/main/java/io/onedev/server/codequality/UnitTestReport.java:142

	}

	private static boolean existsIn(File reportDir) {
		return new File(reportDir, REPORT).isFile();
	}

	@Nullable
	public static UnitTestReport readFrom(Build build, String reportName) {
		checkReportName(reportName);
		Long projectId = build.getProject().getId();
		return OneDev.getInstance(ProjectService.class).runOnActiveServer(projectId,
				new ReadReport(projectId, build.getNumber(), reportName));
	}

	public static void downloadArtifact(Long projectId, Long buildNumber, String reportName,
			String artifactPath, OutputStream os) {
		checkReportName(reportName);
		if (artifactPath.contains("..") || !artifactPath.startsWith(ARTIFACTS + "/"))
			throw new ExplicitException("Invalid request path");

		var clusterService = OneDev.getInstance(ClusterService.class);
		var activeServer = OneDev.getInstance(ProjectService.class).getActiveServer(projectId, true);
		if (activeServer.equals(clusterService.getLocalServerAddress())) {
			read(getReportLockName(projectId, buildNumber), () -> {
				File reportDir = getReportDir(projectId, buildNumber, reportName);
				File artifactFile = new File(reportDir, artifactPath).getCanonicalFile();
				if (!artifactFile.toPath().startsWith(reportDir.getCanonicalFile().toPath())
						|| !artifactFile.isFile()) {
					throw new ExplicitException("Invalid request path");
				}
				try (var is = new FileInputStream(artifactFile)) {
					IOUtils.copy(is, os, BUFFER_SIZE);
				}
				return null;
			});
		} else {
			Client client = ClientBuilder.newClient();

View on GitHub (pinned to d44925c47c)

Solutions

  1. Ensure artifactPath always starts with "artifacts/" before calling downloadArtifact.
  2. Strip or normalize any '..' segments from the path (e.g. normalize relative references before invoking).
  3. If the target lives outside the artifacts subtree, use the appropriate API instead of downloadArtifact.
  4. Log the offending path to find which caller is producing malformed paths.

Example fix

// before
downloadArtifact(projectId, buildNumber, reportName, "../../etc/passwd", os); // throws
// after
String safePath = "artifacts/" + artifactPath.replace("\\", "/");
if (!safePath.contains(".."))
    downloadArtifact(projectId, buildNumber, reportName, safePath, os);
Defensive patterns

Strategy: validation

Validate before calling

if (artifactPath == null || artifactPath.contains("..") || !artifactPath.startsWith("artifacts/"))
    throw new IllegalArgumentException("artifactPath must be a relative path under artifacts/");

Type guard

boolean isSafeArtifactPath(String p) {
    return p != null && !p.contains("..") && p.startsWith("artifacts/");
}

Try / catch

try {
    UnitTestReport.downloadArtifact(projectId, buildNumber, reportName, artifactPath, os);
} catch (ExplicitException e) {
    if (e.getMessage().equals("Invalid request path"))
    log.warn("Rejected artifact path: {}", artifactPath);
}

Prevention

When it happens

Trigger: Calling UnitTestReport.downloadArtifact(projectId, buildNumber, reportName, artifactPath, os) with artifactPath containing '..' anywhere, or with artifactPath not beginning with "artifacts/" (e.g. an absolute path, empty string, or a path rooted elsewhere).

Common situations: Client code or REST consumers passing a raw user-supplied path; constructing artifact paths by string concatenation that accidentally includes '../'; passing a path without the required 'artifacts/' prefix when resolving report attachments.

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/5280e6cad23b965c. Report an issue: GitHub.