Merge branch 'feature/ssh-tunnel-test'

This commit is contained in:
SPRINX0\prochazka
2025-01-06 11:57:59 +01:00
26 changed files with 330 additions and 71 deletions

View File

@@ -0,0 +1,38 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Build test containers
'on':
push:
branches:
- master
- develop
- feature/**
paths:
- e2e-tests/containers/**
jobs:
build-docker-ssh:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v3
- name: Build mysql-ssh-login image
run: |
docker build -t dbgate/mysql-ssh-login:latest e2e-tests/containers/mysql-ssh-login
- name: Build mysql-ssh-keyfile image
run: |
docker build -t dbgate/mysql-ssh-keyfile:latest e2e-tests/containers/mysql-ssh-keyfile
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: '${{ github.actor }}'
password: '${{ secrets.GITHUB_TOKEN }}'
- name: Push mysql-ssh-login to GHCR
run: |
docker tag dbgate/mysql-ssh-login:latest ghcr.io/dbgate/mysql-ssh-login:latest
docker push ghcr.io/dbgate/mysql-ssh-login:latest
- name: Push mysql-ssh-keyfile to GHCR
run: |
docker tag dbgate/mysql-ssh-keyfile:latest ghcr.io/dbgate/mysql-ssh-keyfile:latest
docker push ghcr.io/dbgate/mysql-ssh-keyfile:latest

View File

@@ -9,7 +9,7 @@ name: Run tests
- develop
- feature/**
jobs:
test-runner:
test:
runs-on: ubuntu-latest
container: 'node:18'
steps:
@@ -17,7 +17,7 @@ jobs:
run: |
apt-get update
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: yarn install
@@ -90,3 +90,7 @@ jobs:
image: 'gvenzl/oracle-xe:21-slim'
env:
ORACLE_PASSWORD: Pwd2020Db
mysql-ssh-login:
image: 'ghcr.io/dbgate/mysql-ssh-login:latest'
mysql-ssh-keyfile:
image: 'ghcr.io/dbgate/mysql-ssh-keyfile:latest'

View File

@@ -0,0 +1,27 @@
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server mysql-server && \
apt-get clean && rm -rf /var/lib/apt/lists/*
RUN mkdir /var/run/sshd
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin without-password/' /etc/ssh/sshd_config
RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh
COPY mykey.pub /root/.ssh/authorized_keys
RUN chmod 600 /root/.ssh/authorized_keys
RUN service mysql start && \
mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; FLUSH PRIVILEGES;" && \
service mysql stop
EXPOSE 22
COPY start.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQClN/tu+X7PVYpMBEQDwxtyG0oNuKqcJdBud/vBUalyYSpliXO30BsBusjCyAh8ENVllmGTRaTcqAUPg/vwekaAMFZu9EXmQsdhPEWSxrkPkXXKsWrZVPtRV8l3wuSy/VLxbXRLujTpIEE5Au88e2CFN3zzuhg9rvsXE0cGjyYe9Cw3Ub+Yo6rbnESI7F+Jun29/wD8RF+kRL5cr7BrDoOBQf4yGexrTbUPMZ4b8QlGfXUwZhn6LlWVoWq7BMRelnfIAkuC+LwavvnkMqXMITmGa7sG0zOXByW7iGdP6TWVq9Ks+yCZoAA9ncAkgkpwoNrOSXR6dM5UjrsfJ2Na332QfxK8MmC+nBiyXIKUaQY2G8Wfm93GQqDc+349JFwH4ZjgYWJlTd/UfRCn4j30jxe1RKnjt5xGqb3L+j8JfuSPcojWRekg/K8GxBmaMCcEdqjIFD4MxeIUW3bGHd30wnuZkTPWlSmAGrO0fZXj0s1fmOBME05TjE8jsyKPmb2Kd3JdtCuTrjOgrYiHaMfcv/VqVhIv2kCKjnvhAxCqfGfKxxgcerItbrsfFAvYak1kwZelBquHOK6SkMH5WhDyhvGY+ZF0Wbg3CpynlpJVu4E3LSglYNbkIWqS3pAf+hqJtXl9SHCLgKFUcCIz0ZNQkT+PtUlR3klp6qoPXUWrcL3pdQ== test

View File

@@ -14,9 +14,10 @@ RUN service mysql start && \
mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; FLUSH PRIVILEGES;" && \
service mysql stop
EXPOSE 22 3306
EXPOSE 22
COPY start.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]
CMD ["/start.sh"]

View File

@@ -0,0 +1,7 @@
#!/bin/bash
service ssh start
service mysql start
tail -f /dev/null

View File

@@ -1,3 +1,5 @@
const path = require('path');
describe('Initialization', () => {
it('successfully loads', () => {
cy.visit('http://localhost:3000');
@@ -23,45 +25,71 @@ describe('Initialization', () => {
cy.contains('performance_schema');
});
// it('SSH connection', () => {
// const runOnCI = Cypress.env('runOnCI');
it('SSH connection - user + password', () => {
const runOnCI = Cypress.env('runOnCI');
// cy.get('body')
// .trigger('keydown', {
// key: 'F1',
// code: 'F1',
// which: 112,
// keyCode: 112,
// bubbles: true,
// })
// .trigger('keyup', {
// key: 'F1',
// code: 'F1',
// which: 112,
// keyCode: 112,
// bubbles: true,
// });
// cy.get('body').type('Close all');
// cy.get('body').type('{enter}');
cy.visit('http://localhost:3000');
cy.contains('Connections');
// cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
// cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
// cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
// cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('root');
cy.realPress('F1');
cy.realType('Close all');
cy.realPress('Enter');
// cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').clear().type('root');
// cy.get('[data-testid=ConnectionSshTunnelFields_sshPassword]').clear().type('root');
// if (runOnCI) {
// cy.get('[data-testid=ConnectionSshTunnelFields_sshHost]').clear().type('mysql-ssh');
// } else {
// cy.get('[data-testid=ConnectionSshTunnelFields_sshPort]').clear().type('16006');
// }
// cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-ssh-1');
// cy.get('[data-testid=ConnectionTab_buttonSave]').click();
// cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
// cy.contains('performance_schema');
// });
cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-ssh-1');
cy.get('[data-testid=ConnectionTab_tabSshTunnel]').click();
cy.get('[data-testid=ConnectionSshTunnelFields_useSshTunnel]').check();
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').clear().type('root');
cy.get('[data-testid=ConnectionSshTunnelFields_sshPassword]').clear().type('root');
if (runOnCI) {
cy.get('[data-testid=ConnectionSshTunnelFields_sshHost]').clear().type('mysql-ssh-login');
} else {
cy.get('[data-testid=ConnectionSshTunnelFields_sshPort]').clear().type('16006');
}
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
it('SSH connection - keyfile', () => {
const runOnCI = Cypress.env('runOnCI');
cy.visit('http://localhost:3000');
cy.contains('Connections');
cy.realPress('F1');
cy.realType('Close all');
cy.realPress('Enter');
cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-ssh-2');
cy.get('[data-testid=ConnectionTab_tabSshTunnel]').click();
cy.get('[data-testid=ConnectionSshTunnelFields_useSshTunnel]').check();
cy.get('[data-testid=ConnectionSshTunnelFields_sshMode]').select('Key file');
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').clear()
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').type('root');
cy.get('[data-testid=ConnectionSshTunnelFields_sshKeyfile]')
.clear()
.type(path.join(Cypress.config('fileServerFolder'), 'cypress', 'e2e', 'mykey'));
if (runOnCI) {
cy.get('[data-testid=ConnectionSshTunnelFields_sshHost]').clear().type('mysql-ssh-keyfile');
} else {
cy.get('[data-testid=ConnectionSshTunnelFields_sshPort]').clear().type('16008');
}
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
// it('import chinook DB', () => {
// cy.visit('http://localhost:3000');

View File

@@ -0,0 +1,49 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
jIy5WYf/zrVHAAAABHRlc3QBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----

View File

@@ -17,4 +17,6 @@
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
// require('./commands')
import "cypress-real-events";

View File

@@ -17,9 +17,16 @@ services:
environment:
- MYSQL_ROOT_PASSWORD=Pwd2020Db
mysql-ssh:
build: mysql-ssh
mysql-ssh-login:
build: containers/mysql-ssh-login
restart: always
ports:
- 16005:3306
- 16006:22
mysql-ssh-keyfile:
build: containers/mysql-ssh-keyfile
restart: always
ports:
- 16007:3306
- 16008:22

View File

@@ -7,6 +7,7 @@
"axios": "^1.7.9",
"cross-env": "^7.0.3",
"cypress": "^13.16.1",
"cypress-real-events": "^1.13.0",
"kill-port": "^2.0.1",
"start-server-and-test": "^2.0.8"
},

View File

@@ -350,6 +350,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
cypress-real-events@^1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.13.0.tgz#6b7cd32dcac172db1493608f97a2576c7d0bd5af"
integrity sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==
cypress@^13.16.1:
version "13.16.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.16.1.tgz#82e776f6ad2037ccce6b6feabed768615c476258"

View File

@@ -11,14 +11,11 @@
"scripts": {
"wait:local": "cross-env DEVMODE=1 LOCALTEST=1 node wait.js",
"wait:ci": "cross-env DEVMODE=1 CITEST=1 node wait.js",
"test:local": "cross-env DEVMODE=1 LOCALTEST=1 jest",
"test:local": "cross-env DEVMODE=1 LOCALTEST=1 jest --testTimeout=5000",
"test:local:path": "cross-env DEVMODE=1 LOCALTEST=1 jest --runTestsByPath __tests__/data-duplicator.spec.js",
"test:ci": "cross-env DEVMODE=1 CITEST=1 jest --runInBand --json --outputFile=result.json --testLocationInResults --detectOpenHandles --forceExit",
"test:ci": "cross-env DEVMODE=1 CITEST=1 jest --runInBand --json --outputFile=result.json --testLocationInResults --detectOpenHandles --forceExit --testTimeout=10000",
"run:local": "docker-compose down && docker-compose up -d && yarn wait:local && yarn test:local"
},
"jest": {
"testTimeout": 5000
},
"devDependencies": {
"cross-env": "^7.0.3",
"jest": "^27.0.1",

View File

@@ -60,7 +60,7 @@
"rimraf": "^3.0.0",
"semver": "^7.6.3",
"simple-encryptor": "^4.0.0",
"ssh2": "^1.11.0",
"ssh2": "^1.16.0",
"stream-json": "^1.8.0",
"tar": "^6.0.5"
},

View File

@@ -166,7 +166,9 @@ function start() {
if (time - lastPing > 40 * 1000) {
logger.info('Server connection not alive, exiting');
const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan);
if (dbhan) {
await driver.close(dbhan);
}
process.exit(0);
}
}, 10 * 1000);

View File

@@ -7,7 +7,7 @@ const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-
const logger = getLogger('sshProcess');
async function getSshConnection(connection) {
async function getSshConnection(connection, tunnelConfig) {
const sshConfig = {
endHost: connection.sshHost || '',
endPort: connection.sshPort || 22,
@@ -23,6 +23,7 @@ async function getSshConnection(connection) {
: undefined,
skipAutoPrivateKey: true,
noReadline: true,
bindHost: tunnelConfig.fromHost,
};
const sshConn = new SSHConnection(sshConfig);
@@ -31,7 +32,7 @@ async function getSshConnection(connection) {
async function handleStart({ connection, tunnelConfig }) {
try {
const sshConn = await getSshConnection(connection);
const sshConn = await getSshConnection(connection, tunnelConfig);
await sshConn.forward(tunnelConfig);
process.send({

View File

@@ -130,7 +130,7 @@ class SSHConnection {
const connectionToBastion = await this.connect(bastionHost);
return new Promise((resolve, reject) => {
connectionToBastion.forwardOut(
'127.0.0.1',
this.options.bindHost,
22,
this.options.endHost,
this.options.endPort || 22,
@@ -228,9 +228,9 @@ class SSHConnection {
options.toPort
);
connection.forwardOut(
'localhost',
this.options.bindHost,
options.fromPort,
options.toHost || 'localhost',
options.toHost || this.options.bindHost,
options.toPort,
(error, stream) => {
if (error) {
@@ -241,7 +241,7 @@ class SSHConnection {
}
);
})
.listen(options.fromPort, 'localhost', () => {
.listen(options.fromPort, this.options.bindHost, () => {
return resolve();
});
});

View File

@@ -63,7 +63,7 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
throw new Error(tunnel.message);
}
connection.server = 'localhost';
connection.server = tunnel.localHost;
connection.port = tunnel.localPort;
}

View File

@@ -43,33 +43,44 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
logger.error(extractErrorLogData(err), 'Error connecting SSH');
}
return new Promise((resolve, reject) => {
let promiseHandled = false;
subprocess.on('message', resp => {
// @ts-ignore
const { msgtype, errorMessage } = resp;
if (msgtype == 'connected') {
resolve(subprocess);
promiseHandled = true;
}
if (msgtype == 'error') {
reject(new Error(errorMessage));
promiseHandled = true;
}
});
subprocess.on('exit', code => {
logger.info('SSH forward process exited');
delete sshTunnelCache[tunnelCacheKey];
if (!promiseHandled) {
reject(new Error('SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'));
}
});
});
}
async function getSshTunnel(connection) {
const config = require('../controllers/config');
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
const globalSettings = await config.getSettings();
return await lock.acquire(tunnelCacheKey, async () => {
if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
const localHost = globalSettings?.['connection.sshBindHost'] || '127.0.0.1';
// workaround for `getPortPromise` not releasing the port quickly enough
await new Promise(resolve => setTimeout(resolve, 500));
const tunnelConfig = {
fromPort: localPort,
fromHost: localHost,
toPort: connection.port,
toHost: connection.server,
};
@@ -87,6 +98,7 @@ async function getSshTunnel(connection) {
sshTunnelCache[tunnelCacheKey] = {
state: 'ok',
localPort,
localHost,
subprocess,
};
return sshTunnelCache[tunnelCacheKey];

View File

@@ -7,6 +7,7 @@
slot?: number;
component?: any;
props?: any;
testid?: string;
}
export let tabs: TabDef[];
@@ -27,7 +28,7 @@
<div class="main" class:flex1>
<div class="tabs">
{#each _.compact(tabs) as tab, index}
<div class="tab-item" class:selected={value == index} on:click={() => (value = index)}>
<div class="tab-item" class:selected={value == index} on:click={() => (value = index)} data-testid={tab.testid}>
<span class="ml-2">
{tab.label}
</span>

View File

@@ -202,6 +202,17 @@ ORDER BY
defaultValue="30"
disabled={values['connection.autoRefresh'] === false}
/>
<FormSelectField
label="Local host address for SSH connections"
name="connection.sshBindHost"
isNative
defaultValue="127.0.0.1"
options={[
{ value: '127.0.0.1', label: '127.0.0.1 (IPv4)' },
{ value: '::1', label: '::1 (IPv6)' },
{ value: 'localhost', label: 'localhost (domain name)' },
]}
/>
<div class="heading">Query sessions</div>
<FormCheckboxField

View File

@@ -218,18 +218,22 @@
label: 'General',
component: ConnectionDriverFields,
props: { getDatabaseList, currentConnection },
testid: 'ConnectionTab_tabGeneral',
},
driver?.showConnectionTab('sshTunnel', $values) && {
label: 'SSH Tunnel',
component: ConnectionSshTunnelFields,
testid: 'ConnectionTab_tabSshTunnel',
},
driver?.showConnectionTab('ssl', $values) && {
label: 'SSL',
component: ConnectionSslFields,
testid: 'ConnectionTab_tabSsl',
},
{
label: 'Advanced',
component: ConnectionAdvancedDriverFields,
testid: 'ConnectionTab_tabAdvanced',
},
]}
/>

View File

@@ -0,0 +1,42 @@
name: Build test containers
on:
push:
branches:
- master
- develop
- 'feature/**'
paths:
- e2e-tests/containers/**
jobs:
build-docker-ssh:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v3
- name: Build mysql-ssh-login image
run: |
docker build -t dbgate/mysql-ssh-login:latest e2e-tests/containers/mysql-ssh-login
- name: Build mysql-ssh-keyfile image
run: |
docker build -t dbgate/mysql-ssh-keyfile:latest e2e-tests/containers/mysql-ssh-keyfile
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push mysql-ssh-login to GHCR
run: |
docker tag dbgate/mysql-ssh-login:latest ghcr.io/dbgate/mysql-ssh-login:latest
docker push ghcr.io/dbgate/mysql-ssh-login:latest
- name: Push mysql-ssh-keyfile to GHCR
run: |
docker tag dbgate/mysql-ssh-keyfile:latest ghcr.io/dbgate/mysql-ssh-keyfile:latest
docker push ghcr.io/dbgate/mysql-ssh-keyfile:latest

View File

@@ -1,4 +1,5 @@
name: Run tests
on:
push:
branches:
@@ -7,7 +8,7 @@ on:
- 'feature/**'
jobs:
test-runner:
test:
runs-on: ubuntu-latest
container: node:18
@@ -16,50 +17,60 @@ jobs:
run: |
apt-get update
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: yarn install
run: |
yarn install
- name: Build packer dist for cypress
run: |
yarn prepare:packer
- name: yarn install cypress
run: |
cd e2e-tests
yarn install
- name: Run Cypress tests
run: |
cd e2e-tests
yarn test:ci
- name: Integration tests
run: |
cd integration-tests
yarn test:ci
# yarn wait:ci
- name: Filter parser tests
if: always()
run: |
cd packages/filterparser
yarn test:ci
- name: Datalib (perspective) tests
if: always()
run: |
cd packages/datalib
yarn test:ci
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: integration-tests/result.json
action-name: Integration tests
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
@@ -99,6 +110,9 @@ jobs:
image: gvenzl/oracle-xe:21-slim
env:
ORACLE_PASSWORD: Pwd2020Db
mysql-ssh-login:
image: ghcr.io/dbgate/mysql-ssh-login:latest
# cockroachdb:
# image: cockroachdb/cockroach
mysql-ssh-keyfile:
image: ghcr.io/dbgate/mysql-ssh-keyfile:latest

View File

@@ -4051,7 +4051,7 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
cpu-features@~0.0.9:
cpu-features@~0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5"
integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==
@@ -8741,11 +8741,16 @@ named-placeholders@^1.1.3:
dependencies:
lru-cache "^7.14.1"
nan@^2.12.1, nan@^2.18.0, nan@^2.19.0:
nan@^2.12.1, nan@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0"
integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==
nan@^2.20.0:
version "2.22.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3"
integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -10878,16 +10883,16 @@ ssf@~0.11.2:
dependencies:
frac "~1.1.2"
ssh2@^1.11.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b"
integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==
ssh2@^1.16.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0"
integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==
dependencies:
asn1 "^0.2.6"
bcrypt-pbkdf "^1.0.2"
optionalDependencies:
cpu-features "~0.0.9"
nan "^2.18.0"
cpu-features "~0.0.10"
nan "^2.20.0"
sshpk@^1.7.0:
version "1.18.0"