Skip to content

Commit

Permalink
Merge pull request #416 from AikidoSec/patch-fs-promises
Browse files Browse the repository at this point in the history
Wrap require("fs").promises if "fs" is required
  • Loading branch information
hansott authored Oct 16, 2024
2 parents 02ac426 + cdb5751 commit 4d5404f
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
46 changes: 46 additions & 0 deletions library/sinks/FileSystem.promises.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as t from "tap";
import { Context, runWithContext } from "../agent/Context";
import { FileSystem } from "./FileSystem";
import { createTestAgent } from "../helpers/createTestAgent";

const unsafeContext: Context = {
remoteAddress: "::1",
method: "POST",
url: "http://localhost:4000",
query: {},
headers: {},
body: {
file: {
matches: "../test.txt",
},
},
cookies: {},
routeParams: {},
source: "express",
route: "/posts/:id",
};

t.test("when require('fs').promises is used", async (t) => {
const agent = createTestAgent();

agent.start([new FileSystem()]);

const { promises } = require("fs");

await runWithContext(unsafeContext, async () => {
const error = await t.rejects(() =>
promises.writeFile(
"../../test.txt",
"some other file content to test with",
{ encoding: "utf-8" }
)
);
t.ok(error instanceof Error);
if (error instanceof Error) {
t.match(
error.message,
"Zen has blocked a path traversal attack: fs.writeFile(...) originating from body.file.matches"
);
}
});
});
44 changes: 31 additions & 13 deletions library/sinks/FileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getContext } from "../agent/Context";
import { Hooks } from "../agent/hooks/Hooks";
import { InterceptorResult } from "../agent/hooks/InterceptorResult";
import { wrapExport } from "../agent/hooks/wrapExport";
import { WrapPackageInfo } from "../agent/hooks/WrapPackageInfo";
import { Wrapper } from "../agent/Wrapper";
import { getSemverNodeVersion } from "../helpers/getNodeVersion";
import { isVersionGreaterOrEqual } from "../helpers/isVersionGreaterOrEqual";
Expand All @@ -14,6 +15,8 @@ type FileSystemFunction = {
};

export class FileSystem implements Wrapper {
private patchedPromises = false;

private inspectPath(
args: unknown[],
name: string,
Expand Down Expand Up @@ -91,12 +94,33 @@ export class FileSystem implements Wrapper {
return functions;
}

wrapPromises(exports: unknown, pkgInfo: WrapPackageInfo) {
if (this.patchedPromises) {
// `require("fs").promises.readFile` is the same as `require("fs/promises").readFile`
// We only need to wrap the promise version once
return;
}

const functions = this.getFunctions();
Object.keys(functions).forEach((name) => {
const { pathsArgs, promise } = functions[name];

if (promise) {
wrapExport(exports, name, pkgInfo, {
inspectArgs: (args) => this.inspectPath(args, name, pathsArgs),
});
}
});

this.patchedPromises = true;
}

wrap(hooks: Hooks) {
hooks.addBuiltinModule("fs").onRequire((exports, pkgInfo) => {
const functions = this.getFunctions();

Object.keys(functions).forEach((name) => {
const { pathsArgs, sync, promise } = functions[name];
const { pathsArgs, sync } = functions[name];

wrapExport(exports, name, pkgInfo, {
inspectArgs: (args) => {
Expand All @@ -119,24 +143,18 @@ export class FileSystem implements Wrapper {
return this.inspectPath(args, "realpath.native", 1);
},
});

wrapExport(exports.realpathSync, "native", pkgInfo, {
inspectArgs: (args) => {
return this.inspectPath(args, "realpathSync.native", 1);
},
});
});

hooks.addBuiltinModule("fs/promises").onRequire((exports, pkgInfo) => {
const functions = this.getFunctions();
Object.keys(functions).forEach((name) => {
const { pathsArgs, sync, promise } = functions[name];

if (promise) {
wrapExport(exports, name, pkgInfo, {
inspectArgs: (args) => this.inspectPath(args, name, pathsArgs),
});
}
});
this.wrapPromises(exports.promises, pkgInfo);
});

hooks
.addBuiltinModule("fs/promises")
.onRequire((exports, pkgInfo) => this.wrapPromises(exports, pkgInfo));
}
}

0 comments on commit 4d5404f

Please sign in to comment.