[Eclipse Theia] @theia/plugin-ext: Unauthenticated Path Traversal Allows Arbitrary File Read via /hostedPlugin Endpoint
Basic information
Project name: Eclipse Theia
Project id: ecd.theia
https://github.com/eclipse-theia/theia/security
I am reporting a path traversal vulnerability in the @theia/plugin-ext package that allows an unauthenticated network attacker to read any file on the Theia server that the process can access.
The /hostedPlugin/:pluginId/:path(*) HTTP endpoint in packages/plugin-ext/src/hosted/node/plugin-reader.ts resolves the requested file path using path.resolve(localPath, filePath) but performs no containment check to verify that the resolved path stays within the plugin directory. When an attacker sends a request with percent-encoded dot-dot-slash sequences (%2e%2e%2f), Express v4 decodes these to ../ in req.params.path and passes them to path.resolve, allowing the request to escape the plugin's root directory entirely.
Plugin IDs follow a predictable pattern -- the publisher and name fields from a plugin's package.json joined with an underscore, with non-word characters replaced by underscores -- so built-in plugins like vscode_bat, vscode_javascript, and vscode_python serve as known-good anchors that require no prior knowledge of the target system.
I validated this against the official ghcr.io/eclipse-theia/theia-ide/theia-ide:latest image (v1.72.300) with no authentication required:
curl 'http://localhost:3000/hostedPlugin/vscode_bat/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
This returned the full contents of /etc/passwd with HTTP 200.
Suggested fix: Add a containment check immediately after path.resolve():
const absolutePath = path.resolve(localPath, filePath);
if (!absolutePath.startsWith(localPath + path.sep) && absolutePath !== localPath) {
res.status(403).send('Access denied');
return;
}
Alternatively, pass the localPath as the root option to res.sendFile(), which would confine the served path to within that directory.
This issue is similar in structure to the fix applied in PR #5746 (2019), which addressed a related path traversal in the same endpoint. The current implementation appears to have reintroduced the vulnerability or the containment check was not carried forward.
CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5) CWE-22: Path Traversal