{"record":{"id":"bc582a2807d1fb0c","repo":"theonedev/onedev","slug":"invalid-report-name-bc582a","errorCode":null,"errorMessage":"Invalid report name","messagePattern":"Invalid report name","errorType":"validation","errorClass":"ExplicitException","httpStatus":400,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/codequality/UnitTestReport.java","lineNumber":187,"sourceCode":"\t\t\t\tbuilder.header(AUTHORIZATION, BEARER + \" \"\n\t\t\t\t\t\t+ clusterService.getCredential());\n\t\t\t\ttry (Response response = builder.get()) {\n\t\t\t\t\tcheckStatus(response);\n\t\t\t\t\ttry (var is = response.readEntity(InputStream.class)) {\n\t\t\t\t\t\tIOUtils.copy(is, os, BUFFER_SIZE);\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tclient.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void checkReportName(String reportName) {\n\t\tif (reportName.contains(\"..\"))\n\t\t\tthrow new ExplicitException(\"Invalid report name\");\n\t}\n\t\n\tpublic void writeTo(File reportDir) {\n\t\tFile reportFile = new File(reportDir, REPORT);\n\t\ttry (OutputStream os = new BufferedOutputStream(new FileOutputStream(reportFile), BUFFER_SIZE)) {\n\t\t\tSerializationUtils.serialize(this, os);\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\t\n\tpublic int getTestDuration() {\n\t\tint testDuration = 0;\n\t\tfor (TestSuite testSuite: getTestSuites())\n\t\t\ttestDuration += testSuite.getDuration()/1000;\n\t\treturn testDuration;\n\t}\n\t","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/codequality/UnitTestReport.java#L169-L205","documentation":"checkReportName is the shared guard used by readFrom and downloadArtifact to reject report names containing '..'. Unit test report names map directly to directory names on disk, so '..' would allow escaping the reports directory. Any report name containing the two-dot sequence is rejected with 'Invalid report name'.","triggerScenarios":"Calling UnitTestReport.readFrom(...) or UnitTestReport.downloadArtifact(...) with reportName containing '..'; passing a user-supplied report name straight from a URL without validation.","commonSituations":"Crafted or accidental '..' in the report segment of an HTTP request URL; clients concatenating directory components into the report name; test harnesses generating report names with '..' separators.","solutions":["Sanitize reportName to remove any '..' sequence before passing it to readFrom/downloadArtifact.","Use only the report name exactly as published by the build's report publisher (no path components).","Encode path components properly in URLs so '..' is not decoded into the name server-side.","Reject invalid report names at your own API boundary before calling the library."],"exampleFix":"// before\nString reportName = request.getPath().substring(afterPrefix);\nUnitTestReport.downloadArtifact(projectId, buildNumber, reportName, artifactPath, os);\n// after\nString reportName = request.getPath().substring(afterPrefix);\nif (reportName.contains(\"..\"))\n    throw new WebException(400, \"Invalid report name\");\nUnitTestReport.downloadArtifact(projectId, buildNumber, reportName, artifactPath, os);","handlingStrategy":"validation","validationCode":"if (reportName == null || reportName.contains(\"..\"))\n    throw new IllegalArgumentException(\"Invalid report name: \" + reportName);","typeGuard":"boolean isSafeReportName(String reportName) {\n    return reportName != null && !reportName.contains(\"..\");\n}","tryCatchPattern":"try {\n    UnitTestReport.readFrom(lockName, file);\n} catch (ExplicitException e) {\n    if (e.getMessage().equals(\"Invalid report name\"))\n    throw new BadRequestException(\"Report name must not contain path components\");\n}","preventionTips":["Sanitize report names taken from URLs before passing them to report APIs.","Only use report names as published by the build's report publisher.","Reject path-like names at your own API boundary as defense in depth."],"tags":["path-traversal","validation","http-api"],"backgroundTag":"path-traversal-blocked","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}