Sorgente pubblicata
Scarica sorgente ZIP
CH-J Tools
Sfoglia directory e file per una specifica release di applicazioni.
CH-J / Server Manager
406 file
core-20261024T135205Z-7c487d719de2
Aggiunto: · Costruito:
1
"use strict";3
const test = require("node:test");4
const assert = require("node:assert/strict");5
const windowsPolicy = require("../src/main/users/windowsSshPolicy");6
const { normalizePolicy, parseEffective, evaluateAccess } = require("../src/main/users/sshPolicy");7
const { BOOTSTRAP, bootstrapInput, encodedCommand, parseWindowsOutput } = require("../src/main/sessions/windowsPrivilegedExec");8
const { detectPlatform, shellName } = require("../src/main/sessions/platformDetect");9
const { WindowsUserAdminService, privilegeFromToken, validateWindowsName } = require("../src/main/users/windowsUserAdminService");10
const { UserAdminService } = require("../src/main/users/userAdminService");12
// Default sshd_config of Windows OpenSSH 9.5 (comments shortened), CRLF line endings as shipped.13
const WINDOWS_CONFIG = [14
"# This is the sshd server system-wide configuration file.",15
"#PasswordAuthentication yes",16
"AuthorizedKeysFile\t.ssh/authorized_keys",17
"Subsystem\tsftp\tsftp-server.exe",18
"",19
"Match Group administrators",20
" AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys",21
""22
].join("\r\n");23
const ok = (value) => `#< CLIXML\r\nCHJ_RESULT:${Buffer.from(JSON.stringify(value)).toString("base64")}\r\n`;24
const err = (code, message) => `CHJ_ERR:${code}:${Buffer.from(message).toString("base64")}\r\n`;26
test("Windows sshd_config sections: DenyUsers before the first Match, Match User at the end, no KbdInteractive", () => {27
const policies = new Map([28
["alice", normalizePolicy({ password: "no", ipv6: "deny" })],29
["bob", normalizePolicy({ access: "deny" })],30
["carol", normalizePolicy({})]31
]);32
const text = windowsPolicy.renderConfig(WINDOWS_CONFIG, policies);33
const lines = text.split("\r\n");34
const globalIndex = lines.indexOf(windowsPolicy.GLOBAL_BEGIN);35
const adminMatch = lines.indexOf("Match Group administrators");36
assert.ok(globalIndex >= 0 && globalIndex < adminMatch, "global directives never follow an existing Match block");37
assert.ok(lines.indexOf("DenyUsers alice@*:*") > globalIndex && lines.indexOf("DenyUsers alice@*:*") < adminMatch, "IPv6 rule without CIDR slash");38
assert.doesNotMatch(text, /DenyUsers [^\r\n]*\//, "no slash: Windows OpenSSH rewrites it to a backslash");39
assert.ok(lines.indexOf("DenyUsers bob") < adminMatch);40
assert.ok(lines.indexOf("Match User alice") > adminMatch, "per-user Match blocks keep the lower priority");41
assert.doesNotMatch(text, /KbdInteractiveAuthentication/);42
assert.doesNotMatch(text, /carol/);43
assert.doesNotMatch(text, /[^\r]\n/, "CRLF line endings are preserved");44
const parsed = windowsPolicy.parseManagedPolicies(text);45
assert.equal(parsed.consistent, true);46
assert.deepEqual([...parsed.policies.keys()].sort(), ["alice", "bob"]);47
assert.equal(windowsPolicy.renderConfig(text, new Map()), WINDOWS_CONFIG, "removing all policies restores the original bytes");48
assert.equal(windowsPolicy.renderConfig(WINDOWS_CONFIG, new Map()), WINDOWS_CONFIG);49
});51
test("Windows sshd_config edits are refused for manual edits, damaged markers, Include and misplaced sections", () => {52
const text = windowsPolicy.renderConfig(WINDOWS_CONFIG, new Map([["alice", normalizePolicy({ ipv4: "deny" })]]));53
const edited = text.replace("DenyUsers alice@*.*.*.*", "DenyUsers alice@*.*.*.* mallory");54
assert.equal(windowsPolicy.parseManagedPolicies(edited).consistent, false);55
assert.throws(() => windowsPolicy.renderConfig(edited, new Map()), { code: "USERS_SSH_MANAGED_FILE_MODIFIED" });56
assert.throws(() => windowsPolicy.renderConfig(text.replace(windowsPolicy.GLOBAL_BEGIN, "# x"), new Map()), { code: "USERS_SSH_LAYOUT_UNSUPPORTED" });57
assert.throws(() => windowsPolicy.renderConfig(`Include C:/ProgramData/ssh/extra.conf\r\n${WINDOWS_CONFIG}`, new Map([["alice", normalizePolicy({ access: "deny" })]])), { code: "USERS_SSH_LAYOUT_UNSUPPORTED" });58
const misplaced = `${WINDOWS_CONFIG}${windowsPolicy.GLOBAL_BEGIN}\r\n# END CH-J chj.users global\r\n`;59
assert.equal(windowsPolicy.analyzeConfig(misplaced).supported, false);60
assert.equal(windowsPolicy.renderConfig("PasswordAuthentication yes\n", new Map([["alice", normalizePolicy({ password: "no" })]])),61
`PasswordAuthentication yes\n${windowsPolicy.GLOBAL_BEGIN}\n# chj-policy alice access=allow password=no pubkey=inherit ipv4=allow ipv6=allow\n# END CH-J chj.users global\n${windowsPolicy.MATCH_BEGIN}\nMatch User alice\n\tPasswordAuthentication no\n# END CH-J chj.users match\n`);62
assert.equal(windowsPolicy.sshName("Codex"), "codex");63
assert.equal(windowsPolicy.sshName("Jan Novák"), null, "names with spaces or non-ASCII are not written to sshd_config");64
});66
test("Windows effective evaluation ignores keyboard-interactive, which Windows OpenSSH does not support", () => {67
const dump = parseEffective("passwordauthentication no\nkbdinteractiveauthentication yes\npubkeyauthentication yes\ndenyusers alice@*.*.*.*\ndenyusers bob@*:*");68
assert.equal(evaluateAccess(dump, { username: "bob", address: "192.0.2.1", method: "password", keyboardInteractive: false }).methodAllowed, false);69
assert.equal(evaluateAccess(dump, { username: "bob", address: "192.0.2.1", method: "password" }).methodAllowed, true, "Linux semantics unchanged");70
assert.equal(evaluateAccess(dump, { username: "alice", address: "192.0.2.1", keyboardInteractive: false }).allowed, false);71
assert.equal(evaluateAccess(dump, { username: "alice", address: "2001:db8::1", keyboardInteractive: false }).allowed, true);72
assert.equal(evaluateAccess(dump, { username: "bob", address: "2001:db8::1", keyboardInteractive: false }).allowed, false);73
assert.equal(evaluateAccess(dump, { username: "bob", address: "192.0.2.1", keyboardInteractive: false }).reasons.length, 0);74
assert.equal(evaluateAccess(dump, { username: "alice", address: "192.0.2.1" }).reasons[0].managed, true);75
});77
test("PowerShell transport: short encoded bootstrap, structured stdin, no user data in the command line", () => {78
const command = encodedCommand(BOOTSTRAP);79
assert.ok(command.length < 8191, "fits the cmd.exe command-line limit");80
assert.match(command, /^powershell\.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand [A-Za-z0-9+/=]+$/);81
assert.match(Buffer.from(command.split(" ").pop(), "base64").toString("utf16le"), /\[Console\]::In/);82
const input = bootstrapInput("script", { op: "account", password: "S3cret;& del *", name: "a\"b" });83
const [script, args] = input.trim().split("\n").map((line) => Buffer.from(line, "base64").toString("utf8"));84
assert.equal(script, "script");85
assert.deepEqual(JSON.parse(args), { op: "account", password: "S3cret;& del *", name: "a\"b" });86
assert.equal(command.includes("S3cret"), false);87
});89
test("PowerShell output parsing: results, error codes, cmd.exe failures and restricted PowerShell", () => {90
assert.deepEqual(parseWindowsOutput(ok({ users: [{ name: "Jiří" }] }), "<Objs>progress</Objs>", 0), { users: [{ name: "Jiří" }] });91
assert.throws(() => parseWindowsOutput(err("WINDOWS_ELEVATION_REQUIRED", "Token není zvýšený"), "", 0), { code: "USERS_WINDOWS_ELEVATION_REQUIRED", message: "Token není zvýšený" });92
assert.throws(() => parseWindowsOutput("", "'powershell.exe' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n", 1), { code: "USERS_POWERSHELL_UNAVAILABLE" });93
assert.throws(() => parseWindowsOutput("", "Cannot invoke method. Method invocation is supported only on core types in this language mode.", 1), { code: "USERS_POWERSHELL_RESTRICTED" });94
assert.throws(() => parseWindowsOutput("garbage", "", 0), { code: "USERS_PROTOCOL_ERROR" });95
assert.throws(() => parseWindowsOutput("CHJ_RESULT:not-base64-json", "", 0), { code: "USERS_PROTOCOL_ERROR" });96
});98
function fakeManager(responses) {99
const manager = { calls: [], record: { sessionId: "s", platform: null } };100
manager._requireConnected = () => manager.record;101
manager._windowsSerial = (_record, task) => task();102
manager._execStream = (_record, command) => {103
manager.calls.push(command);104
const response = responses.shift();105
return { done: response instanceof Error ? Promise.reject(response) : Promise.resolve({ code: 0, stdout: "", stderr: "", ...response }) };106
};107
return manager;108
}110
test("platform detection: uname for POSIX, Win32_OperatingSystem for Windows (cmd.exe, Git Bash uname), unknown otherwise", async () => {111
let manager = fakeManager([{ code: 0, stdout: "Linux\n" }]);112
assert.equal((await detectPlatform(manager, "s")).os, "linux");113
assert.equal(manager.calls.length, 1);114
const windowsInfo = { caption: "Microsoft Windows Server 2025 Datacenter", version: "10.0.26100", build: "26100", productType: 3, installationType: "Server Core", defaultShell: "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", psVersion: "5.1.26100.1", is64BitProcess: true, languageMode: "FullLanguage", localAccounts: true, pwsh: "" };115
manager = fakeManager([{ code: 1, stderr: "'uname' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n" }, { stdout: ok(windowsInfo) }]);116
const server = await detectPlatform(manager, "s");117
assert.equal(server.os, "windows"); assert.equal(server.serverCore, true); assert.equal(server.server, true); assert.equal(server.defaultShell, "powershell");118
assert.match(manager.calls[1], /-EncodedCommand /);119
manager = fakeManager([{ code: 0, stdout: "MINGW64_NT-10.0-26200\n" }, { stdout: ok({ ...windowsInfo, productType: 1, installationType: "Client", defaultShell: "" }) }]);120
const client = await detectPlatform(manager, "s");121
assert.equal(client.os, "windows", "Git for Windows uname does not make the host Linux"); assert.equal(client.defaultShell, "cmd.exe");122
manager = fakeManager([{ code: 127, stderr: "sh: uname: not found" }, { code: 127, stderr: "sh: powershell.exe: not found" }]);123
assert.equal((await detectPlatform(manager, "s")).os, "unknown");124
assert.equal(shellName("C:\\Program Files\\PowerShell\\7\\pwsh.exe"), "pwsh");125
assert.equal(shellName("C:\\Windows\\System32\\cmd.exe"), "cmd.exe");126
});128
test("Windows token states: elevated, Administrators member with filtered token, standard user", () => {129
assert.equal(privilegeFromToken({ elevated: true, adminMember: true, name: "PC\\admin", sid: "S-1-5-21-1-2-3-1001" }, { sshClient: "192.0.2.7 50000 22" }).mode, "windows-elevated");130
const filtered = privilegeFromToken({ elevated: false, adminMember: true, adminDenyOnly: true, name: "PC\\admin" }, { sshClient: "2001:db8::7 50000 22" });131
assert.equal(filtered.mode, "windows-admin-unelevated");132
assert.equal(filtered.clientAddress, "2001:db8::7");133
assert.equal(privilegeFromToken({ elevated: false, adminMember: false }, {}).mode, "windows-standard");134
assert.equal(filtered.sudoInstalled, false);135
});137
const MACHINE = "S-1-5-21-3493863762-3532507802-4093449623";138
function inventoryFixture({ elevated = true } = {}) {139
const user = (name, rid, extra = {}) => ({ name, sid: `${MACHINE}-${rid}`, enabled: true, fullName: "", description: "", source: "Local", lastLogon: null, passwordLastSet: "2026-10-01T10:00:00.0000000Z", passwordRequired: true, passwordExpires: null, accountExpires: null, userMayChangePassword: true, ...extra });140
return {141
system: { caption: "Microsoft Windows 11 Pro", version: "10.0.26200", displayVersion: "25H2", installationType: "Client", productType: 1, partOfDomain: true, domain: "corp.example", psVersion: "5.1.26100.9711", psEdition: "Desktop", is64BitProcess: true, sshClient: "192.0.2.142 51000 22" },142
token: { name: "WIN-TEST\\admin", sid: `${MACHINE}-1002`, elevated, adminMember: true, integrity: elevated ? "high" : "medium" },143
users: [user("Administrator", 500, { enabled: false }), user("Guest", 501, { enabled: false }), user("codex", 1002), user("novak", 1004, { fullName: "Jan Novák" }), user("yosef", 1001, { source: "MicrosoftAccount" })],144
// Localized (Czech) group names: Administrators must be identified by SID only.145
groups: [146
{ sid: "S-1-5-32-544", name: "Administrátoři", members: [{ sid: `${MACHINE}-500`, name: "PC\\Administrator" }, { sid: `${MACHINE}-1002`, name: "PC\\codex" }, { sid: "S-1-5-21-111-222-333-1105", name: "CORP\\ops", source: "ActiveDirectory" }] },147
{ sid: "S-1-5-32-545", name: "Uživatelé", members: [{ sid: `${MACHINE}-1004`, name: "PC\\novak" }, { sid: `${MACHINE}-1002`, name: "PC\\codex" }] },148
{ sid: "S-1-5-32-555", name: "Uživatelé vzdálené plochy", members: [] }149
],150
ssh: { installed: true, version: "OpenSSH_for_Windows_9.5p2", service: "Running", configReadable: true, configSha256: "abc", configData: Buffer.from(WINDOWS_CONFIG).toString("base64") }151
};152
}154
function service(runs, { alive = true } = {}) {155
const calls = [];156
const executor = { run: async (_sessionId, { args }) => { calls.push(args); const next = runs[args.op]; if (typeof next === "function") return next(args); if (next instanceof Error) throw next; return next; } };157
const sessionManager = {158
sessionAuthInfo: () => ({ username: "codex", authMethod: "password", publicKeyFingerprint: null }),159
probeLoginRequirements: (_id, credentials) => credentials.password ? { ready: true } : { ready: false, missing: "password" },160
connectionTarget: () => ({ profileId: "p", address: "192.0.2.143" }),161
_requireConnected: () => { if (!alive) throw Object.assign(new Error("gone"), { code: "SESSION_NOT_CONNECTED" }); return {}; },162
freshExec: async () => { throw Object.assign(new Error("A new SSH login was not confirmed: auth failed"), { code: "SSH_PROBE_FAILED" }); }163
};164
return { svc: new WindowsUserAdminService({ sessionManager, windowsExecutor: executor }), calls, sessionManager };165
}167
test("Windows inventory: SID identity, Administrators by SID with localized names, protected and external identities", async () => {168
const { svc } = service({ inventory: inventoryFixture() });169
const inventory = await svc.inventory("s", { defaultShell: "cmd.exe" });170
const byName = Object.fromEntries(inventory.users.map((user) => [user.username, user]));171
assert.equal(inventory.platform, "windows");172
assert.equal(byName.codex.admin, true);173
assert.deepEqual(byName.codex.groups.sort(), ["Administrátoři", "Uživatelé"].sort());174
assert.equal(byName.novak.admin, false);175
assert.equal(byName.Administrator.protection, "builtin");176
assert.equal(byName.codex.protection, "session");177
assert.equal(byName.novak.protection, null);178
assert.equal(byName.novak.uid, undefined, "no invented Linux UID");179
assert.equal(byName.novak.sudo, undefined, "Windows administrators are not presented as sudo");180
assert.equal(inventory.externalIdentities.length, 1);181
assert.equal(inventory.externalIdentities[0].name, "CORP\\ops");182
assert.equal(inventory.server.partOfDomain, true);183
assert.equal(inventory.privileged, true);184
assert.equal(inventory.server.sshLayout, "ok");185
});187
test("Windows management refuses without an elevated token, for protected accounts, Linux-only actions and bad input", async () => {188
const unelevated = service({ probe: { token: inventoryFixture().token && { ...inventoryFixture({ elevated: false }).token }, system: inventoryFixture().system } });189
await assert.rejects(() => unelevated.svc.manage("s", { action: "disable", sid: `${MACHINE}-1004` }), { code: "USERS_WINDOWS_ELEVATION_REQUIRED", message: /UAC/ });190
assert.deepEqual(unelevated.calls.map((call) => call.op), ["probe"], "nothing is attempted without elevation");191
const elevated = service({ probe: { token: inventoryFixture().token, system: inventoryFixture().system }, inventory: inventoryFixture(), account: { result: "ok", steps: [] } });192
await assert.rejects(() => elevated.svc.manage("s", { action: "disable", sid: `${MACHINE}-500` }), { code: "USERS_PROTECTED_USER" });193
await assert.rejects(() => elevated.svc.manage("s", { action: "delete", sid: `${MACHINE}-1002` }), { code: "USERS_PROTECTED_USER" });194
await assert.rejects(() => elevated.svc.manage("s", { action: "setShell", sid: `${MACHINE}-1004`, shell: "/bin/sh" }), { code: "USERS_NOT_SUPPORTED_ON_PLATFORM" });195
await assert.rejects(() => elevated.svc.manage("s", { action: "disable", sid: "S-1-5-21-1-2-3-4;Remove-LocalUser" }), { code: "USERS_SID_INVALID" });196
await assert.rejects(() => elevated.svc.manage("s", { action: "addGroup", sid: `${MACHINE}-1004`, group: "Administrators" }), { code: "USERS_GROUP_INVALID" });197
await assert.rejects(() => elevated.svc.manage("s", { action: "setPassword", sid: `${MACHINE}-1004`, password: "a\r\nb" }), { code: "USERS_PASSWORD_INVALID" });198
await elevated.svc.manage("s", { action: "grantAdmin", sid: `${MACHINE}-1004` });199
const call = elevated.calls.find((entry) => entry.op === "account");200
assert.deepEqual(call, { op: "account", action: "admin-grant", sid: `${MACHINE}-1004` });201
for (const name of ["a;b", "x$(y)", "a|b", "a\"b", "bad/name", "toolong_abcdefghijklm", "end.", "mid\nline"]) assert.throws(() => validateWindowsName(name), { code: "USERS_NAME_INVALID" }, name);202
assert.equal(validateWindowsName("Jan Novak"), "Jan Novak");203
});205
function detailFixture(name = "novak", rid = 1004, groups = [{ sid: "S-1-5-32-545", name: "Uživatelé" }]) {206
const effective = (extra = "") => ({ code: 0, text: `passwordauthentication yes\nkbdinteractiveauthentication yes\npubkeyauthentication yes\nauthorizedkeysfile .ssh/authorized_keys\nlistenaddress 0.0.0.0:22\nlistenaddress [::]:22${extra}` });207
return {208
user: { name, sid: `${MACHINE}-${rid}`, enabled: true, fullName: "", description: "", source: "Local" },209
groups, profilePath: `C:\\Users\\${name}`, token: inventoryFixture().token, system: inventoryFixture().system,210
ssh: inventoryFixture().ssh, syntax: { code: 0, output: "" }, effective4: effective(), effective6: effective(),211
keyFiles: [{ spec: ".ssh/authorized_keys", path: `C:\\Users\\${name}\\.ssh\\authorized_keys`, shared: false, type: "absent", dirExists: true, reparse: false, acl: [] }]212
};213
}215
test("Windows detail: effective IPv4/IPv6 state, shared administrators key file and unsafe ACL warnings", async () => {216
const admin = detailFixture("codex", 1002, [{ sid: "S-1-5-32-544", name: "Administrátoři" }]);217
admin.keyFiles = [{ spec: "__PROGRAMDATA__/ssh/administrators_authorized_keys", path: "C:\\ProgramData\\ssh\\administrators_authorized_keys", shared: true, type: "file", reparse: false, sha256: "x", data: "", acl: [{ sid: "S-1-5-18", rights: "FullControl", type: "Allow" }, { sid: "S-1-5-32-544", rights: "FullControl", type: "Allow" }, { sid: "S-1-5-11", rights: "Modify, Synchronize", type: "Allow" }] }];218
const { svc } = service({ probe: { token: inventoryFixture().token, system: inventoryFixture().system }, detail: admin });219
const detail = await svc.detail("s", { sid: `${MACHINE}-1002` }, {});220
assert.equal(detail.account.admin, true);221
assert.equal(detail.self, true);222
assert.equal(detail.keys.files[0].shared, true);223
assert.ok(detail.ssh.warnings.some((warning) => /share one administrators_authorized_keys/.test(warning)));224
assert.ok(detail.ssh.warnings.some((warning) => /S-1-5-11 has Modify/.test(warning)));225
assert.equal(detail.ssh.effective.ipv4.methods.password, true);226
assert.equal(detail.ssh.layoutSupported, true);227
assert.equal(Object.keys(detail).some((key) => key.startsWith("_") && key !== "_keyFiles" && key !== "_config" && key !== "_addresses" && key !== "_groups"), false);228
});230
test("Windows SSH policy: existing Match precedence, lockout and missing test-login credentials stop before any change", async () => {231
const stageWith = (afterExtra) => (args) => ({232
id: "a".repeat(32), syntaxCode: 0, syntaxOutput: "", candidateSha256: "c",233
before: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: "passwordauthentication yes\npubkeyauthentication yes" }])),234
after: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: afterExtra(tuple) }]))235
});236
const base = { probe: { token: inventoryFixture().token, system: inventoryFixture().system }, detail: detailFixture() };237
let fixture = service({ ...base, "ssh-stage": stageWith(() => "passwordauthentication yes\npubkeyauthentication yes") });238
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "x" } }), { code: "USERS_SSH_POLICY_NOT_EFFECTIVE" });239
assert.equal(fixture.calls.some((call) => call.op === "ssh-commit"), false, "nothing is committed");240
fixture = service(base);241
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" } }), { code: "USERS_SSH_PROBE_CREDENTIALS_REQUIRED" });242
assert.equal(fixture.calls.some((call) => call.op === "ssh-stage"), false);243
fixture = service({ ...base, "ssh-stage": stageWith((tuple) => tuple.user === "codex" ? "passwordauthentication yes\npubkeyauthentication yes\ndenyusers codex@0.0.0.0/0" : "passwordauthentication no\npubkeyauthentication yes") });244
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "x" } }), { code: "USERS_SSH_LOCKOUT_PREVENTED" });245
assert.equal(fixture.calls.some((call) => call.op === "ssh-commit"), false);246
});248
test("Windows SSH policy: a failing new login never confirms and the server-side guard rolls back", async () => {249
const fixture = service({250
probe: { token: inventoryFixture().token, system: inventoryFixture().system },251
detail: detailFixture(),252
"ssh-stage": (args) => ({ id: "b".repeat(32), syntaxCode: 0, syntaxOutput: "", candidateSha256: "c",253
before: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: "passwordauthentication yes\npubkeyauthentication yes" }])),254
after: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: tuple.user === "novak" ? "passwordauthentication no\npubkeyauthentication yes" : "passwordauthentication yes\npubkeyauthentication yes" }])) }),255
"ssh-commit": { id: "b".repeat(32), state: "awaiting-confirmation", message: "sshd restarted" },256
"ssh-confirm": (args) => ({ id: args.id, state: args.confirm ? "confirmed" : "rolled-back", message: "No confirmation from a new SSH login before the timeout." })257
});258
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "wrong" } }), (error) => {259
assert.equal(error.code, "USERS_SSH_PROBE_FAILED");260
assert.match(error.message, /rolled-back/);261
return true;262
});263
const ops = fixture.calls.map((call) => call.op);264
assert.deepEqual(ops.slice(-3), ["ssh-stage", "ssh-commit", "ssh-confirm"]);265
assert.equal(fixture.calls.at(-1).confirm, false, "the old session only waits for the guard and never confirms");266
assert.match(fixture.calls.find((call) => call.op === "ssh-commit").nonce, /^[0-9a-f]{48}$/);267
});269
test("the shared users service routes Windows sessions to the Windows backend without touching sudo", async () => {270
const calls = [];271
const windowsService = new Proxy({}, { get: (_target, name) => async (...args) => { calls.push(name); return { platform: "windows", args: args.length }; } });272
const linuxExecutor = { probe: async () => { throw new Error("sudo probe must not run on Windows"); } };273
const sessionManager = { _requireConnected: () => ({}) };274
const shared = new UserAdminService({ sessionManager, privilegedExecutor: linuxExecutor, windowsService, detectPlatform: async () => ({ os: "windows", defaultShell: "cmd.exe" }) });275
for (const method of ["privileges", "inventory", "detail", "manage", "keys", "sshPolicy"]) await shared[method]("s", {});276
assert.deepEqual(calls, ["privileges", "inventory", "detail", "manage", "keys", "sshPolicy"]);277
const unknown = new UserAdminService({ sessionManager, privilegedExecutor: linuxExecutor, windowsService, detectPlatform: async () => ({ os: "unknown", reason: "no shell" }) });278
await assert.rejects(() => unknown.inventory("s"), { code: "USERS_PLATFORM_UNSUPPORTED" });279
});281
test("Windows remote script keeps secrets in stdin, identifies Administrators by SID and arms rollback before restart", () => {282
const script = require("node:fs").readFileSync(require("node:path").join(__dirname, "../src/main/users/windows/remoteUsers.ps1"), "utf8");283
assert.match(script, /\$AdminsSid = 'S-1-5-32-544'/);284
assert.doesNotMatch(script, /-Name '?Administrators'?|Get-LocalGroup -Name Administrators/i, "no localized group names");285
assert.doesNotMatch(script, /KbdInteractiveAuthentication/);286
assert.doesNotMatch(script, /\bsudo\b/);287
assert.doesNotMatch(script, /Invoke-Expression|\biex\b/);288
assert.match(script, /\[NullString\]::Value/, "File.Replace receives a real null backup path");289
const apply = script.slice(script.indexOf("$ApplyScript = @'"));290
assert.ok(apply.indexOf("Status 'armed'") < apply.indexOf("RestartSshd\n") || apply.indexOf("Status 'armed'") < apply.indexOf("try { RestartSshd }"), "rollback guard is armed before the restart");291
assert.match(apply, /Restore 'No confirmation from a new SSH login before the timeout.'/);292
});294
test("Windows helpers of one session run one after another, also after a failure (Win32-OpenSSH stalls on concurrent stdin helpers)", async () => {295
const { SessionManager } = require("../src/main/sessions/sessionManager");296
const { WindowsExecutor } = require("../src/main/sessions/windowsPrivilegedExec");297
const manager = new SessionManager({ latencyMonitor: { start: () => () => {} }, profileService: {} });298
const record = { sessionId: "w", state: "connected" };299
manager._requireConnected = () => record;300
let active = 0; let peak = 0; const order = [];301
manager._execStream = (_record, _command, { onUpdate }) => {302
active += 1; peak = Math.max(peak, active);303
const controller = { stdout: "", stderr: "", write: (text) => order.push(JSON.parse(Buffer.from(text.split("\n")[1], "base64").toString("utf8")).op), end() {} };304
onUpdate(controller, "open");305
const op = order[order.length - 1];306
const done = new Promise((resolve, reject) => setTimeout(() => {307
active -= 1;308
if (op === "boom") reject(Object.assign(new Error("remote failure"), { code: "REMOTE_COMMAND_FAILED" }));309
else resolve({ code: 0, stderr: "", stdout: ok({ op }) });310
}, 15));311
return { done };312
};313
const executor = new WindowsExecutor({ sessionManager: manager });314
const results = await Promise.allSettled([315
executor.run("w", { script: "s", args: { op: "list" } }),316
executor.run("w", { script: "s", args: { op: "boom" } }),317
manager._runWindowsScript(record, "s", { op: "metrics" }),318
executor.run("w", { script: "s", args: { op: "inventory" } })319
]);320
assert.equal(peak, 1, "never two helpers at once on one session");321
assert.deepEqual(order, ["list", "boom", "metrics", "inventory"]);322
assert.deepEqual(results.map((result) => result.status), ["fulfilled", "rejected", "fulfilled", "fulfilled"]);323
assert.equal(results[3].value.op, "inventory", "the queue continues after a failed helper");324
});SHA-256
f5a6506e918fafcdb108c39bc0f922f77dddb9d56ec02c16bb764870419d9786CH-J Proprietary Software License 1.14 · La sorgente è fornita sotto CH-J Proprietary Software License 1.14. La sua disponibilità non modifica i termini di licenza o concede diritti aggiuntivi.
Archivio SHA-256
476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6