node-command 2.0.13 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/setup.mjs +34 -2
- package/package.json +1 -1
package/lib/setup.mjs
CHANGED
|
@@ -707,6 +707,13 @@ const INCOMPLETE_SYSTEM_HOST_ENV = '/opt/nc/config/nc-client.env';
|
|
|
707
707
|
const INCOMPLETE_SYSTEM_UNIT = '/etc/systemd/system/nc-client.service';
|
|
708
708
|
const INCOMPLETE_SYSTEM_UNIT_DROPIN = '/etc/systemd/system/nc-client.service.d';
|
|
709
709
|
const INCOMPLETE_HELPER_APP = '/opt/nc/libexec/privileged-helper';
|
|
710
|
+
const INCOMPLETE_HELPER_BOOTSTRAP = '/opt/nc/libexec/protected-helper-bootstrap';
|
|
711
|
+
const INCOMPLETE_HELPER_BOOTSTRAP_ENTRIES = new Map([
|
|
712
|
+
['archive.tar.gz', 'file'],
|
|
713
|
+
['install-request.json', 'file'],
|
|
714
|
+
['modules', 'directory'],
|
|
715
|
+
['release', 'directory']
|
|
716
|
+
]);
|
|
710
717
|
const INCOMPLETE_HELPER_DAEMON = INCOMPLETE_HELPER_APP + '/privileged-helper-linux-daemon.mjs';
|
|
711
718
|
const INCOMPLETE_HELPER_PROFILE = INCOMPLETE_HELPER_APP + '/privileged-helper-profile.json';
|
|
712
719
|
const INCOMPLETE_HELPER_UNIT = '/etc/systemd/system/nc-privileged-helper.service';
|
|
@@ -823,12 +830,37 @@ function systemValidateHelperArtifacts(io, runtime) {
|
|
|
823
830
|
return { service: !!service, tmpfiles: !!tmpfiles };
|
|
824
831
|
}
|
|
825
832
|
|
|
833
|
+
/**
|
|
834
|
+
* `libexec` is removable only while empty or holding the exact staging tree a
|
|
835
|
+
* protected helper bootstrap writes before provisioning: root-owned regular
|
|
836
|
+
* files/directories, no symlinks, no group/other write bits, and no unknown
|
|
837
|
+
* entry. A provisioned helper, peercred shim or any other content fails closed.
|
|
838
|
+
*/
|
|
839
|
+
function systemValidateStagedHelperBootstrap(io, libexec) {
|
|
840
|
+
const entries = systemReaddir(io)(libexec);
|
|
841
|
+
if (entries.length === 0) return true;
|
|
842
|
+
if (entries.length !== 1 || entries[0] !== 'protected-helper-bootstrap') return false;
|
|
843
|
+
const stage = systemLstatOrNull(io, INCOMPLETE_HELPER_BOOTSTRAP);
|
|
844
|
+
if (!stage || stage.isSymbolicLink() || !stage.isDirectory() || stage.uid !== 0 || (stage.mode & 0o022)) return false;
|
|
845
|
+
const staged = systemReaddir(io)(INCOMPLETE_HELPER_BOOTSTRAP);
|
|
846
|
+
if (staged.length !== INCOMPLETE_HELPER_BOOTSTRAP_ENTRIES.size) return false;
|
|
847
|
+
for (const [name, type] of INCOMPLETE_HELPER_BOOTSTRAP_ENTRIES) {
|
|
848
|
+
if (!staged.includes(name)) return false;
|
|
849
|
+
const info = systemLstatOrNull(io, path.join(INCOMPLETE_HELPER_BOOTSTRAP, name));
|
|
850
|
+
if (!info || info.isSymbolicLink() || info.uid !== 0 || (info.mode & 0o022)) return false;
|
|
851
|
+
if (type === 'directory' ? !info.isDirectory() : (!info.isFile() || info.nlink !== 1)) return false;
|
|
852
|
+
}
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
|
|
826
856
|
/**
|
|
827
857
|
* Recognize the two root-recoverable system-install leftovers this CLI may
|
|
828
858
|
* remove without the packaged maintenance entry: a helper provisioned but
|
|
829
859
|
* never bound (`pending`/`not_initialized`, no profile, no lifecycle custody)
|
|
830
860
|
* and a failed install whose app payload never landed but whose stale unit
|
|
831
|
-
* remains.
|
|
861
|
+
* remains. A failed install may also leave the exact root-owned protected
|
|
862
|
+
* helper bootstrap staged but never provisioned. Anything else returns null so
|
|
863
|
+
* callers keep failing closed.
|
|
832
864
|
* @param {string} root
|
|
833
865
|
* @param {{ platform?: string, io?: typeof fs }} [options]
|
|
834
866
|
* @returns {null | { kind: 'pending-helper'|'failed-install', runtime: string, payloadUid: number, helperArtifacts: { service: boolean, tmpfiles: boolean } }}
|
|
@@ -900,7 +932,7 @@ export function incompleteSystemInstallationShape(root, { platform = os.platform
|
|
|
900
932
|
if (systemLstatOrNull(io, INCOMPLETE_SYSTEM_LIFECYCLE) || systemLstatOrNull(io, INCOMPLETE_SYSTEM_PAIRED_ROOT)) return null;
|
|
901
933
|
const libexec = systemLstatOrNull(io, path.join(root, 'libexec'));
|
|
902
934
|
if (libexec) {
|
|
903
|
-
if (libexec.isSymbolicLink() || !libexec.isDirectory() ||
|
|
935
|
+
if (libexec.isSymbolicLink() || !libexec.isDirectory() || !systemValidateStagedHelperBootstrap(io, path.join(root, 'libexec'))) return null;
|
|
904
936
|
}
|
|
905
937
|
const launcher = systemLstatOrNull(io, INCOMPLETE_SYSTEM_LAUNCHER);
|
|
906
938
|
if (launcher && (launcher.isSymbolicLink() || !launcher.isFile() || launcher.size !== 0 ||
|