Opublikowane źródło
Pobierz źródło ZIP
CH-J Server Manager
Przeglądaj katalogi i pliki dla konkretnego wydania aplikacji.
CH-J / Server Manager
339 pliki
core-20261014T184258Z-3fbed0b93fdf
Dodano: Data niezanotowana · Built:
1
"use strict";3
const test = require("node:test");4
const assert = require("node:assert/strict");5
const fs = require("node:fs");6
const os = require("node:os");7
const path = require("node:path");8
const { ProfileService } = require("../src/main/profiles/profileService");9
const { VaultStore } = require("../src/main/security/vaultStore");10
const { ProfileTransfer, serializeProfiles, parseProfiles, identity, MAX_BYTES } = require("../src/main/profiles/profileTransfer");12
const document = (servers, version = 1) => JSON.stringify({ format: "chj-server-profiles", version, servers });13
const server = { name: 'Žluťoučký, "server"', host: "NGINX.lan", username: "root", port: 22 };15
async function setup(t) {16
const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-transfer-"));17
t.after(() => fs.rmSync(root, { recursive: true, force: true }));18
const vault = new VaultStore(root); await vault.create("transfer-test-password");19
const profiles = new ProfileService(vault);20
return { vault, profiles, transfer: new ProfileTransfer(profiles) };21
}23
test("JSON and CSV round trip UTF-8, original DNS/IPv4/IPv6 and never serialize secrets or paths", () => {24
const servers = [server, { ...server, host: "192.168.1.10" }, { ...server, host: "2001:db8::10", port: 2222 }, { ...server, name: "=FORMULA()", username: "@user" }, { ...server, name: "'literal" }];25
const profiles = servers.map((item) => ({ ...item, label: item.name, password: "do-not-export", privateKeyPath: "/secret/key", token: "secret-token", resolvedIp: "1.2.3.4", hasStoredPassword: true }));26
for (const format of ["json", "csv"]) {27
const exported = serializeProfiles(profiles, format);28
assert.doesNotMatch(exported, /do-not-export|\/secret\/key|secret-token|1\.2\.3\.4|hasStoredPassword/);29
assert.deepEqual(parseProfiles(exported).servers, servers);30
}31
assert.match(serializeProfiles(profiles, "csv"), /'=FORMULA/);32
assert.deepEqual(Object.keys(JSON.parse(serializeProfiles(profiles)).servers[0]), ["name", "host", "username", "port"]);33
});35
test("import validates the entire document, version, fields, CSV structure and limits", () => {36
for (const input of [document([server], 2), document([{ ...server, host: "bad host" }]), document([{ ...server, port: 0 }]), document([{ ...server, port: 65536 }]), document([{ ...server, port: null }]), document([{ ...server, username: "bad\nuser" }]), document([]), "name,ip,username,port\nx,1.2.3.4,root,22", 'name,host,username,port\n"bad,host,root,22', "name,host,username,port\nx,host,root,22,extra", "x".repeat(MAX_BYTES + 1)]) assert.throws(() => parseProfiles(input));37
assert.deepEqual(parseProfiles('port,username,host,name\r\n22,root,nginx.lan,NGINX\r\n').servers[0], { name: "NGINX", host: "nginx.lan", username: "root", port: 22 });38
assert.equal(parseProfiles(document([{ ...server, port: undefined }])).servers[0].port, 22);39
assert.equal(identity({ ...server, host: "2001:db8::1" }), identity({ ...server, host: "2001:0db8:0:0:0:0:0:1" }));40
});42
test("preview is read-only; skip, update and create handle vault and file duplicates without importing authentication", async (t) => {43
for (const policy of ["skip", "update", "create"]) {44
const { profiles, vault, transfer } = await setup(t);45
const saved = profiles.save({ label: "Old", host: "nginx.lan", username: "root", storePassword: true, password: "old-password" });46
const preview = transfer.preview(document([{ ...server, password: "incoming-password", privateKeyPath: "/incoming/key" }, { ...server, name: "Second" }]));47
assert.deepEqual(preview.servers.map((item) => item.duplicate), [true, true]);48
assert.equal(profiles.get(saved.id).label, "Old");49
assert.equal(profiles.getStoredPassword(saved.id), "old-password");50
assert.deepEqual(transfer.commit(preview.token, policy), policy === "skip" ? { created: 0, updated: 0, skipped: 2 } : policy === "update" ? { created: 0, updated: 2, skipped: 0 } : { created: 2, updated: 0, skipped: 0 });51
if (policy === "update") { assert.equal(profiles.getStoredPassword(saved.id), null); assert.equal(profiles.get(saved.id).label, "Second"); }52
if (policy === "create") {53
assert.equal(profiles.list().length, 3);54
for (const profile of profiles.list().filter((item) => item.id !== saved.id)) { assert.equal(profile.hasStoredPassword, false); assert.equal(profile.privateKeyPath, ""); }55
}56
assert.doesNotMatch(JSON.stringify(vault.getData()), /incoming-password|\/incoming\/key/);57
assert.throws(() => transfer.commit(preview.token, policy), /expired/);58
}59
});61
test("updating a key-authenticated duplicate removes key paths and imported rows require new authentication", async (t) => {62
const { profiles, transfer } = await setup(t);63
const saved = profiles.save({ label: "Key server", host: server.host, username: server.username, tags: ["production"], authMethod: "privateKey", privateKeyPath: "/existing/key" });64
const preview = transfer.preview(document([server, { ...server, host: "other.lan" }]));65
assert.deepEqual(transfer.commit(preview.token, "update"), { created: 1, updated: 1, skipped: 0 });66
assert.equal(profiles.get(saved.id).authMethod, "password");67
assert.equal(profiles.get(saved.id).privateKeyPath, "");68
assert.deepEqual(profiles.get(saved.id).tags, ["production"]);69
});71
test("cancel, lock and stale previews cannot be committed; invalid import leaves the vault intact", async (t) => {72
const { profiles, vault, transfer } = await setup(t);73
let preview = transfer.preview(document([server])); transfer.cancel();74
assert.throws(() => transfer.commit(preview.token, "create"), /expired/);75
preview = transfer.preview(document([server])); vault.lock(); await vault.unlock("transfer-test-password");76
assert.throws(() => transfer.commit(preview.token, "create"), /expired/);77
assert.throws(() => transfer.preview(document([server, { ...server, port: -1 }])));78
assert.equal(profiles.list().length, 0);79
preview = transfer.preview(document([server]));80
assert.throws(() => transfer.commit(preview.token, "invalid"), /policy/);81
assert.equal(profiles.list().length, 0);82
});SHA-256
6796f8af65b6aa706ade0a90daa466662eb60699053a6ff97b5c859a022c579cCH-J Proprietary Software License 1.14 · Źródło jest dostarczane pod CH-J Proprietary Software License 1.14. Jego dostępność nie zmienia warunków licencji ani nie przyznaje dodatkowych praw.
SHA-256
3df08cf5e899262a6ce92d7ec2caf5c42b683f1f2b57055662b845ff7dde67a8