{"record":{"id":"5a457541a377bb20","repo":"elastic/elasticsearch","slug":"extra-config-file-destination-can-t-be-relative-w","errorCode":null,"errorMessage":"extra config file destination can't be relative, was {} for {}","messagePattern":"extra config file destination can't be relative, was (.+?) for (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java","lineNumber":718,"sourceCode":"            if (Files.exists(destination) == false) {\n                fileSystemOperations.copy(spec -> {\n                    if (module.get().getName().toLowerCase().endsWith(\".zip\")) {\n                        spec.from(archiveOperations.zipTree(module));\n                    } else if (module.get().isDirectory()) {\n                        spec.from(module);\n                    } else {\n                        throw new IllegalArgumentException(\"Not a valid module \" + module + \" for \" + this);\n                    }\n                    spec.into(destination);\n                });\n            }\n        }\n    }\n\n    @Override\n    public void extraConfigFile(String destination, File from) {\n        if (destination.contains(\"..\")) {\n            throw new IllegalArgumentException(\"extra config file destination can't be relative, was \" + destination + \" for \" + this);\n        }\n        extraConfigFiles.put(destination, from);\n    }\n\n    @Override\n    public void extraConfigFile(String destination, File from, PropertyNormalization normalization) {\n        if (destination.contains(\"..\")) {\n            throw new IllegalArgumentException(\"extra config file destination can't be relative, was \" + destination + \" for \" + this);\n        }\n        extraConfigFiles.put(destination, from, normalization);\n    }\n\n    @Override\n    public void extraJarFiles(FileCollection from) {\n        extraJarConfigurations.add(from);\n    }\n\n    @Override","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java#L700-L736","documentation":"Thrown by the single-arg extraConfigFile(destination, from) overload when the destination string contains '..'. The check blocks path traversal: destinations are resolved against the config dir, so '..' would escape it and potentially overwrite arbitrary files in the distro. IllegalArgumentException because it is a misuse of the API, not a runtime cluster fault.","triggerScenarios":"Calling extraConfigFile('../elasticsearch.yml', file), extraConfigFile('certs/../../scripts/x', file), or any destination string containing the literal substring '..' (the check is naive: it flags '..' anywhere, including inside filenames like 'my..file').","commonSituations":"Deliberate attempt to write outside config/. Accidental absolute path or Windows backslash causing the '..' substring. Filename containing '..' (false positive due to the substring check).","solutions":["Use a destination path that stays under the config directory (no '..').","If you need a file outside config/, use a different hook (e.g. extraJarFiles for lib, or write a custom task).","If '..' appears inside a legitimate filename, rename the file to avoid the substring.","On Windows, use forward slashes or a plain filename; avoid mixing separators that introduce '..'."],"exampleFix":"// before: tries to escape config dir\nextraConfigFile('../elasticsearch.yml', myFile)\n// after: keep destination under config/\nextraConfigFile('elasticsearch.yml', myFile)","handlingStrategy":"validation","validationCode":"static String safeExtraConfigDest(String dest) {\n    if (dest.contains(\"..\")) {\n        throw new IllegalArgumentException(\"extra config dest must not contain '..': \" + dest);\n    }\n    return dest;\n}\n// Use: node.extraConfigFile(safeExtraConfigDest(\"log4j2.xml\"), file);","typeGuard":"// True when destination stays within the config tree (no traversal)\nstatic boolean isSafeConfigDest(String dest) {\n    return !dest.contains(\"..\") && !dest.startsWith(\"/\");\n}","tryCatchPattern":null,"preventionTips":["Keep destinations relative to the config dir and free of '..'.","Avoid filenames containing '..' (the substring check is naive).","Prefer forward-slash relative paths."],"tags":["testclusters","config","security","validation","path-traversal"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}