Files
diegosouzapw--omniroute/tests/unit/security/elevated-powershell-no-encoded.test.ts
2026-07-13 13:39:12 +08:00

88 lines
3.4 KiB
TypeScript

/**
* Regression: runElevatedPowerShell() must no longer use -EncodedCommand and
* must write the elevated payload to a per-call temp .ps1 file referenced via
* -File. See docs/security/SOCKET_DEV_FINDINGS.md §1.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import fs from "node:fs";
import path from "node:path";
import {
buildElevatedScriptWrapper,
_runElevatedPowerShellForTest,
} from "../../../src/mitm/systemCommands.ts";
test("buildElevatedScriptWrapper does not contain -EncodedCommand fingerprint", () => {
const wrapper = buildElevatedScriptWrapper("C:\\Temp\\omniroute-elevate-x.ps1");
assert.ok(
!wrapper.includes("-EncodedCommand"),
"wrapper must not contain -EncodedCommand (Socket.dev textbook fingerprint)"
);
assert.ok(wrapper.includes("-File"), "wrapper must reference the payload via -File");
assert.ok(wrapper.includes("Start-Process"), "wrapper must use Start-Process -Verb RunAs");
assert.ok(wrapper.includes("-Verb RunAs"), "wrapper must request elevation via -Verb RunAs");
});
test("buildElevatedScriptWrapper quotes the script path safely (no shell injection)", () => {
const wrapper = buildElevatedScriptWrapper("C:\\Temp\\path with spaces'and-quote.ps1");
// PowerShell single-quote escaping doubles the quote — our quotePowerShell
// helper does that. Confirm both the original and the escaped form are present.
assert.ok(
wrapper.includes("'C:\\Temp\\path with spaces''and-quote.ps1'"),
"single quotes in the path must be doubled per PowerShell escaping rules"
);
});
test("_runElevatedPowerShellForTest writes payload to a temp .ps1 file and unlinks after", async () => {
let capturedWrapper: string | null = null;
let capturedTempPath: string | null = null;
await _runElevatedPowerShellForTest(
"Write-Output 'omniroute regression test'",
async (wrapper, tempPath) => {
capturedWrapper = wrapper;
capturedTempPath = tempPath;
assert.ok(fs.existsSync(tempPath), "temp file must exist while the runner is active");
const content = fs.readFileSync(tempPath, "utf8");
assert.match(content, /Write-Output 'omniroute regression test'/);
return "ok";
}
);
assert.ok(capturedWrapper, "wrapper must be captured");
assert.ok(!capturedWrapper!.includes("-EncodedCommand"), "wrapper must not use -EncodedCommand");
assert.ok(capturedTempPath, "temp path must be captured");
assert.ok(
capturedTempPath!.startsWith(path.resolve(os.tmpdir())) ||
capturedTempPath!.startsWith(os.tmpdir()),
"temp .ps1 must live inside os.tmpdir()"
);
assert.ok(
!fs.existsSync(capturedTempPath!),
"temp .ps1 file must be unlinked after the runner returns"
);
});
test("_runElevatedPowerShellForTest unlinks the temp file even when the runner throws", async () => {
let capturedTempPath: string | null = null;
let threw = false;
try {
await _runElevatedPowerShellForTest("Write-Output 'denied'", async (_wrapper, tempPath) => {
capturedTempPath = tempPath;
throw new Error("simulated UAC denial");
});
} catch (err) {
threw = true;
assert.match((err as Error).message, /simulated UAC denial/);
}
assert.ok(threw, "the error must propagate to the caller");
assert.ok(capturedTempPath, "temp path must still be captured");
assert.ok(
!fs.existsSync(capturedTempPath!),
"temp .ps1 must be removed by the finally block even after a failed call"
);
});