{"record":{"id":"a24ea3860362aadd","repo":"apache/druid","slug":"skipping-deep-storage-directory-kill-invalid-path","errorCode":null,"errorMessage":"Skipping deep storage directory kill: invalid path[%s]","messagePattern":"Skipping deep storage directory kill: invalid path\\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"extensions-core/hdfs-storage/src/main/java/org/apache/druid/storage/hdfs/HdfsDataSegmentKiller.java","lineNumber":196,"sourceCode":"   */\n  @Nullable\n  private Path constructHdfsDeletePath(String relativePath)\n  {\n    if (Strings.isNullOrEmpty(relativePath)) {\n      log.warn(\"Skipping deep storage directory kill: relative path is empty\");\n      return null;\n    }\n    if (relativePath.charAt(0) == '/') {\n      log.warn(\"Skipping deep storage directory kill: relative path must not be absolute, got [%s]\", relativePath);\n      return null;\n    }\n    if (relativePath.indexOf('\\\\') >= 0) {\n      log.warn(\"Skipping deep storage directory kill: backslash not allowed in path [%s]\", relativePath);\n      return null;\n    }\n    for (String segment : StringUtils.splitPreserveAllTokens(relativePath, '/')) {\n      if (segment.isEmpty() || \"..\".equals(segment)) {\n        log.warn(\"Skipping deep storage directory kill: invalid path[%s]\", relativePath);\n        return null;\n      }\n    }\n\n    if (storageDirectory == null) {\n      log.warn(\"Skipping deep storage directory kill: storage directory not configured\");\n      return null;\n    }\n\n    final String hdfsRelativePath = relativePath.replace(':', '_');\n    final String storageDirectoryString = storageDirectory.toString();\n    final String sep = storageDirectoryString.endsWith(Path.SEPARATOR) ? \"\" : Path.SEPARATOR;\n    return new Path(storageDirectoryString + sep + hdfsRelativePath);\n  }\n}\n","sourceCodeStart":178,"sourceCodeEnd":212,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/hdfs-storage/src/main/java/org/apache/druid/storage/hdfs/HdfsDataSegmentKiller.java#L178-L212","documentation":"constructHdfsDeletePath splits the relative path on '/' and rejects it if any component is empty (consecutive slashes, leading/trailing slash) or equals '..' (parent-directory traversal). This prevents accidentally deleting directories outside the intended segment location. It logs this warning and returns null so the kill is skipped.","triggerScenarios":"A DataSegment relative path like 'a//b', 'a/', or containing '..' segments is passed to dirToDelete during kill.","commonSituations":"Hand-edited segment loadSpecs, buggy custom deep-storage adapters producing double slashes, or malicious/corrupt metadata rows containing '..' traversal.","solutions":["Normalize the segment path in the metadata store (collapse duplicate slashes, remove '..')","Re-push the affected segments to get clean canonical paths","If the segment truly can't be fixed, delete the directory manually with hdfs dfs -rm -r after verifying the target"],"exampleFix":"// before\n\"path\": \"datasource//2019-01-01T00:00:00.000Z_../index.zip\"\n// after\n\"path\": \"datasource/2019-01-01T00:00:00.000Z_2019-01-02T00:00:00.000Z/2020-01-01T00:00:00.000Z/0/index.zip\"","handlingStrategy":"validation","validationCode":"boolean valid = java.util.Arrays.stream(segmentPath.split(\"/\", -1)).noneMatch(s -> s.isEmpty() || s.equals(\"..\"));\nif (!valid) { throw new IllegalArgumentException(\"invalid segment path: \" + segmentPath); }","typeGuard":"static boolean isSafeRelativePath(String p) {\n  if (p == null) return false;\n  for (String s : p.split(\"/\", -1)) { if (s.isEmpty() || s.equals(\"..\")) return false; }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["Canonicalize paths before storing them in loadSpecs","Treat '..' in stored paths as corruption and re-push","Audit metadata for path anomalies after migrations"],"tags":["hdfs","segment-kill","path-validation","path-traversal","druid"],"backgroundTag":"path-traversal-blocked","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}