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
8 changes: 7 additions & 1 deletion test_node_env/result_writer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Script } from "vm";
import "sicp";
import { readFileSync, createWriteStream } from "fs";
import { resolve, dirname } from "path";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Include sep in the imports from the path module. This is necessary to perform a robust directory prefix check that avoids partial name matches (e.g., preventing /app/test from matching /app/test_malicious/).

Suggested change
import { resolve, dirname } from "path";
import { resolve, dirname, sep } from "path";

("use strict");

let writer = createWriteStream("test_node_env/result.txt");
const args = process.argv.slice(2);
const programPath = args[0];
const allowedDir = resolve(dirname(process.argv[1]), "test_node_env");
const resolvedPath = resolve(programPath);
if (!resolvedPath.startsWith(allowedDir)) {
throw new Error("Invalid program path: must be within test_node_env directory");
}
Comment on lines +10 to +14
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The path validation logic contains a logic error and a security weakness:

  1. Incorrect Directory: The expression resolve(dirname(process.argv[1]), "test_node_env") resolves to a test_node_env subdirectory within the test_node_env folder (e.g., .../test_node_env/test_node_env). However, the test files are actually located in the js_programs/ directory at the project root (as seen in scripts/nodetest.sh). This check will currently block all valid test executions.
  2. Path Prefix Vulnerability: Using startsWith(allowedDir) without a trailing path separator allows for potential bypasses where a directory name is a prefix of another (e.g., /test matching /test_vulnerable/).

Consider resolving the path to the js_programs directory and appending the platform-specific separator for a secure check.

Suggested change
const allowedDir = resolve(dirname(process.argv[1]), "test_node_env");
const resolvedPath = resolve(programPath);
if (!resolvedPath.startsWith(allowedDir)) {
throw new Error("Invalid program path: must be within test_node_env directory");
}
const allowedDir = resolve(dirname(process.argv[1]), "..", "js_programs") + sep;
const resolvedPath = resolve(programPath);
if (!resolvedPath.startsWith(allowedDir)) {
throw new Error("Invalid program path: must be within js_programs directory");
}


writer.once("open", function (fd) {
let s = new Script(readFileSync(programPath));
let s = new Script(readFileSync(resolvedPath));
let r = s.runInThisContext();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The primary objective of this PR, as stated in the title and description, is to prevent arbitrary code execution by avoiding runInThisContext(). However, the current implementation still uses runInThisContext(), which executes the script with full access to the current Node.js process context. To fulfill the security requirement, you should use runInNewContext() to execute the script in a sandbox, consistent with the implementation in test_node_env/expected_writer.js.

Suggested change
let r = s.runInThisContext();
let r = s.runInNewContext();

if (typeof r !== "undefined") {
writer.write(JSON.stringify(r));
Expand Down