This commit is contained in:
2026-04-07 14:50:23 +09:00
commit b4e485502b
4778 changed files with 2017091 additions and 0 deletions

View File

@@ -0,0 +1,225 @@
import { TestEnvironmentSetup } from '../util/prepareEnvironment';
import * as common from '../../src/common';
import * as fs from 'fs';
import * as path from 'path';
import * as child_process from 'child_process';
jest.setTimeout(60 * 1000);
describe('Basic acceptance test', () => {
const envSetup = new TestEnvironmentSetup();
const cliVersionForTesting =
'1.1294.0-dev.fb7d0fdb0ab3beb8af7142c84ded754b568ba2f4';
beforeEach(async () => {
process.env.SNYK_DISABLE_ANALYTICS = '1';
await envSetup.prepareEnvironment(cliVersionForTesting);
});
afterEach(() => {
delete process.env.SNYK_DISABLE_ANALYTICS;
envSetup.cleanupDirectories();
});
it('Bootstrap binary & execute a command', () => {
const config = common.getCurrentConfiguration();
const executable = config
.getLocalLocation()
.replace(envSetup.inputfolder, envSetup.outputfolder);
try {
fs.unlinkSync(executable);
} catch {
//
}
expect(fs.existsSync(executable)).toBeFalsy();
const indexScript = path.join(envSetup.outputfolder, 'index.js');
const bootstrapScript = path.join(envSetup.outputfolder, 'bootstrap.js');
// run system under test: bootsrap
const resultBootstrap = child_process.spawnSync(
'node ' + bootstrapScript + ' exec',
{ shell: true },
);
if (resultBootstrap.status != 0) {
console.debug(resultBootstrap.stdout.toString());
console.error(resultBootstrap.stderr.toString());
}
// The binary wrapper should not output anything to stdout
// as it will conflict with stdout from the CLI leading to incidents
expect(resultBootstrap.stdout.toString()).toEqual('');
expect(resultBootstrap.status).toEqual(0);
expect(fs.existsSync(executable)).toBeTruthy();
// run system under test: index
const resultIndex = child_process.spawnSync(
'node ' + indexScript + ' --version',
{ shell: true },
);
if (resultIndex.status != 0) {
console.debug(resultIndex);
}
expect(resultIndex.status).toEqual(0);
expect(
resultIndex.stdout.toString().includes(cliVersionForTesting),
).toBeTruthy();
fs.unlinkSync(executable);
});
it('Execute a command without bootstrap', () => {
const config = common.getCurrentConfiguration();
const executable = config
.getLocalLocation()
.replace(envSetup.inputfolder, envSetup.outputfolder);
try {
fs.unlinkSync(executable);
} catch {
//
}
expect(fs.existsSync(executable)).toBeFalsy();
const indexScript = path.join(envSetup.outputfolder, 'index.js');
// run system under test: index
const resultIndex = child_process.spawnSync(
'node ' + indexScript + ' --version',
{ shell: true },
);
if (resultIndex.status != 0) {
console.debug(resultIndex.stdout.toString());
console.debug(resultIndex.stderr.toString());
}
expect(fs.existsSync(executable)).toBeTruthy();
expect(resultIndex.status).toEqual(0);
// The binary wrapper should not output anything to stdout
// Assert the only stdout is from the CLI --version flag
expect(resultIndex.stdout.toString().split(' ')[0].trim()).toEqual(
cliVersionForTesting,
);
fs.unlinkSync(executable);
});
it('Execute with --legacy-cli', () => {
const config = common.getCurrentConfiguration();
const executable = config
.getLocalLocation()
.replace(envSetup.inputfolder, envSetup.outputfolder);
try {
fs.unlinkSync(executable);
} catch {
//
}
expect(fs.existsSync(executable)).toBeFalsy();
const indexScript = path.join(envSetup.outputfolder, 'index.js');
// run system under test: index
const resultIndex = child_process.spawnSync(
'node ' + indexScript + ' --legacy-cli --version',
{ shell: true },
);
expect(fs.existsSync(executable)).toBeFalsy();
expect(resultIndex.status).not.toEqual(0); // we expect this to fail, since the legacy cli is not available in the test, still we want to see that the logic around is working properly
expect(resultIndex.stdout.toString()).toEqual('');
expect(resultIndex.stderr.toString()).toContain(
'You are currently running a degraded version of the Snyk CLI.',
);
});
it('Execute with a failing download', () => {
const config = common.getCurrentConfiguration();
const executable = config
.getLocalLocation()
.replace(envSetup.inputfolder, envSetup.outputfolder);
try {
fs.unlinkSync(executable);
} catch {
//
}
expect(fs.existsSync(executable)).toBeFalsy();
const indexScript = path.join(envSetup.outputfolder, 'index.js');
// introduce error state by deleting the generated folder
fs.rmdirSync(path.join(envSetup.outputfolder, 'generated'), {
recursive: true,
});
// run system under test: index
const resultIndex = child_process.spawnSync(
'node ' + indexScript + ' --version',
{ shell: true },
);
expect(fs.existsSync(executable)).toBeFalsy();
expect(resultIndex.status).not.toEqual(0); // we expect this to fail, since the legacy cli is not available in the test, still we want to see that the logic around is working properly
expect(resultIndex.stdout.toString()).toEqual('');
expect(resultIndex.stderr.toString()).toContain(
'You are currently running a degraded version of the Snyk CLI.',
);
});
it('Bootstrap binary fails when proxy is used but not allowed', () => {
// only run when proxy is set
if (!process.env.https_proxy) {
console.info('Skipping test because https_proxy is not set');
return;
}
// setup
const config = common.getCurrentConfiguration();
const executable = config
.getLocalLocation()
.replace(envSetup.inputfolder, envSetup.outputfolder);
try {
fs.unlinkSync(executable);
} catch {
//
}
expect(fs.existsSync(executable)).toBeFalsy();
const bootstrapScript = path.join(envSetup.outputfolder, 'bootstrap.js');
// set NO_PROXY for snyk.io
process.env.NO_PROXY = '*.snyk.io';
// run system under test: index
const resultBootstrap = child_process.spawnSync(
'node ' + bootstrapScript + ' exec',
{ shell: true, env: { ...process.env } },
);
if (resultBootstrap.status != 0) {
console.debug(resultBootstrap.stdout.toString());
console.error(resultBootstrap.stderr.toString());
}
const expectedErrorMessage = 'ECONNREFUSED';
const expectedError = resultBootstrap.stderr
.toString()
.includes(expectedErrorMessage);
expect(expectedError).toBeTruthy();
expect(resultBootstrap.status).toEqual(0);
});
});

