diff --git a/cliv2/cmd/cliv2/main.go b/cliv2/cmd/cliv2/main.go index b54a68a1e1..9fe17b081c 100644 --- a/cliv2/cmd/cliv2/main.go +++ b/cliv2/cmd/cliv2/main.go @@ -227,29 +227,6 @@ func getErrorFromWorkFlowData(engine workflow.Engine, data []workflow.Data) erro return nil } -func sendAnalytics(analytics analytics.Analytics, debugLogger *zerolog.Logger) { - debugLogger.Print("Sending Analytics") - - res, err := analytics.Send() - if err != nil { - debugLogger.Err(err).Msg("Failed to send Analytics") - return - } - defer res.Body.Close() - - successfullySend := 200 <= res.StatusCode && res.StatusCode < 300 - if successfullySend { - debugLogger.Print("Analytics successfully send") - } else { - var details string - if res != nil { - details = res.Status - } - - debugLogger.Print("Failed to send Analytics:", details) - } -} - func sendInstrumentation(eng workflow.Engine, instrumentor analytics.InstrumentationCollector, logger *zerolog.Logger) { // Avoid duplicate data to be sent for IDE integrations that use the CLI if !shallSendInstrumentation(eng.GetConfiguration(), instrumentor) { @@ -577,10 +554,6 @@ func MainWithErrorCode() int { if exitCode == 2 { cliAnalytics.GetInstrumentation().SetStatus(analytics.Failure) } - - if !globalConfiguration.GetBool(configuration.ANALYTICS_DISABLED) { - sendAnalytics(cliAnalytics, globalLogger) - } sendInstrumentation(globalEngine, cliAnalytics.GetInstrumentation(), globalLogger) // cleanup resources in use diff --git a/src/lib/analytics/index.ts b/src/lib/analytics/index.ts index 3022873234..75a6114feb 100644 --- a/src/lib/analytics/index.ts +++ b/src/lib/analytics/index.ts @@ -4,7 +4,6 @@ import stripAnsi = require('strip-ansi'); import { getAuthHeader, someTokenExists } from '../api-token'; import config from '../config'; import { makeRequest } from '../request'; -import { config as userConfig } from '../user-config'; import { getStandardData } from './getStandardData'; // Add flags whose values should be redacted in analytics here. @@ -56,11 +55,7 @@ export function addDataAndSend( } export function allowAnalytics(): boolean { - if (userConfig.get('disable-analytics') || config.DISABLE_ANALYTICS) { - return false; - } else { - return true; - } + return false; } /** diff --git a/test/fixtures/demo-os/core/test/integration/api/api_mail_spec.js b/test/fixtures/demo-os/core/test/integration/api/api_mail_spec.js index 8cb2b6e880..dd72366cf0 100644 --- a/test/fixtures/demo-os/core/test/integration/api/api_mail_spec.js +++ b/test/fixtures/demo-os/core/test/integration/api/api_mail_spec.js @@ -112,38 +112,4 @@ describe('Mail API', function () { }).catch(done); }); }); - - describe.skip('Stub', function () { - it('returns a success', function (done) { - config.set({mail: {transport: 'stub'}}); - - mailer.init().then(function () { - mailer.transport.transportType.should.eql('STUB'); - return MailAPI.send(mailDataNoServer, testUtils.context.internal); - }).then(function (response) { - should.exist(response.mail); - should.exist(response.mail[0].message); - should.exist(response.mail[0].status); - response.mail[0].status.should.eql({message: 'Message Queued'}); - response.mail[0].message.subject.should.eql('testemail'); - done(); - }).catch(done); - }); - - it('returns a boo boo', function (done) { - config.set({mail: {transport: 'stub', error: 'Stub made a boo boo :('}}); - - mailer.init().then(function () { - mailer.transport.transportType.should.eql('STUB'); - return MailAPI.send(mailDataNoServer, testUtils.context.internal); - }).then(function (response) { - console.log('res', response.mail[0]); - done(new Error('Stub did not error')); - }, function (error) { - error.message.should.startWith('Email Error: Failed sending email: there is no mail server at this address'); - error.errorType.should.eql('EmailError'); - done(); - }).catch(done); - }); - }); }); diff --git a/test/jest/acceptance/analytics.spec.ts b/test/jest/acceptance/analytics.spec.ts deleted file mode 100644 index f8cb6c5170..0000000000 --- a/test/jest/acceptance/analytics.spec.ts +++ /dev/null @@ -1,555 +0,0 @@ -import { fakeServer } from '../../acceptance/fake-server'; -import { - createProjectFromFixture, - createProjectFromWorkspace, -} from '../util/createProject'; -import { runSnykCLI } from '../util/runSnykCLI'; -import { getServerPort } from '../util/getServerPort'; - -jest.setTimeout(1000 * 30); - -describe('analytics module', () => { - let server; - let env: Record; - const port = getServerPort(process); - - beforeAll((done) => { - const baseApi = '/api/v1'; - env = { - ...process.env, - SNYK_API: 'http://localhost:' + port + baseApi, - SNYK_HOST: 'http://localhost:' + port, - SNYK_TOKEN: '123456789', - SNYK_INTEGRATION_NAME: 'JENKINS', - SNYK_INTEGRATION_VERSION: '1.2.3', - }; - server = fakeServer(baseApi, env.SNYK_TOKEN); - server.listen(port, () => { - done(); - }); - }); - - afterEach(() => { - server.restore(); - }); - - afterAll((done) => { - server.close(() => { - done(); - }); - }); - - it('sends analytics for `snyk test` with no vulns found', async () => { - const project = await createProjectFromWorkspace('npm-package'); - const { code } = await runSnykCLI('test --debug', { - cwd: project.path(), - env, - }); - - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - // in this case an extra analytics event is being sent, which needs to be dropped - requests.pop(); - - const lastRequest = requests.pop(); - - expect(lastRequest).toMatchObject({ - headers: { - host: `localhost:${port}`, - accept: 'application/json', - authorization: 'token 123456789', - 'content-type': 'application/json; charset=utf-8', - 'x-snyk-cli-version': expect.stringMatching(/^(\d+\.){2}.*/), - }, - query: {}, - body: { - data: { - args: [{}], - ci: expect.any(Boolean), - command: 'test', - metadata: { - pluginName: 'snyk-nodejs-lockfile-parser', - packageManager: 'npm', - packageName: 'npm-package', - packageVersion: '1.0.0', - isDocker: false, - depGraph: true, - vulns: 0, - }, - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: { - 'network_time': { - type: 'timer', - values: expect.any(Array), - total: expect.any(Number), - }, - 'cpu_time': { - type: 'synthetic', - values: [expect.any(Number)], - total: expect.any(Number), - }, - }, - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('sends analytics for `snyk test` with vulns found', async () => { - const project = await createProjectFromFixture( - 'npm/with-vulnerable-lodash-dep', - ); - server.setCustomResponse( - await project.readJSON('test-dep-graph-result.json'), - ); - - const { code } = await runSnykCLI('test', { - cwd: project.path(), - env, - }); - - expect(code).toBe(1); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - // in this case an extra analytics event is being sent, which needs to be dropped - requests.pop(); - - const lastRequest = requests.pop(); - expect(lastRequest).toMatchObject({ - headers: { - host: `localhost:${port}`, - accept: 'application/json', - authorization: 'token 123456789', - 'content-type': 'application/json; charset=utf-8', - 'x-snyk-cli-version': expect.stringMatching(/^(\d+\.){2}.*/), - }, - query: {}, - body: { - data: { - args: [{}], - ci: expect.any(Boolean), - command: 'test', - metadata: { - 'generating-node-dependency-tree': { - lockFile: true, - targetFile: 'package-lock.json', - }, - lockfileVersion: 2, - pluginName: 'snyk-nodejs-lockfile-parser', - packageManager: 'npm', - packageName: 'with-vulnerable-lodash-dep', - packageVersion: '1.2.3', - prePrunedPathsCount: 2, - depGraph: true, - isDocker: false, - 'vulns-pre-policy': 5, - vulns: 5, - actionableRemediation: true, - 'error-code': 'VULNS', - 'error-message': 'Vulnerabilities found', - }, - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: { - 'network_time': { - type: 'timer', - values: expect.any(Array), - total: expect.any(Number), - }, - 'cpu_time': { - type: 'synthetic', - values: [expect.any(Number)], - total: expect.any(Number), - }, - }, - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('sends correct analytics data a bad command', async () => { - const project = await createProjectFromWorkspace('npm-package'); - const { code } = await runSnykCLI('random-nonsense-command --some-option', { - cwd: project.path(), - env, - }); - - expect(code).toBe(2); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - // in this case an extra analytics event is being sent, which needs to be dropped - requests.pop(); - - const lastRequest = requests.pop(); - expect(lastRequest).toMatchObject({ - headers: { - host: `localhost:${port}`, - accept: 'application/json', - authorization: 'token 123456789', - 'content-type': 'application/json; charset=utf-8', - 'x-snyk-cli-version': expect.stringMatching(/^(\d+\.){2}.*/), - }, - query: {}, - body: { - data: { - args: ['random-nonsense-command'], - ci: expect.any(Boolean), - command: 'bad-command', - metadata: { - command: 'random-nonsense-command', - error: expect.stringContaining( - 'Error: Unknown command "random-nonsense-command"', - ), - 'error-code': 'UNKNOWN_COMMAND', - 'error-message': 'Unknown command "random-nonsense-command"', - }, - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: { - 'network_time': { - type: 'timer', - values: expect.any(Array), - total: expect.any(Number), - }, - 'cpu_time': { - type: 'synthetic', - values: [expect.any(Number)], - total: expect.any(Number), - }, - }, - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('sends correct analytics with error-details containing 403 error body', async () => { - server.setNextResponse({ error: 'Unauthorized' }); - server.setNextStatusCode(403); - - const project = await createProjectFromFixture( - 'npm/with-vulnerable-lodash-dep', - ); - - const { code } = await runSnykCLI( - 'test --org=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', - { - cwd: project.path(), - env, - }, - ); - - expect(code).toBe(2); - - const requests = server.getRequests().filter((value) => { - return value.url.includes('/api/v1/analytics/cli'); - }); - - // in this case an extra analytics event is being sent, which needs to be dropped - requests.pop(); - - const lastRequest = requests.pop(); - expect(lastRequest).toMatchObject({ - query: {}, - body: { - data: { - args: [{}], - ci: expect.any(Boolean), - command: 'bad-command', - metadata: { - 'generating-node-dependency-tree': { - lockFile: true, - targetFile: 'package-lock.json', - }, - lockfileVersion: 2, - pluginName: 'snyk-nodejs-lockfile-parser', - packageManager: 'npm', - packageName: 'with-vulnerable-lodash-dep', - packageVersion: '1.2.3', - prePrunedPathsCount: 2, - 'error-code': 403, - 'error-message': expect.stringContaining( - 'Authentication failed. Please check the API token on', - ), - 'error-details': { error: 'Unauthorized' }, - }, - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: { - 'network_time': { - type: 'timer', - values: expect.any(Array), - total: expect.any(Number), - }, - 'cpu_time': { - type: 'synthetic', - values: [expect.any(Number)], - total: expect.any(Number), - }, - }, - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('sends analytics data a bad command', async () => { - const project = await createProjectFromWorkspace('npm-package'); - const { code } = await runSnykCLI('', { - cwd: project.path(), - env, - }); - - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - // in this case an extra analytics event is being sent, which needs to be dropped - requests.pop(); - - const lastRequest = requests.pop(); - expect(lastRequest).toMatchObject({ - headers: { - host: `localhost:${port}`, - accept: 'application/json', - authorization: 'token 123456789', - 'content-type': 'application/json; charset=utf-8', - 'x-snyk-cli-version': expect.stringMatching(/^(\d+\.){2}.*/), - }, - query: {}, - body: { - data: { - args: ['help', {}], - ci: expect.any(Boolean), - command: 'help', - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: { - 'network_time': { - type: 'timer', - values: expect.any(Array), - total: expect.any(Number), - }, - 'cpu_time': { - type: 'synthetic', - values: [expect.any(Number)], - total: expect.any(Number), - }, - }, - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('sends analytics data with basic check only', async () => { - const project = await createProjectFromWorkspace('npm-package'); - const { code } = await runSnykCLI('', { - cwd: project.path(), - env, - }); - - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - const lastRequest = requests.at(-1); - - expect(lastRequest).toMatchObject({ - headers: { - host: `localhost:${port}`, - 'content-length': expect.any(String), - authorization: 'token 123456789', - 'content-type': 'application/json; charset=utf-8', - 'x-snyk-cli-version': expect.stringMatching(/^(\d+\.){2}.*/), - }, - query: {}, - body: { - data: { - args: expect.any(Array), - ci: expect.any(Boolean), - command: expect.any(String), - durationMs: expect.any(Number), - id: expect.any(String), - integrationEnvironment: '', - integrationEnvironmentVersion: '', - integrationName: 'JENKINS', - integrationVersion: '1.2.3', - // prettier-ignore - metrics: expect.any(Object), - nodeVersion: expect.any(String), - os: expect.any(String), - standalone: expect.any(Boolean), - version: expect.stringMatching(/^(\d+\.){2}.*/), - }, - }, - }); - }); - - it('uses OAUTH token if set', async () => { - const project = await createProjectFromWorkspace('npm-package'); - const { code } = await runSnykCLI('test woof', { - cwd: project.path(), - env: { - ...env, - SNYK_OAUTH_TOKEN: 'oauth-jwt-token', - }, - }); - expect(code).toBe(0); - - const lastRequest = server.popRequest(); - expect(lastRequest).toMatchObject({ - headers: { - authorization: 'Bearer oauth-jwt-token', - }, - }); - }); - - it("won't send analytics if disable analytics is set via SNYK_DISABLE_ANALYTICS", async () => { - const { code } = await runSnykCLI(`version`, { - env: { - ...env, - SNYK_DISABLE_ANALYTICS: '1', - }, - }); - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - const lastRequest = requests.pop(); - expect(lastRequest).toBeUndefined(); - }); - - it("won't send analytics if disable analytics is set via SNYK_CFG_DISABLE_ANALYTICS", async () => { - const { code } = await runSnykCLI(`version`, { - env: { - ...env, - SNYK_CFG_DISABLE_ANALYTICS: '1', - }, - }); - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - const lastRequest = requests.pop(); - expect(lastRequest).toBeUndefined(); - }); - - it("won't send analytics if disable analytics is set via config and disable-analytics", async () => { - const envWithDisabledAnalytics = { - ...env, - SNYK_DISABLE_ANALYTICS: '1', - }; - - // set config - await runSnykCLI(`config set disable-analytics=1`, { - env: envWithDisabledAnalytics, - }); - - const { code } = await runSnykCLI(`version`, { - env: env, - }); - - // unset config - await runSnykCLI(`config unset disable-analytics`, { - env: envWithDisabledAnalytics, - }); - - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - const lastRequest = requests.pop(); - expect(lastRequest).toBeUndefined(); - }); - - it("won't send analytics if disable analytics is set via --DISABLE_ANALYTICS", async () => { - const { code } = await runSnykCLI(`version -d --DISABLE_ANALYTICS`, { - env: env, - }); - expect(code).toBe(0); - - const requests = server.getRequests().filter((value) => { - return value.url == '/api/v1/analytics/cli'; - }); - - expect(requests.length).toBe(0); - }); - - it('if analytics are disabled with --DISABLE_ANALYTICS, SNYK_DISABLE_ANALYTICS will be set to 1', async () => { - // Using woof --language=cat prints currently set environment variables. - // --env will print the environment variable's value. - const { code, stdout } = await runSnykCLI( - `woof --language=cat --env=SNYK_DISABLE_ANALYTICS --DISABLE_ANALYTICS`, - { - env: { - ...env, - }, - }, - ); - - expect(code).toBe(0); - expect(stdout).toContain('SNYK_DISABLE_ANALYTICS=1'); - }); -}); diff --git a/test/jest/acceptance/snyk-test/protect-upgrade-notification.spec.ts b/test/jest/acceptance/snyk-test/protect-upgrade-notification.spec.ts index 258e9ae5d3..ad1acab86a 100644 --- a/test/jest/acceptance/snyk-test/protect-upgrade-notification.spec.ts +++ b/test/jest/acceptance/snyk-test/protect-upgrade-notification.spec.ts @@ -136,24 +136,6 @@ describe('analytics module', () => { expect(stdout).not.toContain( project.path('with-package-json-without-snyk-dep/package.json'), ); - - const requests = server.getRequests().filter((req: any) => { - return JSON.stringify(req.body).includes('upgradable-snyk-protect-paths'); - }); - - expect(requests[0].url).toEqual('/api/v1/analytics/cli'); - expect(requests).toHaveLength(1); - expect(requests[0]).toMatchObject({ - query: {}, - body: { - data: { - command: 'test', - metadata: { - 'upgradable-snyk-protect-paths': 2, - }, - }, - }, - }); }); it('detects no upgradable protect paths with `snyk test` with no upgradable paths in the cwd', async () => { @@ -171,23 +153,5 @@ describe('analytics module', () => { 'WARNING: It looks like you have the `snyk` dependency in the `package.json` file(s) at the following path(s):', ); expect(stdout).not.toContain(project.path('package.json')); - - const requests = server.getRequests().filter((req: any) => { - return JSON.stringify(req.body).includes('upgradable-snyk-protect-paths'); - }); - - expect(requests[0].url).toEqual('/api/v1/analytics/cli'); - expect(requests).toHaveLength(1); - expect(requests[0]).toMatchObject({ - query: {}, - body: { - data: { - command: 'test', - metadata: { - 'upgradable-snyk-protect-paths': 0, - }, - }, - }, - }); }); }); diff --git a/test/jest/unit/lib/analytics/index.spec.ts b/test/jest/unit/lib/analytics/index.spec.ts deleted file mode 100644 index 47eba7f784..0000000000 --- a/test/jest/unit/lib/analytics/index.spec.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as analytics from '../../../../../src/lib/analytics'; -import * as request from '../../../../../src/lib/request'; -import { argsFrom } from './utils'; -import * as apiTokenModule from '../../../../../src/lib/api-token'; - -describe('analytics module', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('sends analytics with no token set', async () => { - analytics.add('k1', 'v1'); - const requestSpy = jest.spyOn(request, 'makeRequest'); - requestSpy.mockResolvedValue(); - - const someTokenExistsSpy = jest.spyOn(apiTokenModule, 'someTokenExists'); - someTokenExistsSpy.mockReturnValue(false); - - await analytics.addDataAndSend({ - args: argsFrom({}), - }); - - expect(requestSpy).toBeCalledTimes(1); - expect(requestSpy.mock.calls[0][0]).not.toHaveProperty( - 'headers.authorization', - ); - }); - - it('removes sensitive flags', async () => { - const requestSpy = jest.spyOn(request, 'makeRequest'); - requestSpy.mockResolvedValue(); - - await analytics.addDataAndSend({ - args: argsFrom({ - 'tfc-endpoint': "I don't care who sees this", - 'tfc-token': 'itsasecret', - }), - }); - - expect(requestSpy.mock.calls[0][0].body.data).toHaveProperty('args', [ - { - 'tfc-endpoint': "I don't care who sees this", - 'tfc-token': 'REDACTED', - }, - ]); - }); - - it('ignores analytics request failures', async () => { - const requestSpy = jest.spyOn(request, 'makeRequest'); - requestSpy.mockRejectedValue(new Error('this should be ignored')); - - const result = analytics.addDataAndSend({ - args: argsFrom({}), - }); - - await expect(result).resolves.toBeUndefined(); - }); - - it('adds a value to the current analytics metadata', async () => { - const requestSpy = jest.spyOn(request, 'makeRequest'); - requestSpy.mockResolvedValue(); - - analytics.add('k2', 2); - analytics.add('k3', { test: 'test' }); - analytics.add('k1', 'v2'); - analytics.add('k1', ['v3', 'v4']); - analytics.add('k1', 5); - analytics.add('k2', 4); - analytics.add('k3', { test: 'test' }); - - await analytics.addDataAndSend({ - args: argsFrom({}), - }); - - expect(requestSpy.mock.calls[0][0].body.data).toHaveProperty('metadata', { - k1: ['v1', 'v2', 'v3', 'v4', 5], //v1 was added in the 1st test - k2: 6, - k3: [{ test: 'test' }, { test: 'test' }], - }); - }); - - it('adds "iac-type" data to the current analytics metadata', async () => { - const requestSpy = jest.spyOn(request, 'makeRequest'); - requestSpy.mockResolvedValue(); - - analytics.add('iac-type', { - cloudformationconfig: { - count: 1, - low: 8, - medium: 9, - }, - }); - analytics.add('iac-type', { - terraformconfig: { - count: 1, - medium: 15, - high: 10, - low: 15, - }, - }); - analytics.add('iac-type', { - terraformconfig: { - count: 1, - low: 5, - }, - cloudformationconfig: { - count: 1, - low: 2, - medium: 6, - high: 20, - }, - }); - - await analytics.addDataAndSend({ - args: argsFrom({}), - }); - - expect(requestSpy.mock.calls[0][0].body.data.metadata).toHaveProperty( - 'iac-type', - { - cloudformationconfig: { count: 2, low: 10, medium: 15, high: 20 }, - terraformconfig: { count: 2, medium: 15, high: 10, low: 20 }, - }, - ); - }); -});