{"record":{"id":"04581d7e593ad9ea","repo":"apache/cassandra","slug":"illegal-file-to-fetch","errorCode":null,"errorMessage":"Illegal file to fetch: ","messagePattern":"Illegal file to fetch: ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/AsyncProfilerService.java","lineNumber":349,"sourceCode":"        try\n        {\n            maybeCreateProfilesLogDir();\n            return Arrays.stream(new File(logDir).list()).map(File::name).sorted().collect(toList());\n        }\n        catch (Throwable t)\n        {\n            return List.of();\n        }\n    }\n\n    @Override\n    public byte[] fetch(String resultFile) throws IOException\n    {\n        try\n        {\n            if (!Path.of(logDir, resultFile).toAbsolutePath().getParent().equals(Path.of(logDir)))\n            {\n                throw new IllegalArgumentException(\"Illegal file to fetch: \" + resultFile);\n            }\n            maybeCreateProfilesLogDir();\n            return Files.readAllBytes(new File(logDir, resultFile).toPath());\n        }\n        catch (NoSuchFileException t)\n        {\n            logger.error(\"Result file \" + resultFile + \" not found or error occurred while returning it.\", t);\n            throw t;\n        }\n    }\n\n    @Override\n    public void purge()\n    {\n        maybeCreateProfilesLogDir();\n        new File(logDir).deleteRecursive();\n    }\n","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/AsyncProfilerService.java#L331-L367","documentation":"AsyncProfilerService.fetch reads a previously generated profiling result file from the profiles log directory. To prevent path traversal it resolves the requested file and requires that its absolute parent equals the log directory; anything escaping that directory (e.g. '../secrets') is rejected with this IllegalArgumentException.","triggerScenarios":"Calling fetch(\"somefile\") where the resolved path's parent is not the profiles log dir — e.g. fetch(\"../cassandra.yaml\"), absolute paths, or names containing path separators.","commonSituations":"Tooling concatenating user input into the file name; attempting to download non-profile files through the profiling MBean; passing names produced by a different Cassandra version with different log dir layout.","solutions":["Pass only the plain file name as returned by the list() operation, with no slashes or '..' segments","List available results via the list() MBean operation and pick a name from it","Verify Path.of(logDir, name).toAbsolutePath().getParent() equals the log dir before calling"],"exampleFix":"// before\nbyte[] data = service.fetch(\"../heap.dump\");\n// after\nString name = (String) service.list().stream().filter(f -> f.endsWith(\".html\")).findFirst().get();\nbyte[] data = service.fetch(name);","handlingStrategy":"validation","validationCode":"Path expected = Path.of(logDir).toAbsolutePath();\nPath resolved = Path.of(logDir, name).toAbsolutePath();\nif (!expected.equals(resolved.getParent())) throw new IllegalArgumentException(\"file must be a bare name inside the log dir\");","typeGuard":null,"tryCatchPattern":"try { return svc.fetch(name); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith(\"Illegal file to fetch\")) return null; throw e; }","preventionTips":["Only fetch names returned by list()","Never interpolate user input into the file name","Reject any name containing '/', '\\\\', or '..' before calling"],"tags":["security","path-traversal","jmx"],"backgroundTag":"path-traversal-blocked","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}