View File

@@ -0,0 +1,360 @@
import * as common from '../../src/common';
import * as fs from 'fs';
import * as path from 'path';
jest.setTimeout(60 * 1000);
const binaryDeploymentsPath = path.join(
__dirname,
'..',
'..',
'src',
'generated',
'binary-deployments.json',
);
const binaryDeployments = fs.readFileSync(binaryDeploymentsPath, 'utf8');
const supportedPlatforms = JSON.parse(binaryDeployments);
describe('Determine Binary Name', () => {
it('Determine Binary Name (darwin)', async () => {
const expectedamd64 = supportedPlatforms['darwin']['amd64'];
const expectedarm64 = supportedPlatforms['darwin']['arm64'];
const actualx64 = common.determineBinaryName('darwin', 'x64');
const actualarm64 = common.determineBinaryName('darwin', 'arm64');
const actualamd64 = common.determineBinaryName('darwin', 'amd64');
expect(actualx64).toEqual(expectedamd64);
expect(actualarm64).toEqual(expectedarm64);
expect(actualamd64).toEqual(expectedamd64);
});
it('Determine Binary Name (win)', async () => {
const expected = supportedPlatforms['windows']['amd64'];
const actualx64 = common.determineBinaryName('win32', 'x64');
const actualamd64 = common.determineBinaryName('win32', 'amd64');
expect(actualx64).toEqual(expected);
expect(actualamd64).toEqual(expected);
});
it('Determine Binary Name (linux)', async () => {
const expectedx64 = supportedPlatforms['linux']['amd64'];
const expectedarm64 = supportedPlatforms['linux']['arm64'];
const actualx64 = common.determineBinaryName('linux', 'x64');
const actualamd64 = common.determineBinaryName('linux', 'amd64');
const actualarm64 = common.determineBinaryName('linux', 'arm64');
expect(actualx64).toEqual(expectedx64);
expect(actualamd64).toEqual(expectedx64);
expect(actualarm64).toEqual(expectedarm64);
});
it('Determine Binary Name (alpine)', async () => {
const expectedx64 = supportedPlatforms['alpine']['amd64'];
const actualx64 = common.determineBinaryName('alpine', 'x64');
const actualamd64 = common.determineBinaryName('alpine', 'amd64');
expect(actualx64).toEqual(expectedx64);
expect(actualamd64).toEqual(expectedx64);
});
it('Unsupported Architecture', async () => {
expect(() => {
common.determineBinaryName('linux', 'mipsel');
}).toThrow();
});
it('Unsupported OS', async () => {
expect(() => {
common.determineBinaryName('unknownos', 'amd64');
}).toThrow();
});
});
describe('Get Version', () => {
it('Version available', async () => {
const expected = '1.1080.0';
const file = path.join(__dirname, 'test-version' + Math.random());
fs.writeFileSync(file, '1.1080.0\n');
const actual = common.getCurrentVersion(file);
expect(actual).toEqual(expected);
fs.unlinkSync(file);
});
it('Version file not available', async () => {
const expected = '';
const file = path.join(__dirname, 'not-existing-file');
const actual = common.getCurrentVersion(file);
expect(actual).toEqual(expected);
});
});
describe('Get Shasum', () => {
it('Shasum available (multiple)', async () => {
const expected = '0a238fe123';
const file = path.join(__dirname, 'sha256sums.txt' + Math.random());
fs.writeFileSync(
file,
'098fe123 *snyk-win\n12345 *snyk-macos\ncecece *snyk-linux-arm64\n0a238fe123 *snyk-linux',
);
const actual = common.getCurrentSha256sum('snyk-linux', file);
expect(actual).toEqual(expected);
fs.unlinkSync(file);
});
it('Shasum available (single)', async () => {
const expected = '0a238fe123';
const file = path.join(__dirname, 'sha256sums.txt' + Math.random());
fs.writeFileSync(file, '0a238fe123 snyk-linux\n');
const actual = common.getCurrentSha256sum('snyk-linux', file);
expect(actual).toEqual(expected);
fs.unlinkSync(file);
});
it('Shasum not available', async () => {
const expected = 'unknown-shasum-';
const file = path.join(__dirname, 'sha256sums.txt' + Math.random());
fs.writeFileSync(
file,
'098fe123 *snyk-win\n12345 *snyk-macos\n0a238fe123 *snyk-linux',
);
const actual = common.getCurrentSha256sum('snyk-linux-arm64', file);
expect(actual).toContain(expected);
fs.unlinkSync(file);
});
});
describe('Configuration', () => {
it('Download and local location', async () => {
const expectedDownloadLocation =
'https://downloads.snyk.io/cli/v1.2.3/snyk-win.exe';
const expectedLocalLocation = path.join(
__dirname,
'..',
'..',
'src',
'snyk-win.exe',
);
const config = new common.WrapperConfiguration(
'1.2.3',
'snyk-win.exe',
'1234abcdef',
);
const actualDownloadLocation = config.getDownloadLocations().downloadUrl;
expect(actualDownloadLocation).toEqual(expectedDownloadLocation);
const actualLocalLocation = config.getLocalLocation();
expect(actualLocalLocation).toEqual(expectedLocalLocation);
});
});
describe('Testing binary wrapper', () => {
it('getCliArguments() filter important stuff', async () => {
const indexFile = path.join(__dirname, '..', '..', 'src', 'index.ts');
const input = ['ignore', indexFile, 'important', 'stuff'];
const expected = ['important', 'stuff'];
const actual = common.getCliArguments(input);
expect(actual).toEqual(expected);
});
it('getCliArguments() filter important stuff (with directory only)', async () => {
const indexFile = path.join(__dirname, '..', '..', 'src');
const input = ['ignore', indexFile, 'important', 'stuff'];
const expected = ['important', 'stuff'];
const actual = common.getCliArguments(input);
expect(actual).toEqual(expected);
});
it('runWrapper() succesfully', async () => {
const executable = 'node';
const cliArguments = ['--version'];
const expected = 0;
const actual = common.runWrapper(executable, cliArguments);
expect(actual).toEqual(expected);
});
it('runWrapper() fail', async () => {
const executable = 'node-unknown';
const cliArguments = ['--version'];
const expected = 2;
const actual = common.runWrapper(executable, cliArguments);
expect(actual).toEqual(expected);
});
it('debugEnabled() false', async () => {
const cliArguments = ['--version', '--something', 'else'];
const expected = false;
const actual = common.debugEnabled(cliArguments);
expect(actual).toEqual(expected);
});
it('debugEnabled() true (--debug)', async () => {
const cliArguments = ['--version', '--something', '--debug', 'else'];
const expected = true;
const actual = common.debugEnabled(cliArguments);
expect(actual).toEqual(expected);
});
it('debugEnabled() true (-d)', async () => {
const cliArguments = ['--version', '--something', '-d', 'else'];
const expected = true;
const actual = common.debugEnabled(cliArguments);
expect(actual).toEqual(expected);
});
});
describe('Testing binary bootstrapper', () => {
it('downloadExecutable() succesfull', async () => {
const binaryName = 'snyk-macos';
const shafileExtension = '.sha256';
const config = new common.WrapperConfiguration('1.1080.0', binaryName, '');
const shasumFile =
config.getLocalLocation() + Math.random() + shafileExtension;
// download the shasum first, here we don't expect a shasum comparison
const shasumDownload = await common.downloadExecutable(
config.getDownloadLocations().downloadUrl + shafileExtension,
shasumFile,
'',
);
expect(shasumDownload).toBeUndefined();
expect(fs.existsSync(shasumFile)).toBeTruthy();
const expectedShasum = common.getCurrentSha256sum(binaryName, shasumFile);
const { downloadUrl } = config.getDownloadLocations();
// download binary next and use previously downloaded shasum to check validity
const binaryDownload = await common.downloadExecutable(
downloadUrl,
config.getLocalLocation(),
expectedShasum,
);
expect(binaryDownload).toBeUndefined();
expect(fs.existsSync(config.getLocalLocation())).toBeTruthy();
const stats = fs.statSync(config.getLocalLocation());
expect(stats.mode).toEqual(0o100755);
try {
// check if the binary is executable
expect(
fs.accessSync(config.getLocalLocation(), fs.constants.X_OK),
).not.toThrow();
} catch {
// execution of binary not possible
}
fs.unlinkSync(shasumFile);
fs.unlinkSync(config.getLocalLocation());
});
it('downloadWithBackup() succesfull', async () => {
const binaryName = 'snyk-macos';
const shafileExtension = '.sha256';
const config = new common.WrapperConfiguration('1.1080.0', binaryName, '');
const shasumFile =
config.getLocalLocation() + Math.random() + shafileExtension;
const { downloadUrl } = config.getDownloadLocations();
// download the shasum first, here we don't expect a shasum comparison
const shasumDownload = await common.downloadWithBackup(
'https://notdownloads.snyk.io/cli/v1.1080.0/snyk-macos.sha256',
downloadUrl + shafileExtension,
shasumFile,
'',
);
expect(shasumDownload).toBeUndefined();
expect(fs.existsSync(shasumFile)).toBeTruthy();
const expectedShasum = common.getCurrentSha256sum(binaryName, shasumFile);
// download binary next and use previously downloaded shasum to check validity
const binaryDownload = await common.downloadWithBackup(
'https://notdownloads.snyk.io/cli/v1.1080.0/snyk-macos',
downloadUrl,
config.getLocalLocation(),
expectedShasum,
);
expect(binaryDownload).toBeUndefined();
expect(fs.existsSync(config.getLocalLocation())).toBeTruthy();
const stats = fs.statSync(config.getLocalLocation());
expect(stats.mode).toEqual(0o100755);
try {
// check if the binary is executable
expect(
fs.accessSync(config.getLocalLocation(), fs.constants.X_OK),
).not.toThrow();
} catch {
// execution of binary not possible
}
fs.unlinkSync(shasumFile);
fs.unlinkSync(config.getLocalLocation());
});
it('downloadExecutable() fails due to incorrect shasum', async () => {
const binaryName = 'snyk-macos';
const shafileExtension = '.sha256';
const config = new common.WrapperConfiguration('1.1080.0', binaryName, '');
const shasumFile =
config.getLocalLocation() + Math.random() + shafileExtension;
const { downloadUrl } = config.getDownloadLocations();
// download just any file and state a shasum expectation that never can be fullfilled
const shasumDownload = await common.downloadExecutable(
downloadUrl + shafileExtension,
shasumFile,
'incorrect-shasum',
);
expect(shasumDownload?.message).toContain('Shasum comparison failed');
expect(fs.existsSync(shasumFile)).toBeFalsy();
});
it("downloadExecutable() try to download a file that doesn't exist", async () => {
const binaryName = 'snyk-macos';
const shafileExtension = '.shoe256';
const config = new common.WrapperConfiguration('1.1080.0', binaryName, '');
const shasumFile =
config.getLocalLocation() + Math.random() + shafileExtension;
const { downloadUrl } = config.getDownloadLocations();
// try to download a file that doesn't exis
const shasumDownload = await common.downloadExecutable(
downloadUrl + shafileExtension,
shasumFile,
'incorrect-shasum',
);
expect(shasumDownload?.message).toContain(
'Download failed! Server Response:',
);
expect(fs.existsSync(shasumFile)).toBeFalsy();
});
it('downloadExecutable() fails due to an error in the https connection', async () => {
// download the just any file and state a shasum expectation that never can be fullfilled
const shasumDownload = await common.downloadExecutable(
'https://notaurl',
'',
'',
);
expect(shasumDownload).toBeDefined();
});
});
describe('isAnalyticsEnabled', () => {
it('enabled', async () => {
delete process.env.SNYK_DISABLE_ANALYTICS;
expect(common.isAnalyticsEnabled()).toBeTruthy();
});
it('disabled', async () => {
process.env.SNYK_DISABLE_ANALYTICS = '1';
expect(common.isAnalyticsEnabled()).toBeFalsy();
delete process.env.SNYK_DISABLE_ANALYTICS;
});
});

