Skip to content

Commit

Permalink
fix: validate integer values in Deno.exitCode setter (denoland#24068)
Browse files Browse the repository at this point in the history
  • Loading branch information
petamoriken authored Jun 3, 2024
1 parent 754f21f commit eda43c4
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 135 deletions.
94 changes: 0 additions & 94 deletions cli/tests/unit/os_test.ts

This file was deleted.

41 changes: 13 additions & 28 deletions ext/node/polyfills/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { report } from "ext:deno_node/internal/process/report.ts";
import { validateString } from "ext:deno_node/internal/validators.mjs";
import {
ERR_INVALID_ARG_TYPE,
ERR_OUT_OF_RANGE,
ERR_UNKNOWN_SIGNAL,
errnoException,
} from "ext:deno_node/internal/errors.ts";
Expand Down Expand Up @@ -439,38 +440,22 @@ Object.defineProperty(Process.prototype, "exitCode", {
return ProcessExitCode;
},
set(code: number | string | null | undefined) {
let parsedCode;

if (typeof code === "number") {
if (Number.isNaN(code)) {
parsedCode = 1;
denoOs.setExitCode(parsedCode);
ProcessExitCode = parsedCode;
return;
}

// This is looser than `denoOs.setExitCode` which requires exit code
// to be decimal or string of a decimal, but Node accept eg. 0x10.
parsedCode = parseInt(code);
denoOs.setExitCode(parsedCode);
ProcessExitCode = parsedCode;
return;
let parsedCode: number;
if (code == null) {
parsedCode = 0;
} else if (typeof code === "number") {
parsedCode = code;
} else if (typeof code === "string") {
parsedCode = Number(code);
} else {
throw new ERR_INVALID_ARG_TYPE("code", "number", code);
}

if (typeof code === "string") {
parsedCode = parseInt(code);
if (Number.isNaN(parsedCode)) {
throw new TypeError(
`The "code" argument must be of type number. Received type ${typeof code} (${code})`,
);
}
denoOs.setExitCode(parsedCode);
ProcessExitCode = parsedCode;
return;
if (!Number.isInteger(parsedCode)) {
throw new ERR_OUT_OF_RANGE("code", "an integer", parsedCode);
}

// TODO(bartlomieju): hope for the best here. This should be further tightened.
denoOs.setExitCode(code);
denoOs.setExitCode(parsedCode);
ProcessExitCode = code;
},
});
Expand Down
15 changes: 10 additions & 5 deletions runtime/js/30_os.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
const {
Error,
FunctionPrototypeBind,
NumberParseInt,
NumberIsInteger,
RangeError,
SymbolFor,
TypeError,
} = primordials;
Expand Down Expand Up @@ -102,13 +103,17 @@ function getExitCode() {
}

function setExitCode(value) {
const code = NumberParseInt(value, 10);
if (typeof code !== "number") {
if (typeof value !== "number") {
throw new TypeError(
`Exit code must be a number, got: ${code} (${typeof code}).`,
`Exit code must be a number, got: ${value} (${typeof value})`,
);
}
op_set_exit_code(code);
if (!NumberIsInteger(value)) {
throw new RangeError(
`Exit code must be an integer, got: ${value}`,
);
}
op_set_exit_code(value);
}

function setEnv(key, value) {
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/os_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,70 @@ Deno.test(function memoryUsage() {
assert(typeof mem.external === "number");
assert(mem.rss >= mem.heapTotal);
});

Deno.test("Deno.exitCode getter and setter", () => {
// Initial value is 0
assertEquals(Deno.exitCode, 0);

try {
// Set a new value
Deno.exitCode = 5;
assertEquals(Deno.exitCode, 5);
} finally {
// Reset to initial value
Deno.exitCode = 0;
}

assertEquals(Deno.exitCode, 0);
});

Deno.test("Setting Deno.exitCode to non-number throws TypeError", () => {
// Throws on non-number values
assertThrows(
() => {
// @ts-expect-error Testing for runtime error
Deno.exitCode = "123";
},
TypeError,
"Exit code must be a number, got: 123 (string)",
);

// Throws on bigint values
assertThrows(
() => {
// @ts-expect-error Testing for runtime error
Deno.exitCode = 1n;
},
TypeError,
"Exit code must be a number, got: 1 (bigint)",
);
});

Deno.test("Setting Deno.exitCode to non-integer throws RangeError", () => {
// Throws on non-integer values
assertThrows(
() => {
Deno.exitCode = 3.14;
},
RangeError,
"Exit code must be an integer, got: 3.14",
);
});

Deno.test("Setting Deno.exitCode does not cause an immediate exit", () => {
let exited = false;

const originalExit = Deno.exit;
// @ts-expect-error; read-only
Deno.exit = () => {
exited = true;
};

try {
Deno.exitCode = 1;
assertEquals(exited, false);
} finally {
Deno.exit = originalExit;
Deno.exitCode = 0;
}
});
8 changes: 0 additions & 8 deletions tests/unit_node/process_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -813,10 +813,6 @@ Deno.test("process.exitCode in should change exit code", async () => {
"import process from 'node:process'; process.exitCode = 127;",
127,
);
await exitCodeTest(
"import process from 'node:process'; process.exitCode = 2.5;",
2,
);
await exitCodeTest(
"import process from 'node:process'; process.exitCode = '10';",
10,
Expand All @@ -825,10 +821,6 @@ Deno.test("process.exitCode in should change exit code", async () => {
"import process from 'node:process'; process.exitCode = '0x10';",
16,
);
await exitCodeTest(
"import process from 'node:process'; process.exitCode = NaN;",
1,
);
});

Deno.test("Deno.exit should override process exit", async () => {
Expand Down

0 comments on commit eda43c4

Please sign in to comment.