From 6ff0c04b25fb476de5daac3afad6a4a9f76dd778 Mon Sep 17 00:00:00 2001 From: VeinDevTtv Date: Sat, 11 Apr 2026 08:43:27 -0700 Subject: [PATCH] fix(fxdk): prevent path traversal in remote resources Fixes #2209 Validate the FxCode remote resource path before passing it to Express sendFile. The previous route trusted the user-controlled path query forwarded through getRemoteResourcePath, allowing arbitrary absolute paths to be served by the shell backend. The route now rejects empty paths and paths that resolve outside sdkRootFXCode, preventing traversal and absolute-path reads while preserving FxCode resources under the SDK root. --- .../shell/src/backend/fxcode/fxcode-service.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ext/sdk/resources/sdk-root/shell/src/backend/fxcode/fxcode-service.ts b/ext/sdk/resources/sdk-root/shell/src/backend/fxcode/fxcode-service.ts index 36d4c393a4..d064d3e301 100644 --- a/ext/sdk/resources/sdk-root/shell/src/backend/fxcode/fxcode-service.ts +++ b/ext/sdk/resources/sdk-root/shell/src/backend/fxcode/fxcode-service.ts @@ -9,6 +9,7 @@ import { concurrently } from "utils/concurrently"; import { ShellBackend } from 'backend/shell-backend'; import { URL } from 'url'; import { NotificationService } from "backend/notification/notification-service"; +import * as path from 'path'; interface FXCode { getRootPath(): string; @@ -68,7 +69,12 @@ export class FXCodeService implements AppContribution { this.shellBackend.expressApp.get('/vscode-remote-resource', (req, res) => { if (this.fxcode) { - res.sendFile(this.fxcode.getRemoteResourcePath(getUrlQuery(req.url))); + const resourcePath = this.fxcode.getRemoteResourcePath(getUrlQuery(req.url)); + if (!resourcePath || !isPathInside(resourcePath, this.configService.sdkRootFXCode)) { + res.status(400).send('Invalid resource path'); + return; + } + res.sendFile(resourcePath); } else { res.send('FXCode not started it seems, rip'); } @@ -167,3 +173,11 @@ function getUrlQuery(url: string): Record { return acc; }, {}); } + +function isPathInside(candidate: string, root: string): boolean { + const resolvedRoot = path.resolve(root); + const resolvedCandidate = path.resolve(candidate); + const relativePath = path.relative(resolvedRoot, resolvedCandidate); + + return relativePath === '' || (!!relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)); +}