Vydané zdroje
Stáhnout zdroje ZIP
CH-J Server Manager
Procházejte adresáře a soubory konkrétního vydání aplikace.
Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.
1
"use strict";3
const fs = require("node:fs");4
const path = require("node:path");5
const { spawn } = require("node:child_process");7
const PKEXEC_PATH = "/usr/bin/pkexec";8
const APT_GET_PATH = "/usr/bin/apt-get";9
const DEFAULT_INSTALL_TIMEOUT_MS = 30 * 60 * 1000;10
const MAX_OUTPUT_CHARS = 32 * 1024;12
class UpdateInstallError extends Error {13
constructor(message, code = "UPDATE_INSTALL_FAILED", options = {}) {14
super(message, options);15
this.name = "UpdateInstallError";16
this.code = code;17
}18
}20
function assertSafeRegularFile(filePath, label) {21
if (!path.isAbsolute(filePath)) {22
throw new UpdateInstallError(`${label} path must be absolute.`, "UPDATE_INSTALL_UNSAFE_PATH");23
}24
let stat;25
try {26
stat = fs.lstatSync(filePath);27
} catch (error) {28
throw new UpdateInstallError(`${label} is missing.`, "UPDATE_INSTALL_FILE_MISSING", { cause: error });29
}30
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size <= 0) {31
throw new UpdateInstallError(`${label} must be a non-empty regular file.`, "UPDATE_INSTALL_UNSAFE_PATH");32
}33
return stat;34
}36
function assertTrustedSystemExecutable(filePath) {37
const stat = assertSafeRegularFile(filePath, "System installer executable");38
if (typeof stat.uid === "number" && stat.uid !== 0) {39
throw new UpdateInstallError("System installer executable is not owned by root.", "UPDATE_INSTALL_UNSAFE_EXECUTABLE");40
}41
if ((stat.mode & 0o111) === 0) {42
throw new UpdateInstallError("System installer executable is not executable.", "UPDATE_INSTALL_UNSAFE_EXECUTABLE");43
}44
if ((stat.mode & 0o022) !== 0) {45
throw new UpdateInstallError("System installer executable is writable by an untrusted account.", "UPDATE_INSTALL_UNSAFE_EXECUTABLE");46
}47
}49
function appendBounded(current, chunk) {50
const next = `${current}${String(chunk || "")}`;51
return next.length > MAX_OUTPUT_CHARS ? next.slice(-MAX_OUTPUT_CHARS) : next;52
}54
class UpdateInstallerLauncher {55
constructor(options = {}) {56
this.platform = options.platform || process.platform;57
this.shell = options.shell;58
this.spawnImpl = options.spawnImpl || spawn;59
this.logger = options.logger;60
this.installTimeoutMs = Number(options.installTimeoutMs || DEFAULT_INSTALL_TIMEOUT_MS);61
this.pkexecPath = options.pkexecPath || PKEXEC_PATH;62
this.aptGetPath = options.aptGetPath || APT_GET_PATH;63
if (!Number.isFinite(this.installTimeoutMs) || this.installTimeoutMs <= 0) {64
throw new Error("The update installation timeout is invalid.");65
}66
}68
async install(filePath) {69
if (this.platform === "linux") return this._installDeb(filePath);70
if (!this.shell?.openPath) throw new UpdateInstallError("System installer integration is unavailable.");71
const error = await this.shell.openPath(filePath);72
if (error) throw new UpdateInstallError(`The installer could not be opened: ${error}`);73
return { launched: true, installed: false, restartApplication: false };74
}76
async _installDeb(filePath) {77
if (!path.isAbsolute(filePath)) {78
throw new UpdateInstallError("Verified Debian update package path must be absolute.", "UPDATE_INSTALL_UNSAFE_PATH");79
}80
const artifactPath = path.resolve(filePath);81
if (path.extname(artifactPath).toLowerCase() !== ".deb") {82
throw new UpdateInstallError("Linux updates must use a Debian package.", "UPDATE_INSTALL_UNSUPPORTED_PACKAGE");83
}84
assertSafeRegularFile(artifactPath, "Verified Debian update package");85
assertTrustedSystemExecutable(this.pkexecPath);86
assertTrustedSystemExecutable(this.aptGetPath);88
// Both executables and all options are fixed by the main process. The only89
// variable argument is the private-cache artifact that was re-hashed and90
// OpenPGP-verified immediately before this method is called.91
const args = [this.aptGetPath, "install", "--reinstall", "--yes", artifactPath];92
this.logger?.info("Starting privileged Debian update installation.", {93
filename: path.basename(artifactPath)94
});96
return new Promise((resolve, reject) => {97
let stdout = "";98
let stderr = "";99
let settled = false;100
let child;101
let timer;102
const finish = (callback) => {103
if (settled) return;104
settled = true;105
clearTimeout(timer);106
callback();107
};109
try {110
child = this.spawnImpl(this.pkexecPath, args, {111
shell: false,112
windowsHide: true,113
stdio: ["ignore", "pipe", "pipe"]114
});115
} catch (error) {116
reject(new UpdateInstallError("The privileged installer could not be started.", "UPDATE_INSTALL_LAUNCH_FAILED", { cause: error }));117
return;118
}120
child.stdout?.on("data", (chunk) => { stdout = appendBounded(stdout, chunk); });121
child.stderr?.on("data", (chunk) => { stderr = appendBounded(stderr, chunk); });122
child.once("error", (error) => finish(() => reject(new UpdateInstallError(123
"The privileged installer could not be started.",124
"UPDATE_INSTALL_LAUNCH_FAILED",125
{ cause: error }126
))));127
child.once("close", (code, signal) => finish(() => {128
if (code === 0) {129
this.logger?.info("Debian update installation completed.", { filename: path.basename(artifactPath) });130
resolve({ launched: true, installed: true, restartApplication: true });131
return;132
}133
const authorizationDenied = code === 126 || code === 127;134
const failure = new UpdateInstallError(135
authorizationDenied136
? "Administrator authorization for the update was canceled or denied."137
: "The Debian update installation failed.",138
authorizationDenied ? "UPDATE_INSTALL_AUTHORIZATION_DENIED" : "UPDATE_INSTALL_FAILED"139
);140
this.logger?.warn("Debian update installation failed.", {141
code,142
signal: signal || null,143
output: `${stdout}\n${stderr}`.trim().slice(-4096)144
});145
reject(failure);146
}));148
timer = setTimeout(() => {149
try { child.kill("SIGTERM"); } catch {}150
finish(() => reject(new UpdateInstallError(151
"The Debian update installation timed out.",152
"UPDATE_INSTALL_TIMEOUT"153
)));154
}, this.installTimeoutMs);155
timer.unref?.();156
});157
}158
}160
module.exports = {161
APT_GET_PATH,162
PKEXEC_PATH,163
UpdateInstallError,164
UpdateInstallerLauncher,165
assertSafeRegularFile166
};SHA-256: d04c292544819be1adefdcafd926bd62dd0c09b3df8a3d884e43a313c5be68b6
SHA-256 archivu: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0