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
- Ensure artifactPath always starts with "artifacts/" before calling downloadArtifact.
- Strip or normalize any '..' segments from the path (e.g. normalize relative references before invoking).
- If the target lives outside the artifacts subtree, use the appropriate API instead of downloadArtifact.
- 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
- Always build artifact paths from known-safe components, never raw user input.
- Normalize paths and reject '..' before any file-serving API call.
- Keep the mandatory 'artifacts/' prefix as part of a single path-building helper.
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.