View File

@@ -0,0 +1,84 @@
import * as common from '../../src/common';
import * as child_process from 'child_process';
import * as path from 'path';
import { copyFileSync, mkdirSync, rmdirSync, writeFileSync } from 'fs';
export class TestEnvironmentSetup {
constructor(
readonly root = path.join(__dirname, '..', '..', '..'),
readonly inputfolder = path.join(__dirname, '..', '..', 'src'),
readonly outputfolder = path.join(__dirname, 'something'),
) {
this.root = root;
this.inputfolder = inputfolder;
this.outputfolder = outputfolder;
}
async prepareEnvironment(version: string) {
const versionFile = common.versionFile.replace(
this.inputfolder,
this.outputfolder,
);
const shasumFile = common.shasumFile.replace(
this.inputfolder,
this.outputfolder,
);
try {
this.prepareDirectories();
this.compileTSCode();
writeFileSync(versionFile, version);
await this.downloadShaSum(version, shasumFile);
} catch (error) {
console.error('Error while preparing environment');
console.error(error);
throw error;
}
}
cleanupDirectories() {
rmdirSync(this.outputfolder, { recursive: true }); // remove output folder
}
prepareDirectories() {
mkdirSync(path.join(this.outputfolder, 'generated'), {
recursive: true,
});
copyFileSync(
path.join(this.root, 'binary-deployments.json'),
path.join(this.outputfolder, 'generated', 'binary-deployments.json'),
);
}
private compileTSCode() {
const tsc = child_process.spawnSync(
'npx tsc --outDir ' +
this.outputfolder +
' --tsBuildInfoFile ' +
path.join(this.outputfolder, 'tsconfig.tsbuildinfo'),
{ cwd: this.inputfolder, shell: true },
);
if (tsc.status) {
console.debug(tsc);
console.debug(tsc.stdout.toString());
console.debug(tsc.stderr.toString());
}
}
private async downloadShaSum(version: string, shasumFile: string) {
await common.downloadExecutable(
'https://downloads.snyk.io/cli/v' + version + '/sha256sums.txt.asc',
shasumFile,
'',
);
}
}
if (process.argv.includes('exec')) {
(async function () {
const env = new TestEnvironmentSetup();
await env.prepareEnvironment('1.1292.1');
})();
}