Foinse foilsithe
Íosluchtaigh foinse ZIP
CH-J Server Manager
Brabhsáil comhadlanna agus comhaid chun feidhmchlár áirithe a scaoileadh.
CH-J / Server Manager
377 Comhaid
core-20261019T111616Z-160a00bfcddc
Suimigh: · Tógtha:
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._execStream = (_record, command) => {102
manager.calls.push(command);103
const response = responses.shift();104
return { done: response instanceof Error ? Promise.reject(response) : Promise.resolve({ code: 0, stdout: "", stderr: "", ...response }) };105
};106
return manager;107
}109
test("platform detection: uname for POSIX, Win32_OperatingSystem for Windows (cmd.exe, Git Bash uname), unknown otherwise", async () => {110
let manager = fakeManager([{ code: 0, stdout: "Linux\n" }]);111
assert.equal((await detectPlatform(manager, "s")).os, "linux");112
assert.equal(manager.calls.length, 1);113
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: "" };114
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) }]);115
const server = await detectPlatform(manager, "s");116
assert.equal(server.os, "windows"); assert.equal(server.serverCore, true); assert.equal(server.server, true); assert.equal(server.defaultShell, "powershell");117
assert.match(manager.calls[1], /-EncodedCommand /);118
manager = fakeManager([{ code: 0, stdout: "MINGW64_NT-10.0-26200\n" }, { stdout: ok({ ...windowsInfo, productType: 1, installationType: "Client", defaultShell: "" }) }]);119
const client = await detectPlatform(manager, "s");120
assert.equal(client.os, "windows", "Git for Windows uname does not make the host Linux"); assert.equal(client.defaultShell, "cmd.exe");121
manager = fakeManager([{ code: 127, stderr: "sh: uname: not found" }, { code: 127, stderr: "sh: powershell.exe: not found" }]);122
assert.equal((await detectPlatform(manager, "s")).os, "unknown");123
assert.equal(shellName("C:\\Program Files\\PowerShell\\7\\pwsh.exe"), "pwsh");124
assert.equal(shellName("C:\\Windows\\System32\\cmd.exe"), "cmd.exe");125
});127
test("Windows token states: elevated, Administrators member with filtered token, standard user", () => {128
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");129
const filtered = privilegeFromToken({ elevated: false, adminMember: true, adminDenyOnly: true, name: "PC\\admin" }, { sshClient: "2001:db8::7 50000 22" });130
assert.equal(filtered.mode, "windows-admin-unelevated");131
assert.equal(filtered.clientAddress, "2001:db8::7");132
assert.equal(privilegeFromToken({ elevated: false, adminMember: false }, {}).mode, "windows-standard");133
assert.equal(filtered.sudoInstalled, false);134
});136
const MACHINE = "S-1-5-21-3493863762-3532507802-4093449623";137
function inventoryFixture({ elevated = true } = {}) {138
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 });139
return {140
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" },141
token: { name: "WIN-TEST\\admin", sid: `${MACHINE}-1002`, elevated, adminMember: true, integrity: elevated ? "high" : "medium" },142
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" })],143
// Localized (Czech) group names: Administrators must be identified by SID only.144
groups: [145
{ 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" }] },146
{ sid: "S-1-5-32-545", name: "Uživatelé", members: [{ sid: `${MACHINE}-1004`, name: "PC\\novak" }, { sid: `${MACHINE}-1002`, name: "PC\\codex" }] },147
{ sid: "S-1-5-32-555", name: "Uživatelé vzdálené plochy", members: [] }148
],149
ssh: { installed: true, version: "OpenSSH_for_Windows_9.5p2", service: "Running", configReadable: true, configSha256: "abc", configData: Buffer.from(WINDOWS_CONFIG).toString("base64") }150
};151
}153
function service(runs, { alive = true } = {}) {154
const calls = [];155
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; } };156
const sessionManager = {157
sessionAuthInfo: () => ({ username: "codex", authMethod: "password", publicKeyFingerprint: null }),158
probeLoginRequirements: (_id, credentials) => credentials.password ? { ready: true } : { ready: false, missing: "password" },159
connectionTarget: () => ({ profileId: "p", address: "192.0.2.143" }),160
_requireConnected: () => { if (!alive) throw Object.assign(new Error("gone"), { code: "SESSION_NOT_CONNECTED" }); return {}; },161
freshExec: async () => { throw Object.assign(new Error("A new SSH login was not confirmed: auth failed"), { code: "SSH_PROBE_FAILED" }); }162
};163
return { svc: new WindowsUserAdminService({ sessionManager, windowsExecutor: executor }), calls, sessionManager };164
}166
test("Windows inventory: SID identity, Administrators by SID with localized names, protected and external identities", async () => {167
const { svc } = service({ inventory: inventoryFixture() });168
const inventory = await svc.inventory("s", { defaultShell: "cmd.exe" });169
const byName = Object.fromEntries(inventory.users.map((user) => [user.username, user]));170
assert.equal(inventory.platform, "windows");171
assert.equal(byName.codex.admin, true);172
assert.deepEqual(byName.codex.groups.sort(), ["Administrátoři", "Uživatelé"].sort());173
assert.equal(byName.novak.admin, false);174
assert.equal(byName.Administrator.protection, "builtin");175
assert.equal(byName.codex.protection, "session");176
assert.equal(byName.novak.protection, null);177
assert.equal(byName.novak.uid, undefined, "no invented Linux UID");178
assert.equal(byName.novak.sudo, undefined, "Windows administrators are not presented as sudo");179
assert.equal(inventory.externalIdentities.length, 1);180
assert.equal(inventory.externalIdentities[0].name, "CORP\\ops");181
assert.equal(inventory.server.partOfDomain, true);182
assert.equal(inventory.privileged, true);183
assert.equal(inventory.server.sshLayout, "ok");184
});186
test("Windows management refuses without an elevated token, for protected accounts, Linux-only actions and bad input", async () => {187
const unelevated = service({ probe: { token: inventoryFixture().token && { ...inventoryFixture({ elevated: false }).token }, system: inventoryFixture().system } });188
await assert.rejects(() => unelevated.svc.manage("s", { action: "disable", sid: `${MACHINE}-1004` }), { code: "USERS_WINDOWS_ELEVATION_REQUIRED", message: /UAC/ });189
assert.deepEqual(unelevated.calls.map((call) => call.op), ["probe"], "nothing is attempted without elevation");190
const elevated = service({ probe: { token: inventoryFixture().token, system: inventoryFixture().system }, inventory: inventoryFixture(), account: { result: "ok", steps: [] } });191
await assert.rejects(() => elevated.svc.manage("s", { action: "disable", sid: `${MACHINE}-500` }), { code: "USERS_PROTECTED_USER" });192
await assert.rejects(() => elevated.svc.manage("s", { action: "delete", sid: `${MACHINE}-1002` }), { code: "USERS_PROTECTED_USER" });193
await assert.rejects(() => elevated.svc.manage("s", { action: "setShell", sid: `${MACHINE}-1004`, shell: "/bin/sh" }), { code: "USERS_NOT_SUPPORTED_ON_PLATFORM" });194
await assert.rejects(() => elevated.svc.manage("s", { action: "disable", sid: "S-1-5-21-1-2-3-4;Remove-LocalUser" }), { code: "USERS_SID_INVALID" });195
await assert.rejects(() => elevated.svc.manage("s", { action: "addGroup", sid: `${MACHINE}-1004`, group: "Administrators" }), { code: "USERS_GROUP_INVALID" });196
await assert.rejects(() => elevated.svc.manage("s", { action: "setPassword", sid: `${MACHINE}-1004`, password: "a\r\nb" }), { code: "USERS_PASSWORD_INVALID" });197
await elevated.svc.manage("s", { action: "grantAdmin", sid: `${MACHINE}-1004` });198
const call = elevated.calls.find((entry) => entry.op === "account");199
assert.deepEqual(call, { op: "account", action: "admin-grant", sid: `${MACHINE}-1004` });200
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);201
assert.equal(validateWindowsName("Jan Novak"), "Jan Novak");202
});204
function detailFixture(name = "novak", rid = 1004, groups = [{ sid: "S-1-5-32-545", name: "Uživatelé" }]) {205
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}` });206
return {207
user: { name, sid: `${MACHINE}-${rid}`, enabled: true, fullName: "", description: "", source: "Local" },208
groups, profilePath: `C:\\Users\\${name}`, token: inventoryFixture().token, system: inventoryFixture().system,209
ssh: inventoryFixture().ssh, syntax: { code: 0, output: "" }, effective4: effective(), effective6: effective(),210
keyFiles: [{ spec: ".ssh/authorized_keys", path: `C:\\Users\\${name}\\.ssh\\authorized_keys`, shared: false, type: "absent", dirExists: true, reparse: false, acl: [] }]211
};212
}214
test("Windows detail: effective IPv4/IPv6 state, shared administrators key file and unsafe ACL warnings", async () => {215
const admin = detailFixture("codex", 1002, [{ sid: "S-1-5-32-544", name: "Administrátoři" }]);216
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" }] }];217
const { svc } = service({ probe: { token: inventoryFixture().token, system: inventoryFixture().system }, detail: admin });218
const detail = await svc.detail("s", { sid: `${MACHINE}-1002` }, {});219
assert.equal(detail.account.admin, true);220
assert.equal(detail.self, true);221
assert.equal(detail.keys.files[0].shared, true);222
assert.ok(detail.ssh.warnings.some((warning) => /share one administrators_authorized_keys/.test(warning)));223
assert.ok(detail.ssh.warnings.some((warning) => /S-1-5-11 has Modify/.test(warning)));224
assert.equal(detail.ssh.effective.ipv4.methods.password, true);225
assert.equal(detail.ssh.layoutSupported, true);226
assert.equal(Object.keys(detail).some((key) => key.startsWith("_") && key !== "_keyFiles" && key !== "_config" && key !== "_addresses" && key !== "_groups"), false);227
});229
test("Windows SSH policy: existing Match precedence, lockout and missing test-login credentials stop before any change", async () => {230
const stageWith = (afterExtra) => (args) => ({231
id: "a".repeat(32), syntaxCode: 0, syntaxOutput: "", candidateSha256: "c",232
before: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: "passwordauthentication yes\npubkeyauthentication yes" }])),233
after: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: afterExtra(tuple) }]))234
});235
const base = { probe: { token: inventoryFixture().token, system: inventoryFixture().system }, detail: detailFixture() };236
let fixture = service({ ...base, "ssh-stage": stageWith(() => "passwordauthentication yes\npubkeyauthentication yes") });237
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "x" } }), { code: "USERS_SSH_POLICY_NOT_EFFECTIVE" });238
assert.equal(fixture.calls.some((call) => call.op === "ssh-commit"), false, "nothing is committed");239
fixture = service(base);240
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" } }), { code: "USERS_SSH_PROBE_CREDENTIALS_REQUIRED" });241
assert.equal(fixture.calls.some((call) => call.op === "ssh-stage"), false);242
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") });243
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "x" } }), { code: "USERS_SSH_LOCKOUT_PREVENTED" });244
assert.equal(fixture.calls.some((call) => call.op === "ssh-commit"), false);245
});247
test("Windows SSH policy: a failing new login never confirms and the server-side guard rolls back", async () => {248
const fixture = service({249
probe: { token: inventoryFixture().token, system: inventoryFixture().system },250
detail: detailFixture(),251
"ssh-stage": (args) => ({ id: "b".repeat(32), syntaxCode: 0, syntaxOutput: "", candidateSha256: "c",252
before: Object.fromEntries(args.tuples.map((tuple) => [`${tuple.user}|${tuple.addr}`, { code: 0, text: "passwordauthentication yes\npubkeyauthentication yes" }])),253
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" }])) }),254
"ssh-commit": { id: "b".repeat(32), state: "awaiting-confirmation", message: "sshd restarted" },255
"ssh-confirm": (args) => ({ id: args.id, state: args.confirm ? "confirmed" : "rolled-back", message: "No confirmation from a new SSH login before the timeout." })256
});257
await assert.rejects(() => fixture.svc.sshPolicy("s", { sid: `${MACHINE}-1004`, policy: { password: "no" }, probeCredentials: { password: "wrong" } }), (error) => {258
assert.equal(error.code, "USERS_SSH_PROBE_FAILED");259
assert.match(error.message, /rolled-back/);260
return true;261
});262
const ops = fixture.calls.map((call) => call.op);263
assert.deepEqual(ops.slice(-3), ["ssh-stage", "ssh-commit", "ssh-confirm"]);264
assert.equal(fixture.calls.at(-1).confirm, false, "the old session only waits for the guard and never confirms");265
assert.match(fixture.calls.find((call) => call.op === "ssh-commit").nonce, /^[0-9a-f]{48}$/);266
});268
test("the shared users service routes Windows sessions to the Windows backend without touching sudo", async () => {269
const calls = [];270
const windowsService = new Proxy({}, { get: (_target, name) => async (...args) => { calls.push(name); return { platform: "windows", args: args.length }; } });271
const linuxExecutor = { probe: async () => { throw new Error("sudo probe must not run on Windows"); } };272
const sessionManager = { _requireConnected: () => ({}) };273
const shared = new UserAdminService({ sessionManager, privilegedExecutor: linuxExecutor, windowsService, detectPlatform: async () => ({ os: "windows", defaultShell: "cmd.exe" }) });274
for (const method of ["privileges", "inventory", "detail", "manage", "keys", "sshPolicy"]) await shared[method]("s", {});275
assert.deepEqual(calls, ["privileges", "inventory", "detail", "manage", "keys", "sshPolicy"]);276
const unknown = new UserAdminService({ sessionManager, privilegedExecutor: linuxExecutor, windowsService, detectPlatform: async () => ({ os: "unknown", reason: "no shell" }) });277
await assert.rejects(() => unknown.inventory("s"), { code: "USERS_PLATFORM_UNSUPPORTED" });278
});280
test("Windows remote script keeps secrets in stdin, identifies Administrators by SID and arms rollback before restart", () => {281
const script = require("node:fs").readFileSync(require("node:path").join(__dirname, "../src/main/users/windows/remoteUsers.ps1"), "utf8");282
assert.match(script, /\$AdminsSid = 'S-1-5-32-544'/);283
assert.doesNotMatch(script, /-Name '?Administrators'?|Get-LocalGroup -Name Administrators/i, "no localized group names");284
assert.doesNotMatch(script, /KbdInteractiveAuthentication/);285
assert.doesNotMatch(script, /\bsudo\b/);286
assert.doesNotMatch(script, /Invoke-Expression|\biex\b/);287
assert.match(script, /\[NullString\]::Value/, "File.Replace receives a real null backup path");288
const apply = script.slice(script.indexOf("$ApplyScript = @'"));289
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");290
assert.match(apply, /Restore 'No confirmation from a new SSH login before the timeout.'/);291
});SHA-256
ec0f348f2f5c840620142742571a4f818455390188939569593252d10e415d02CH-J Proprietary Software License 1.14 · Tá Foinse ar fáil faoin CH-J Proprietary Software License 1.14. Ní chuireann a infhaighteacht a athrú ar na téarmaí ceadúnais nó cearta breise a dheonú.
Cartlann SHA-256
a9605c0870a223b6a8542bb0705edd51d8b04f3069582653417a8397fabcc09a