Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions tfjs-node/src/io/file_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@

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';

const stat = promisify(fs.stat);
const writeFile = promisify(fs.writeFile);
const readFile = promisify(fs.readFile);
const mkdir = promisify(fs.mkdir);
const realpath = promisify(fs.realpath);

function doesNotExistHandler(name: string): (e: NodeJS.ErrnoException) =>
never {
Expand Down Expand Up @@ -185,11 +186,26 @@ export class NodeFileSystem implements tf.io.IOHandler {
weightsManifest: tf.io.WeightsManifestConfig,
path: string): Promise<[tf.io.WeightsManifestEntry[], ArrayBuffer]> {
const dirName = dirname(path);
// Resolve the model directory to its canonical path so that symlinks
// inside the directory cannot be used to escape the boundary check.
const resolvedBase =
await realpath(resolve(dirName)).catch(() => resolve(dirName));
const buffers: Buffer[] = [];
const weightSpecs: tf.io.WeightsManifestEntry[] = [];
for (const group of weightsManifest) {
for (const path of group.paths) {
const weightFilePath = join(dirName, path);
for (const weightPath of group.paths) {
const weightFilePath = join(dirName, weightPath);
// Verify that the resolved weight path stays within the model
// directory. This prevents path-traversal attacks where a malicious
// model.json uses "../" sequences to read arbitrary files.
const resolvedWeight = await realpath(weightFilePath)
.catch(() => resolve(dirName, weightPath));
if (!resolvedWeight.startsWith(resolvedBase + sep)) {
throw new Error(
`Weight file path "${weightPath}" is outside the model ` +
`directory. Loading weights from paths outside the model ` +
`directory is not supported.`);
}
const buffer = await readFile(weightFilePath)
.catch(doesNotExistHandler('Weight file'));
buffers.push(buffer);
Expand Down