1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00
denoland-deno/tests/unit/os_test.ts
David Sherret c6793f52b9
fix(permissions): disallow any LD_ or DYLD_ prefixed env var without full --allow-run permissions (#25271)
Follow up to https://github.com/denoland/deno/pull/25221

I looked into what the list was and it was quite extensive, so I think
as suggested in
https://github.com/denoland/deno/issues/11964#issuecomment-2314585135 we
should disallow this for any `LD_` prefixed env var.
2024-08-28 21:11:37 -04:00

392 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
assertNotEquals,
assertThrows,
} from "./test_util.ts";
Deno.test({ permissions: { env: true } }, function envSuccess() {
Deno.env.set("TEST_VAR", "A");
const env = Deno.env.toObject();
Deno.env.set("TEST_VAR", "B");
assertEquals(env["TEST_VAR"], "A");
assertNotEquals(Deno.env.get("TEST_VAR"), env["TEST_VAR"]);
});
Deno.test({ permissions: { env: true } }, function envNotFound() {
const r = Deno.env.get("env_var_does_not_exist!");
assertEquals(r, undefined);
});
Deno.test({ permissions: { env: true } }, function deleteEnv() {
Deno.env.set("TEST_VAR", "A");
assertEquals(Deno.env.get("TEST_VAR"), "A");
assertEquals(Deno.env.delete("TEST_VAR"), undefined);
assertEquals(Deno.env.get("TEST_VAR"), undefined);
});
Deno.test({ permissions: { env: true } }, function hasEnv() {
Deno.env.set("TEST_VAR", "A");
assert(Deno.env.has("TEST_VAR"));
Deno.env.delete("TEST_VAR");
assert(!Deno.env.has("TEST_VAR"));
});
Deno.test({ permissions: { env: true } }, function avoidEmptyNamedEnv() {
assertThrows(() => Deno.env.set("", "v"), TypeError);
assertThrows(() => Deno.env.set("a=a", "v"), TypeError);
assertThrows(() => Deno.env.set("a\0a", "v"), TypeError);
assertThrows(() => Deno.env.set("TEST_VAR", "v\0v"), TypeError);
assertThrows(() => Deno.env.get(""), TypeError);
assertThrows(() => Deno.env.get("a=a"), TypeError);
assertThrows(() => Deno.env.get("a\0a"), TypeError);
assertThrows(() => Deno.env.delete(""), TypeError);
assertThrows(() => Deno.env.delete("a=a"), TypeError);
assertThrows(() => Deno.env.delete("a\0a"), TypeError);
});
Deno.test({ permissions: { env: false } }, function envPermissionDenied1() {
assertThrows(() => {
Deno.env.toObject();
}, Deno.errors.PermissionDenied);
});
Deno.test({ permissions: { env: false } }, function envPermissionDenied2() {
assertThrows(() => {
Deno.env.get("PATH");
}, Deno.errors.PermissionDenied);
});
// This test verifies that on Windows, environment variables are
// case-insensitive. Case normalization needs be done using the collation
// that Windows uses, rather than naively using String.toLowerCase().
Deno.test(
{
ignore: Deno.build.os !== "windows",
permissions: { read: true, env: true, run: true },
},
async function envCaseInsensitive() {
// Utility function that runs a Deno subprocess with the environment
// specified in `inputEnv`. The subprocess reads the environment variables
// which are in the keys of `expectedEnv` and writes them to stdout as JSON.
// It is then verified that these match with the values of `expectedEnv`.
const checkChildEnv = async (
inputEnv: Record<string, string>,
expectedEnv: Record<string, string>,
) => {
const src = `
console.log(
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k))
)`;
const { success, stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", src],
env: { ...inputEnv, NO_COLOR: "1" },
}).output();
assertEquals(success, true);
const expectedValues = Object.values(expectedEnv);
const actualValues = JSON.parse(new TextDecoder().decode(stdout));
assertEquals(actualValues, expectedValues);
};
assertEquals(Deno.env.get("path"), Deno.env.get("PATH"));
assertEquals(Deno.env.get("Path"), Deno.env.get("PATH"));
// Check 'foo', 'Foo' and 'Foo' are case folded.
await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" });
// Check that 'µ' and 'Μ' are not case folded.
const lc1 = "µ";
const uc1 = lc1.toUpperCase();
assertNotEquals(lc1, uc1);
await checkChildEnv(
{ [lc1]: "mu", [uc1]: "MU" },
{ [lc1]: "mu", [uc1]: "MU" },
);
// Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved.
const c2 = "Dž";
const lc2 = c2.toLowerCase();
const uc2 = c2.toUpperCase();
assertNotEquals(c2, lc2);
assertNotEquals(c2, uc2);
await checkChildEnv(
{ [c2]: "Dz", [lc2]: "dz" },
{ [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" },
);
await checkChildEnv(
{ [c2]: "Dz", [uc2]: "DZ" },
{ [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" },
);
},
);
Deno.test({ permissions: { env: true } }, function envInvalidChars() {
assertThrows(() => Deno.env.get(""), TypeError, "Key is an empty string");
assertThrows(
() => Deno.env.get("\0"),
TypeError,
'Key contains invalid characters: "\\0"',
);
assertThrows(
() => Deno.env.get("="),
TypeError,
'Key contains invalid characters: "="',
);
assertThrows(
() => Deno.env.set("", "foo"),
TypeError,
"Key is an empty string",
);
assertThrows(
() => Deno.env.set("\0", "foo"),
TypeError,
'Key contains invalid characters: "\\0"',
);
assertThrows(
() => Deno.env.set("=", "foo"),
TypeError,
'Key contains invalid characters: "="',
);
assertThrows(
() => Deno.env.set("foo", "\0"),
TypeError,
'Value contains invalid characters: "\\0"',
);
});
Deno.test(function osPid() {
assertEquals(typeof Deno.pid, "number");
assert(Deno.pid > 0);
});
Deno.test(function osPpid() {
assertEquals(typeof Deno.ppid, "number");
assert(Deno.ppid > 0);
});
Deno.test(
{ permissions: { run: true, read: true } },
async function osPpidIsEqualToPidOfParentProcess() {
const decoder = new TextDecoder();
const { stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", "-p", "--unstable", "Deno.ppid"],
env: { NO_COLOR: "true" },
}).output();
const expected = Deno.pid;
const actual = parseInt(decoder.decode(stdout));
assertEquals(actual, expected);
},
);
Deno.test({ permissions: { read: true } }, function execPath() {
assertNotEquals(Deno.execPath(), "");
});
Deno.test({ permissions: { read: false } }, function execPathPerm() {
assertThrows(
() => {
Deno.execPath();
},
Deno.errors.PermissionDenied,
"Requires read access to <exec_path>, run again with the --allow-read flag",
);
});
Deno.test(
{
ignore: Deno.build.os !== "linux",
permissions: { read: true, run: false },
},
function procRequiresAllowAll() {
assertThrows(
() => {
Deno.readTextFileSync("/proc/net/dev");
},
Deno.errors.PermissionDenied,
`Requires all access to "/proc/net/dev", run again with the --allow-all flag`,
);
},
);
Deno.test(
{ permissions: { sys: ["loadavg"] } },
function loadavgSuccess() {
const load = Deno.loadavg();
assertEquals(load.length, 3);
},
);
Deno.test({ permissions: { sys: false } }, function loadavgPerm() {
assertThrows(() => {
Deno.loadavg();
}, Deno.errors.PermissionDenied);
});
Deno.test(
{ permissions: { sys: ["hostname"] } },
function hostnameDir() {
assertNotEquals(Deno.hostname(), "");
},
);
Deno.test(
{ permissions: { run: [Deno.execPath()], read: true } },
// See https://github.com/denoland/deno/issues/16527
async function hostnameWithoutOtherNetworkUsages() {
const { stdout } = await new Deno.Command(Deno.execPath(), {
args: ["eval", "-p", "Deno.hostname()"],
env: {
LD_PRELOAD: "",
LD_LIBRARY_PATH: "",
DYLD_FALLBACK_LIBRARY_PATH: "",
},
}).output();
const hostname = new TextDecoder().decode(stdout).trim();
assert(hostname.length > 0);
},
);
Deno.test({ permissions: { sys: false } }, function hostnamePerm() {
assertThrows(() => {
Deno.hostname();
}, Deno.errors.PermissionDenied);
});
Deno.test(
{ permissions: { sys: ["osRelease"] } },
function releaseDir() {
assertNotEquals(Deno.osRelease(), "");
},
);
Deno.test({ permissions: { sys: false } }, function releasePerm() {
assertThrows(() => {
Deno.osRelease();
}, Deno.errors.PermissionDenied);
});
Deno.test({ permissions: { sys: ["osUptime"] } }, function osUptime() {
const uptime = Deno.osUptime();
assert(typeof uptime === "number");
assert(uptime > 0);
});
Deno.test({ permissions: { sys: false } }, function osUptimePerm() {
assertThrows(() => {
Deno.osUptime();
}, Deno.errors.PermissionDenied);
});
Deno.test(
{ permissions: { sys: ["systemMemoryInfo"] } },
function systemMemoryInfo() {
const info = Deno.systemMemoryInfo();
assert(info.total >= 0);
assert(info.free >= 0);
assert(info.available >= 0);
assert(info.buffers >= 0);
assert(info.cached >= 0);
assert(info.swapTotal >= 0);
assert(info.swapFree >= 0);
},
);
Deno.test({ permissions: { sys: ["uid"] } }, function getUid() {
if (Deno.build.os === "windows") {
assertEquals(Deno.uid(), null);
} else {
const uid = Deno.uid();
assert(typeof uid === "number");
assert(uid > 0);
}
});
Deno.test({ permissions: { sys: ["gid"] } }, function getGid() {
if (Deno.build.os === "windows") {
assertEquals(Deno.gid(), null);
} else {
const gid = Deno.gid();
assert(typeof gid === "number");
assert(gid > 0);
}
});
Deno.test(function memoryUsage() {
const mem = Deno.memoryUsage();
assert(typeof mem.rss === "number");
assert(typeof mem.heapTotal === "number");
assert(typeof mem.heapUsed === "number");
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;
}
});