fix: keep Alta bearer out of proxy command lines
This commit is contained in:
+19
-20
@@ -11,7 +11,7 @@ const APPROVED_DIRECTORY = 'C:\\Program Files\\Alta Proxy Tool';
|
||||
const APPROVED_HELPER = 'C:\\Program Files\\Alta Proxy Tool\\aware-cam-proxy.exe';
|
||||
const VALID_HOST = 'tenant.avasecurity.com';
|
||||
const VALID_DEVICE_ID = '123e4567-e89b-42d3-a456-426614174000';
|
||||
const SYNTHETIC_COOKIE = 'va=HERMES_SENTINEL_SECRET';
|
||||
const VALID_USERNAME = 'proxy.operator+apt@example.com';
|
||||
|
||||
function loadModule() {
|
||||
return require(MODULE_PATH);
|
||||
@@ -56,7 +56,7 @@ function validRequest(overrides = {}) {
|
||||
return {
|
||||
deploymentHost: VALID_HOST,
|
||||
deviceId: VALID_DEVICE_ID,
|
||||
cookie: SYNTHETIC_COOKIE,
|
||||
username: VALID_USERNAME,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
@@ -72,10 +72,10 @@ test('launches the approved helper directly with exact argv and shell disabled',
|
||||
|
||||
assert.deepEqual(calls, [[
|
||||
APPROVED_HELPER,
|
||||
['-a', VALID_HOST, '-d', VALID_DEVICE_ID, '-k', SYNTHETIC_COOKIE],
|
||||
['-a', VALID_HOST, '-u', VALID_USERNAME, '-d', VALID_DEVICE_ID],
|
||||
{
|
||||
shell: false,
|
||||
detached: false,
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
windowsHide: false
|
||||
}
|
||||
@@ -89,13 +89,13 @@ test('launches the approved helper directly with exact argv and shell disabled',
|
||||
});
|
||||
});
|
||||
|
||||
test('passes cookie metacharacters literally in argv without invoking a shell', () => {
|
||||
const cookie = 'va=abc&whoami|calc.exe;<>()^%!"`$';
|
||||
test('passes username punctuation literally in argv without invoking a shell', () => {
|
||||
const username = 'proxy+apt&literal|name@example.com';
|
||||
const { manager, calls } = createHarness();
|
||||
|
||||
manager.launchProxy(validRequest({ cookie }));
|
||||
manager.launchProxy(validRequest({ username }));
|
||||
|
||||
assert.equal(calls[0][1][5], cookie);
|
||||
assert.equal(calls[0][1][3], username);
|
||||
assert.equal(calls[0][2].shell, false);
|
||||
});
|
||||
|
||||
@@ -104,8 +104,8 @@ test('rejects the CRLF calc.exe reproducer in every structured input', () => {
|
||||
const attacks = [
|
||||
{ deploymentHost: `${VALID_HOST}\r\ncalc.exe` },
|
||||
{ deviceId: `${VALID_DEVICE_ID}\r\ncalc.exe` },
|
||||
{ cookie: `${SYNTHETIC_COOKIE}\r\ncalc.exe` },
|
||||
{ cookie: `${SYNTHETIC_COOKIE}\0calc.exe` }
|
||||
{ username: `${VALID_USERNAME}\r\ncalc.exe` },
|
||||
{ username: `${VALID_USERNAME}\0calc.exe` }
|
||||
];
|
||||
|
||||
for (const attack of attacks) {
|
||||
@@ -178,10 +178,10 @@ test('normalizes a valid Alta hostname to lowercase', () => {
|
||||
assert.equal(calls[0][1][1], 'tenant.avigilon.com');
|
||||
});
|
||||
|
||||
test('rejects empty, oversized, and control-character cookies', () => {
|
||||
for (const cookie of ['', 'x'.repeat(4097), 'va=abc\nxyz', 'va=abc\rxyz', 'va=abc\0xyz']) {
|
||||
test('rejects empty, oversized, non-string, and control-character usernames', () => {
|
||||
for (const username of ['', 'x'.repeat(255), 42, 'user\nname', 'user\rname', 'user\0name', 'user\u007fname']) {
|
||||
const { manager } = createHarness();
|
||||
assert.throws(() => manager.launchProxy(validRequest({ cookie })), /cookie/i);
|
||||
assert.throws(() => manager.launchProxy(validRequest({ username })), /username/i);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -205,27 +205,25 @@ test('requires an absolute approved application directory and Windows platform',
|
||||
);
|
||||
});
|
||||
|
||||
test('redacts the cookie if spawn throws an error containing it', () => {
|
||||
test('reports a bounded spawn failure without credential redaction machinery', () => {
|
||||
const { createProxyManager } = loadModule();
|
||||
const manager = createProxyManager({
|
||||
appDirectory: APPROVED_DIRECTORY,
|
||||
fs: { existsSync: () => true },
|
||||
platform: 'win32',
|
||||
spawn: () => { throw new Error(`spawn failed for ${SYNTHETIC_COOKIE}`); }
|
||||
spawn: () => { throw new Error('spawn failed'); }
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => manager.launchProxy(validRequest()),
|
||||
(error) => {
|
||||
assert.match(error.message, /spawn failed/);
|
||||
assert.match(error.message, /\[REDACTED\]/);
|
||||
assert.doesNotMatch(error.message, /HERMES_SENTINEL_SECRET/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('tracks only safe process metadata and never exposes or persists cookies', () => {
|
||||
test('tracks only safe process metadata and never exposes usernames', () => {
|
||||
const { manager } = createHarness();
|
||||
manager.launchProxy(validRequest());
|
||||
|
||||
@@ -236,7 +234,7 @@ test('tracks only safe process metadata and never exposes or persists cookies',
|
||||
startedAt: 1_777_777_777_777,
|
||||
status: 'running'
|
||||
}]);
|
||||
assert.doesNotMatch(JSON.stringify(tracked), /HERMES_SENTINEL_SECRET/);
|
||||
assert.doesNotMatch(JSON.stringify(tracked), /proxy\.operator/);
|
||||
});
|
||||
|
||||
test('stopping one tracked process leaves the other tracked process alive', () => {
|
||||
@@ -309,8 +307,9 @@ test('exit events remove only the matching owned child', () => {
|
||||
assert.deepEqual(manager.listTrackedProxies().map((item) => item.processId), [4101]);
|
||||
});
|
||||
|
||||
test('source contains no shell launchers, broad process killers, or persistence APIs', () => {
|
||||
test('source contains no bearer argv, shell launchers, broad process killers, or persistence APIs', () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '..', 'src', 'proxy-launch.js'), 'utf8');
|
||||
assert.doesNotMatch(source, /cookie|bearer|token|-k/i);
|
||||
assert.doesNotMatch(source, /\b(?:cmd(?:\.exe)?|powershell|taskkill|pkill|wmic)\b/i);
|
||||
assert.doesNotMatch(source, /\.(?:bat|command)\b/i);
|
||||
assert.doesNotMatch(source, /(?:writeFile|appendFile|mkdtemp|tmpdir)/);
|
||||
|
||||
Reference in New Issue
Block a user