{"record":{"id":"fcc042f4c2b80ab7","repo":"SonarSource/sonarqube","slug":"bad-filename-filename","errorCode":null,"errorMessage":"Bad filename: ${filename}","messagePattern":"Bad filename: (.+?)","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/BatchIndex.java","lineNumber":90,"sourceCode":"      throw new IllegalStateException(format(\"%s folder not found\", batchDir.getAbsolutePath()));\n    }\n    this.index = sb.toString();\n  }\n\n  @Override\n  public void stop() {\n    // Nothing to do\n  }\n\n  String getIndex() {\n    return index;\n  }\n\n  File getFile(String filename) {\n    try {\n      File input = new File(batchDir, filename);\n      if (!FilenameUtils.directoryContains(batchDir.getCanonicalPath(), input.getCanonicalPath()) || !input.exists()) {\n        throw new NotFoundException(\"Bad filename: \" + filename);\n      }\n      return input;\n    } catch (IOException e) {\n      throw new IllegalStateException(\"Can get file \" + filename, e);\n    }\n  }\n}\n","sourceCodeStart":72,"sourceCodeEnd":98,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/batch/BatchIndex.java#L72-L98","documentation":"BatchIndex.getFile resolves a requested scanner file name against the batch directory and rejects it unless the file's canonical path is contained within the batch directory AND the file exists, throwing NotFoundException 'Bad filename: <name>'. It is a path-traversal and existence guard for the batch file-serving endpoint.","triggerScenarios":"A batch WS request (get_file/check_location_of_file) asks for a filename containing '../' or absolute paths, or a name not present in the batch directory — including clients requesting jars that were removed.","commonSituations":"Scanner/server version mismatch where the scanner requests a jar no longer in lib/batch; malicious or buggy clients probing for traversal; symlinked batch dirs breaking canonical containment checks.","solutions":["Ensure the scanner and server are compatible versions and the scanner re-fetches the index (api/batch/index) before requesting files","Restore any missing jars in the batch directory from the distribution","Never hand-craft file paths in batch requests — use paths returned by get_index","If batch is behind a proxy/symlink, ensure the real canonical path stays inside the batch directory"],"exampleFix":"// before\nGET api/batch/file?name=../../etc/passwd\n// after\nGET api/batch/index  -> use returned filenames, e.g. name=sonar-scanner-api-2.1.0.244.jar","handlingStrategy":"try-catch","validationCode":"// Before requesting a batch file, take the name only from the index response:\nconst index = await (await fetch(`${sonarUrl}/api/batch/index`)).text();\nif (!index.includes(requestedFile)) {\n  throw new Error(`'${requestedFile}' is not in the batch index — refresh the index instead of crafting paths`);\n}","typeGuard":"function isSafeBatchFilename(name) {\n  return typeof name === 'string' && /^[A-Za-z0-9._-]+\\.jar$/.test(name) && !name.includes('..');\n}","tryCatchPattern":"try {\n  jar = batchIndex.file(filename);\n} catch (NotFoundException e) {\n  if (e.getMessage().startsWith(\"Bad filename:\")) {\n    log.warn(\"Rejected batch file request (traversal or missing): \" + e.getMessage());\n    // re-fetch api/batch/index and retry with a valid listed name\n  } else { throw e; }\n}","preventionTips":["Always fetch api/batch/index first and request only listed file names","Keep scanner and server versions aligned so requested jars exist in lib/batch","Never send relative/absolute path components in batch file requests","Avoid symlinked batch directories that break canonical containment checks"],"tags":["batch","path-traversal","not-found","file-serving"],"backgroundTag":"path-traversal-blocked","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}