{"record":{"id":"c108edf585e3281e","repo":"apache/pulsar","slug":"invalid-path-path","errorCode":null,"errorMessage":"Invalid path: ${path}","messagePattern":"Invalid path: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-package-management/filesystem-storage/src/main/java/org/apache/pulsar/packages/management/storage/filesystem/FileSystemPackagesStorage.java","lineNumber":67,"sourceCode":"\n    private final File storagePath;\n\n    FileSystemPackagesStorage(PackagesStorageConfiguration configuration) {\n        String storagePath = configuration.getProperty(STORAGE_PATH);\n        if (storagePath != null) {\n            this.storagePath = new File(storagePath);\n        } else {\n            this.storagePath = new File(DEFAULT_STORAGE_PATH);\n        }\n    }\n\n    private File getPath(String path) throws IOException {\n        // Normalize the path to remove any redundant path elements\n        File f = Paths.get(storagePath.toString(), path).normalize().toFile();\n\n        // Ensure the normalized path is still within the storagePath\n        if (!f.getAbsolutePath().startsWith(storagePath.getAbsolutePath())) {\n            throw new IOException(\"Invalid path: \" + path);\n        }\n\n        if (!f.getParentFile().exists()) {\n            if (!f.getParentFile().mkdirs()) {\n                throw new RuntimeException(\"Failed to create parent dirs for \" + path);\n            }\n        }\n        return f;\n    }\n\n    @Override\n    public void initialize() {\n        if (!storagePath.exists()) {\n            if (!storagePath.mkdirs()) {\n                throw new RuntimeException(\"Failed to create base storage directory at \" + storagePath);\n            }\n        }\n","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-package-management/filesystem-storage/src/main/java/org/apache/pulsar/packages/management/storage/filesystem/FileSystemPackagesStorage.java#L49-L85","documentation":"FileSystemPackagesStorage.getPath resolves a user-supplied path relative to the configured storage directory. After normalization it verifies the resulting absolute path still starts with the storage root; if not, the path escapes the storage directory (path traversal, e.g. via \"../\") and an IOException is thrown. This guards against reading/writing/deleting files outside the package storage.","triggerScenarios":"read/delete/list calls with paths containing \"..\" segments or absolute components that resolve outside storagePath; storagePath itself configured as a relative path or symlink so the startsWith prefix check fails even for legitimate paths.","commonSituations":"Path traversal attempt in a request; misconfigured storagePath (relative path, trailing differences, symlinked directory) making valid paths appear outside the root; storage backends migrated between machines with different layouts.","solutions":["Remove \"..\" segments and use paths relative to the storage root.","Check the storagePath configuration (packagesManagementStorageProvider config) — set it to a canonical absolute directory and ensure paths resolve under it.","Resolve symlinks: the check compares string prefixes of absolute paths, so a symlinked storagePath can false-positive; use the real canonical path in configuration.","Inspect the failing path value in the message for traversal sequences or encoding issues."],"exampleFix":"// before\nstorage.read(\"../../etc/passwd\"); // throws Invalid path\n// after\nstorage.read(\"tenant/namespace/package/metadata\"); // stays under storage root","handlingStrategy":"validation","validationCode":"static boolean isPathInsideStorage(String path, String storageRoot) throws java.io.IOException {\n    java.nio.file.Path resolved = java.nio.file.Paths.get(storageRoot, path).normalize().toAbsolutePath().toRealPath();\n    java.nio.file.Path root = java.nio.file.Paths.get(storageRoot).toAbsolutePath().toRealPath();\n    return resolved.startsWith(root);\n}","typeGuard":null,"tryCatchPattern":"try {\n    storage.readAsync(path).get();\n} catch (java.io.IOException e) {\n    if (e.getMessage().startsWith(\"Invalid path:\")) {\n        log.warn(\"Rejected path outside storage root: {}\", path);\n        throw new RestException(Response.Status.BAD_REQUEST, \"invalid path\");\n    }\n    throw e;\n}","preventionTips":["Never pass client-controlled paths containing '..' or absolute components.","Configure storagePath as a canonical absolute directory (avoid symlink ambiguity).","Normalize/validate paths server-side before storage calls.","Log rejected traversal attempts for security auditing."],"tags":["security","path-traversal","filesystem","storage"],"backgroundTag":"path-traversal-blocked","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}