From a1dab6c3c054ebffdb1e09a4e6beef19ca08a596 Mon Sep 17 00:00:00 2001 From: adilburaksen Date: Mon, 1 Jun 2026 19:31:42 +0300 Subject: [PATCH] Validate weight file paths stay within the model directory In tfjs-node, NodeFileSystem.loadWeights joins each weightsManifest path from the model's weights manifest onto the model directory and reads it, without checking that the result stays inside that directory. Because the manifest is untrusted input, a path containing '..' or an absolute path could read a file outside the model directory. Resolve each weight file path and require it to remain within the model directory before reading it. --- tfjs-node/src/io/file_system.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tfjs-node/src/io/file_system.ts b/tfjs-node/src/io/file_system.ts index 74b4cc582d..83ff241270 100644 --- a/tfjs-node/src/io/file_system.ts +++ b/tfjs-node/src/io/file_system.ts @@ -17,7 +17,7 @@ import * as tf from '@tensorflow/tfjs'; import * as fs from 'fs'; -import {dirname, join, resolve} from 'path'; +import {dirname, join, resolve, sep} from 'path'; import {promisify} from 'util'; import {toArrayBuffer} from './io_utils'; @@ -190,6 +190,17 @@ export class NodeFileSystem implements tf.io.IOHandler { for (const group of weightsManifest) { for (const path of group.paths) { const weightFilePath = join(dirName, path); + // The weight paths come from the (untrusted) model manifest. Ensure + // each resolves inside the model directory so that a path containing + // '..' or an absolute path cannot read files outside of it. + const resolvedDir = resolve(dirName); + const resolvedFile = resolve(weightFilePath); + if (resolvedFile !== resolvedDir && + !resolvedFile.startsWith(resolvedDir + sep)) { + throw new Error( + `Invalid weight file path '${path}': it must stay within the ` + + `model directory.`); + } const buffer = await readFile(weightFilePath) .catch(doesNotExistHandler('Weight file')); buffers.push(buffer);