mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-24 07:46:39 +00:00
chore: Remove 'umount' dependency (as advised by "npm audit")
Address security advisory https://www.npmjs.com/advisories/1512 Change-type: patch
This commit is contained in:
parent
c8f5542c8a
commit
f914fa2d8a
@ -68,7 +68,7 @@ export default class ConfigInjectCmd extends Command {
|
||||
ConfigInjectCmd,
|
||||
);
|
||||
|
||||
const { safeUmount } = await import('../../utils/helpers');
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
|
||||
const drive =
|
||||
options.drive || (await getVisuals().drive('Select the device/OS drive'));
|
||||
|
@ -54,7 +54,7 @@ export default class ConfigReadCmd extends Command {
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReadCmd);
|
||||
|
||||
const { safeUmount } = await import('../../utils/helpers');
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
|
||||
const drive =
|
||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||
|
@ -58,7 +58,7 @@ export default class ConfigReconfigureCmd extends Command {
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReconfigureCmd);
|
||||
|
||||
const { safeUmount } = await import('../../utils/helpers');
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
|
||||
const drive =
|
||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||
|
@ -75,7 +75,7 @@ export default class ConfigWriteCmd extends Command {
|
||||
ConfigWriteCmd,
|
||||
);
|
||||
|
||||
const { safeUmount } = await import('../../utils/helpers');
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
|
||||
const drive =
|
||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||
|
@ -63,7 +63,7 @@ export default class LocalConfigureCmd extends Command {
|
||||
const path = await import('path');
|
||||
const reconfix = await import('reconfix');
|
||||
const denymount = promisify(await import('denymount'));
|
||||
const { safeUmount } = await import('../../utils/helpers');
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
const Logger = await import('../../utils/logger');
|
||||
|
||||
const logger = Logger.getLogger();
|
||||
|
@ -74,9 +74,7 @@ export default class OsInitializeCmd extends Command {
|
||||
OsInitializeCmd,
|
||||
);
|
||||
|
||||
const { getManifest, safeUmount, sudo } = await import(
|
||||
'../../utils/helpers'
|
||||
);
|
||||
const { getManifest, sudo } = await import('../../utils/helpers');
|
||||
|
||||
console.info(`Initializing device ${INIT_WARNING_MESSAGE}`);
|
||||
|
||||
@ -96,6 +94,7 @@ export default class OsInitializeCmd extends Command {
|
||||
`Going to erase ${answers.drive}.`,
|
||||
true,
|
||||
);
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
await safeUmount(answers.drive);
|
||||
}
|
||||
|
||||
@ -108,6 +107,7 @@ export default class OsInitializeCmd extends Command {
|
||||
]);
|
||||
|
||||
if (answers.drive != null) {
|
||||
const { safeUmount } = await import('../../utils/umount');
|
||||
await safeUmount(answers.drive);
|
||||
console.info(`You can safely remove ${answers.drive} now`);
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ export default class SshCmd extends Command {
|
||||
}
|
||||
|
||||
// Remote connection
|
||||
const { getProxyConfig, which } = await import('../utils/helpers');
|
||||
const { getProxyConfig } = await import('../utils/helpers');
|
||||
const { getOnlineTargetDeviceUuid } = await import('../utils/patterns');
|
||||
const sdk = getBalenaSdk();
|
||||
|
||||
@ -156,6 +156,7 @@ export default class SshCmd extends Command {
|
||||
|
||||
const deviceId = device.id;
|
||||
const supervisorVersion = device.supervisor_version;
|
||||
const { which } = await import('../utils/which');
|
||||
|
||||
const [whichProxytunnel, username, proxyUrl] = await Promise.all([
|
||||
useProxy ? which('proxytunnel', false) : undefined,
|
||||
@ -301,7 +302,7 @@ export default class SshCmd extends Command {
|
||||
// container
|
||||
const childProcess = await import('child_process');
|
||||
const { escapeRegExp } = await import('lodash');
|
||||
const { which } = await import('../utils/helpers');
|
||||
const { which } = await import('../utils/which');
|
||||
const { deviceContainerEngineBinary } = await import(
|
||||
'../utils/device/ssh'
|
||||
);
|
||||
|
@ -42,6 +42,7 @@ import {
|
||||
import type { DeviceInfo } from './device/api';
|
||||
import { getBalenaSdk, getChalk, stripIndent } from './lazy';
|
||||
import Logger = require('./logger');
|
||||
import { exists } from './which';
|
||||
|
||||
/**
|
||||
* Given an array representing the raw `--release-tag` flag of the deploy and
|
||||
@ -98,15 +99,6 @@ export async function applyReleaseTagKeysAndValues(
|
||||
);
|
||||
}
|
||||
|
||||
const exists = async (filename: string) => {
|
||||
try {
|
||||
await fs.access(filename);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const LOG_LENGTH_MAX = 512 * 1024; // 512KB
|
||||
const compositionFileNames = ['docker-compose.yml', 'docker-compose.yaml'];
|
||||
const hr =
|
||||
|
@ -405,90 +405,6 @@ function windowsCmdExeEscapeArg(arg: string): string {
|
||||
return `"${arg.replace(/["]/g, '""')}"`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handling wrapper around the npm `which` package:
|
||||
* "Like the unix which utility. Finds the first instance of a specified
|
||||
* executable in the PATH environment variable. Does not cache the results,
|
||||
* so hash -r is not needed when the PATH changes."
|
||||
*
|
||||
* @param program Basename of a program, for example 'ssh'
|
||||
* @param rejectOnMissing If the program cannot be found, reject the promise
|
||||
* with an ExpectedError instead of fulfilling it with an empty string.
|
||||
* @returns The program's full path, e.g. 'C:\WINDOWS\System32\OpenSSH\ssh.EXE'
|
||||
*/
|
||||
export async function which(
|
||||
program: string,
|
||||
rejectOnMissing = true,
|
||||
): Promise<string> {
|
||||
const whichMod = await import('which');
|
||||
let programPath: string;
|
||||
try {
|
||||
programPath = await whichMod(program);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
if (rejectOnMissing) {
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(
|
||||
`'${program}' program not found. Is it installed?`,
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call which(programName) and spawn() with the given arguments.
|
||||
*
|
||||
* If returnExitCodeOrSignal is true, the returned promise will resolve to
|
||||
* an array [code, signal] with the child process exit code number or exit
|
||||
* signal string respectively (as provided by the spawn close event).
|
||||
*
|
||||
* If returnExitCodeOrSignal is false, the returned promise will reject with
|
||||
* a custom error if the child process returns a non-zero exit code or a
|
||||
* non-empty signal string (as reported by the spawn close event).
|
||||
*
|
||||
* In either case and if spawn itself emits an error event or fails synchronously,
|
||||
* the returned promise will reject with a custom error that includes the error
|
||||
* message of spawn's error.
|
||||
*/
|
||||
export async function whichSpawn(
|
||||
programName: string,
|
||||
args: string[],
|
||||
options: import('child_process').SpawnOptions = { stdio: 'inherit' },
|
||||
returnExitCodeOrSignal = false,
|
||||
): Promise<[number | undefined, string | undefined]> {
|
||||
const { spawn } = await import('child_process');
|
||||
const program = await which(programName);
|
||||
if (process.env.DEBUG) {
|
||||
console.error(`[debug] [${program}, ${args.join(', ')}]`);
|
||||
}
|
||||
let error: Error | undefined;
|
||||
let exitCode: number | undefined;
|
||||
let exitSignal: string | undefined;
|
||||
try {
|
||||
[exitCode, exitSignal] = await new Promise((resolve, reject) => {
|
||||
spawn(program, args, options)
|
||||
.on('error', reject)
|
||||
.on('close', (code, signal) => resolve([code, signal]));
|
||||
});
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
if (error || (!returnExitCodeOrSignal && (exitCode || exitSignal))) {
|
||||
const msg = [
|
||||
`${programName} failed with exit code=${exitCode} signal=${exitSignal}:`,
|
||||
`[${program}, ${args.join(', ')}]`,
|
||||
...(error ? [`${error}`] : []),
|
||||
];
|
||||
throw new Error(msg.join('\n'));
|
||||
}
|
||||
return [exitCode, exitSignal];
|
||||
}
|
||||
|
||||
export interface ProxyConfig {
|
||||
host: string;
|
||||
port: string;
|
||||
@ -614,16 +530,3 @@ export async function awaitInterruptibleTask<
|
||||
process.removeListener('SIGINT', sigintHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if `drive` is mounted, and if so umount it. No-op on Windows. */
|
||||
export async function safeUmount(drive: string) {
|
||||
if (!drive) {
|
||||
return;
|
||||
}
|
||||
const { isMounted, umount } = await import('umount');
|
||||
const isMountedAsync = promisify(isMounted);
|
||||
if (await isMountedAsync(drive)) {
|
||||
const umountAsync = promisify(umount);
|
||||
await umountAsync(drive);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export async function exec(
|
||||
cmd: string,
|
||||
stdout?: NodeJS.WritableStream,
|
||||
): Promise<void> {
|
||||
const { which } = await import('./helpers');
|
||||
const { which } = await import('./which');
|
||||
const program = await which('ssh');
|
||||
const args = [
|
||||
'-n',
|
||||
@ -132,7 +132,7 @@ export async function spawnSshAndThrowOnError(
|
||||
args: string[],
|
||||
options?: import('child_process').SpawnOptions,
|
||||
) {
|
||||
const { whichSpawn } = await import('./helpers');
|
||||
const { whichSpawn } = await import('./which');
|
||||
const [exitCode, exitSignal] = await whichSpawn(
|
||||
'ssh',
|
||||
args,
|
||||
|
122
lib/utils/umount.ts
Normal file
122
lib/utils/umount.ts
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This module was inspired by the npm `umount` package:
|
||||
* https://www.npmjs.com/package/umount
|
||||
* With some important changes:
|
||||
* - Fix "Command Injection" security advisory 1512
|
||||
* https://www.npmjs.com/advisories/1512
|
||||
* - Port from CoffeeScript to TypeScript
|
||||
* - Convert callbacks to async/await
|
||||
*/
|
||||
|
||||
import { promisify } from 'util';
|
||||
import * as child_process from 'child_process';
|
||||
|
||||
const execFile = promisify(child_process.execFile);
|
||||
|
||||
/**
|
||||
* Unmount a device on Linux or macOS. No-op on Windows.
|
||||
* @param device Device path, e.g. '/dev/disk2'
|
||||
*/
|
||||
export async function umount(device: string): Promise<void> {
|
||||
if (process.platform === 'win32') {
|
||||
return;
|
||||
}
|
||||
const { sanitizePath, whichBin } = await import('./which');
|
||||
// sanitize user's input (regular expression attacks ?)
|
||||
device = sanitizePath(device);
|
||||
const cmd: string[] = [];
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
cmd.push('/usr/sbin/diskutil', 'unmountDisk', 'force', device);
|
||||
} else {
|
||||
// Linux
|
||||
const glob = promisify(await import('glob'));
|
||||
// '?*' expands a base device path like '/dev/sdb' to an array of paths
|
||||
// like '/dev/sdb1', '/dev/sdb2', ..., '/dev/sdb11', ... (partitions)
|
||||
// that exist for balenaOS images and are needed as arguments to 'umount'
|
||||
// on Linux (otherwise, umount produces an error "/dev/sdb: not mounted")
|
||||
const devices = await glob(`${device}?*`, { nodir: true, nonull: true });
|
||||
cmd.push(await whichBin('umount'), ...devices);
|
||||
}
|
||||
if (cmd.length > 1) {
|
||||
let stderr = '';
|
||||
try {
|
||||
const proc = await execFile(cmd[0], cmd.slice(1));
|
||||
stderr = proc.stderr;
|
||||
} catch (err) {
|
||||
const msg = [
|
||||
'',
|
||||
`Error executing "${cmd.join(' ')}"`,
|
||||
stderr || '',
|
||||
err.message || '',
|
||||
];
|
||||
if (process.platform === 'linux') {
|
||||
// ignore errors like: "umount: /dev/sdb4: not mounted."
|
||||
if (process.env.DEBUG) {
|
||||
console.error(msg.join('\n[debug] '));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(msg.join('\n'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a device is mounted on Linux or macOS. Always true on Windows.
|
||||
* @param device Device path, e.g. '/dev/disk2'
|
||||
*/
|
||||
export async function isMounted(device: string): Promise<boolean> {
|
||||
if (process.platform === 'win32') {
|
||||
return true;
|
||||
}
|
||||
if (!device) {
|
||||
return false;
|
||||
}
|
||||
const { whichBin } = await import('./which');
|
||||
const mountCmd = await whichBin('mount');
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
try {
|
||||
const proc = await execFile(mountCmd);
|
||||
stdout = proc.stdout;
|
||||
stderr = proc.stderr;
|
||||
} catch (err) {
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(
|
||||
`Error executing "${mountCmd}":\n${stderr}\n${err.message}`,
|
||||
);
|
||||
}
|
||||
const result = (stdout || '')
|
||||
.split('\n')
|
||||
.some((line) => line.startsWith(device));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Check if `drive` is mounted and, if so, umount it. No-op on Windows. */
|
||||
export async function safeUmount(drive: string) {
|
||||
if (!drive) {
|
||||
return;
|
||||
}
|
||||
if (await isMounted(drive)) {
|
||||
await umount(drive);
|
||||
}
|
||||
}
|
146
lib/utils/which.ts
Normal file
146
lib/utils/which.ts
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { promises as fs, constants } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export const { F_OK, R_OK, W_OK, X_OK } = constants;
|
||||
|
||||
export async function exists(filename: string, mode = F_OK) {
|
||||
try {
|
||||
await fs.access(filename, mode);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace sequences of untowardly characters like /[<>:"/\\|?*\u0000-\u001F]/g
|
||||
* and '.' or '..' with an underscore, plus other rules enforced by the filenamify
|
||||
* package. See https://github.com/sindresorhus/filenamify/
|
||||
*/
|
||||
export function sanitizePath(filepath: string) {
|
||||
const filenamify = require('filenamify') as typeof import('filenamify');
|
||||
// normalize also converts forward slash to backslash on Windows
|
||||
return path
|
||||
.normalize(filepath)
|
||||
.split(path.sep)
|
||||
.map((f) => filenamify(f, { replacement: '_' }))
|
||||
.join(path.sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a program name like 'mount', search for it in a pre-defined set of
|
||||
* folders ('/usr/bin', '/bin', '/usr/sbin', '/sbin') and return the full path if found.
|
||||
*
|
||||
* For executables, in some scenarios, this can be more secure than allowing
|
||||
* any folder in the PATH. Only relevant on Linux or macOS.
|
||||
*/
|
||||
export async function whichBin(programName: string): Promise<string> {
|
||||
for (const dir of ['/usr/bin', '/bin', '/usr/sbin', '/sbin']) {
|
||||
const candidate = path.join(dir, programName);
|
||||
if (await exists(candidate, X_OK)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handling wrapper around the npm `which` package:
|
||||
* "Like the unix which utility. Finds the first instance of a specified
|
||||
* executable in the PATH environment variable. Does not cache the results,
|
||||
* so hash -r is not needed when the PATH changes."
|
||||
*
|
||||
* @param program Basename of a program, for example 'ssh'
|
||||
* @param rejectOnMissing If the program cannot be found, reject the promise
|
||||
* with an ExpectedError instead of fulfilling it with an empty string.
|
||||
* @returns The program's full path, e.g. 'C:\WINDOWS\System32\OpenSSH\ssh.EXE'
|
||||
*/
|
||||
export async function which(
|
||||
program: string,
|
||||
rejectOnMissing = true,
|
||||
): Promise<string> {
|
||||
const whichMod = await import('which');
|
||||
let programPath: string;
|
||||
try {
|
||||
programPath = await whichMod(program);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
if (rejectOnMissing) {
|
||||
const { ExpectedError } = await import('../errors');
|
||||
throw new ExpectedError(
|
||||
`'${program}' program not found. Is it installed?`,
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call which(programName) and spawn() with the given arguments.
|
||||
*
|
||||
* If returnExitCodeOrSignal is true, the returned promise will resolve to
|
||||
* an array [code, signal] with the child process exit code number or exit
|
||||
* signal string respectively (as provided by the spawn close event).
|
||||
*
|
||||
* If returnExitCodeOrSignal is false, the returned promise will reject with
|
||||
* a custom error if the child process returns a non-zero exit code or a
|
||||
* non-empty signal string (as reported by the spawn close event).
|
||||
*
|
||||
* In either case and if spawn itself emits an error event or fails synchronously,
|
||||
* the returned promise will reject with a custom error that includes the error
|
||||
* message of spawn's error.
|
||||
*/
|
||||
export async function whichSpawn(
|
||||
programName: string,
|
||||
args: string[],
|
||||
options: import('child_process').SpawnOptions = { stdio: 'inherit' },
|
||||
returnExitCodeOrSignal = false,
|
||||
): Promise<[number | undefined, string | undefined]> {
|
||||
const { spawn } = await import('child_process');
|
||||
const program = await which(programName);
|
||||
if (process.env.DEBUG) {
|
||||
console.error(`[debug] [${program}, ${args.join(', ')}]`);
|
||||
}
|
||||
let error: Error | undefined;
|
||||
let exitCode: number | undefined;
|
||||
let exitSignal: string | undefined;
|
||||
try {
|
||||
[exitCode, exitSignal] = await new Promise((resolve, reject) => {
|
||||
spawn(program, args, options)
|
||||
.on('error', reject)
|
||||
.on('close', (code, signal) => resolve([code, signal]));
|
||||
});
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
if (error || (!returnExitCodeOrSignal && (exitCode || exitSignal))) {
|
||||
const msg = [
|
||||
`${programName} failed with exit code=${exitCode} signal=${exitSignal}:`,
|
||||
`[${program}, ${args.join(', ')}]`,
|
||||
...(error ? [`${error}`] : []),
|
||||
];
|
||||
throw new Error(msg.join('\n'));
|
||||
}
|
||||
return [exitCode, exitSignal];
|
||||
}
|
73
npm-shrinkwrap.json
generated
73
npm-shrinkwrap.json
generated
@ -1269,20 +1269,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.16.tgz",
|
||||
"integrity": "sha512-6CLxw83vQf6DKqXxMPwl8qpF8I7THFZuIwLt4TnNsumxkp1VsRZWT8txQxncT/Rl2UojTsFzWgDG4FRMwafrlA==",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -7823,6 +7809,21 @@
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"filename-reserved-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik="
|
||||
},
|
||||
"filenamify": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
|
||||
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
|
||||
"requires": {
|
||||
"filename-reserved-regex": "^2.0.0",
|
||||
"strip-outer": "^1.0.1",
|
||||
"trim-repeated": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@ -8491,9 +8492,9 @@
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@ -11972,6 +11973,20 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@ -17031,6 +17046,14 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"strip-outer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
|
||||
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"struct-fu": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/struct-fu/-/struct-fu-1.2.1.tgz",
|
||||
@ -17599,6 +17622,14 @@
|
||||
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
|
||||
"integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
|
||||
},
|
||||
"trim-repeated": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz",
|
||||
@ -17847,14 +17878,6 @@
|
||||
"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=",
|
||||
"dev": true
|
||||
},
|
||||
"umount": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/umount/-/umount-1.1.6.tgz",
|
||||
"integrity": "sha1-p0kPu9pIunalAKL0vgDV5mAnzQA=",
|
||||
"requires": {
|
||||
"lodash": "~4.17.4"
|
||||
}
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
|
||||
|
@ -231,7 +231,9 @@
|
||||
"fast-boot2": "^1.1.0",
|
||||
"fast-levenshtein": "^3.0.0",
|
||||
"file-disk": "^8.0.1",
|
||||
"filenamify": "^4.3.0",
|
||||
"get-stdin": "^8.0.0",
|
||||
"glob": "^7.1.7",
|
||||
"global-agent": "^2.1.12",
|
||||
"global-tunnel-ng": "^2.1.1",
|
||||
"humanize": "0.0.9",
|
||||
@ -276,7 +278,6 @@
|
||||
"through2": "^2.0.3",
|
||||
"tmp": "^0.2.1",
|
||||
"typed-error": "^3.2.1",
|
||||
"umount": "^1.1.6",
|
||||
"update-notifier": "^4.1.0",
|
||||
"which": "^2.0.2",
|
||||
"window-size": "^1.1.0"
|
||||
|
@ -37,7 +37,7 @@ describe('balena ssh', function () {
|
||||
if (hasSshExecutable) {
|
||||
[sshServer, sshServerPort] = await startMockSshServer();
|
||||
}
|
||||
const modPath = '../../build/utils/helpers';
|
||||
const modPath = '../../build/utils/which';
|
||||
const mod = await import(modPath);
|
||||
mock(modPath, {
|
||||
...mod,
|
||||
@ -130,7 +130,7 @@ describe('balena ssh', function () {
|
||||
|
||||
/** Check whether the 'ssh' tool (executable) exists in the PATH */
|
||||
async function checkSsh(): Promise<boolean> {
|
||||
const { which } = await import('../../build/utils/helpers');
|
||||
const { which } = await import('../../build/utils/which');
|
||||
const sshPath = await which('ssh', false);
|
||||
if ((sshPath || '').includes('\\Windows\\System32\\OpenSSH\\ssh')) {
|
||||
// don't use Windows' built-in ssh tool for these test cases
|
||||
|
27
typings/umount/index.d.ts
vendored
27
typings/umount/index.d.ts
vendored
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare module 'umount' {
|
||||
export const umount: (
|
||||
device: string,
|
||||
callback: (err?: Error, stdout?: any, stderr?: any) => void,
|
||||
) => void;
|
||||
export const isMounted: (
|
||||
device: string,
|
||||
callback: (err: Error | null, isMounted?: boolean) => void,
|
||||
) => void;
|
||||
}
|
Loading…
Reference in New Issue
Block a user