diff --git a/cliv2/go.mod b/cliv2/go.mod index 6fe4f748a5..292cf7a319 100644 --- a/cliv2/go.mod +++ b/cliv2/go.mod @@ -9,9 +9,9 @@ require ( github.com/google/uuid v1.3.1 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 - github.com/snyk/cli-extension-dep-graph v0.0.0-20230821114325-956f460c443c + github.com/snyk/cli-extension-dep-graph v0.0.0-20230831101913-402a467e32e7 github.com/snyk/cli-extension-iac-rules v0.0.0-20230601153200-c572cfce46ce - github.com/snyk/cli-extension-sbom v0.0.0-20230608154310-6573cedca977 + github.com/snyk/cli-extension-sbom v0.0.0-20230831113416-7ffac8738181 github.com/snyk/go-application-framework v0.0.0-20230825084328-b839e0e50201 github.com/snyk/go-httpauth v0.0.0-20230726132335-d454674305a7 github.com/snyk/snyk-iac-capture v0.6.0 diff --git a/cliv2/go.sum b/cliv2/go.sum index 512adc4d5e..d947a6e33c 100644 --- a/cliv2/go.sum +++ b/cliv2/go.sum @@ -613,12 +613,12 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/snyk/cli-extension-dep-graph v0.0.0-20230821114325-956f460c443c h1:T9A/nUHxfxVrLWnlDg//FXa1Moj7gFJJexbPhHP5Wzc= -github.com/snyk/cli-extension-dep-graph v0.0.0-20230821114325-956f460c443c/go.mod h1:QF3v8HBpOpyudYNCuR8LqfULutO76c91sBdLzD+pBJU= +github.com/snyk/cli-extension-dep-graph v0.0.0-20230831101913-402a467e32e7 h1:V5b1Yz4Qjufs14bFoYBH6BRtfzTTCHeW32rV/9TiUY8= +github.com/snyk/cli-extension-dep-graph v0.0.0-20230831101913-402a467e32e7/go.mod h1:QF3v8HBpOpyudYNCuR8LqfULutO76c91sBdLzD+pBJU= github.com/snyk/cli-extension-iac-rules v0.0.0-20230601153200-c572cfce46ce h1:WchwuyPX4mEr7tFCGD6EsjwTDipFWfLxs4Wps6KB3b4= github.com/snyk/cli-extension-iac-rules v0.0.0-20230601153200-c572cfce46ce/go.mod h1:5/IYYTgf32pST7St4GhS3KNz32WE17Ys+Hdb5Pqxex0= -github.com/snyk/cli-extension-sbom v0.0.0-20230608154310-6573cedca977 h1:kW8XGQ3hseTP6WbGrO4Q6ssqGejT02hleNzxfWga72E= -github.com/snyk/cli-extension-sbom v0.0.0-20230608154310-6573cedca977/go.mod h1:O/cjwCbKhJQWyXHPmNbZ7ToQKnhyw0VUp1Qhim3WEcw= +github.com/snyk/cli-extension-sbom v0.0.0-20230831113416-7ffac8738181 h1:BMiPwr4/sD71Jfrvorgy+L2E7PCkbT36c4wo2N+BKPg= +github.com/snyk/cli-extension-sbom v0.0.0-20230831113416-7ffac8738181/go.mod h1:O/cjwCbKhJQWyXHPmNbZ7ToQKnhyw0VUp1Qhim3WEcw= github.com/snyk/go-application-framework v0.0.0-20230825084328-b839e0e50201 h1:5oHKA6Zs/c2oJ6MEznPn4grAJyedzltm9ms3o9ojU0Y= github.com/snyk/go-application-framework v0.0.0-20230825084328-b839e0e50201/go.mod h1:Aun65T/AmzxjZe9jZZBqia6RHwoS7oq8QB2UfQIcPjU= github.com/snyk/go-httpauth v0.0.0-20230726132335-d454674305a7 h1:m8C34vcouY2vEvow2gV/uAZ0LKiV7vhwC5HI15nUDX4= diff --git a/test/fixtures/gradle-configurations/build.gradle b/test/fixtures/gradle-configurations/build.gradle new file mode 100644 index 0000000000..ba08b5cfd7 --- /dev/null +++ b/test/fixtures/gradle-configurations/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'java-library' +} + +group = 'com.example' +version = '1.0' + +repositories { + mavenCentral() +} + +def testAttribute = Attribute.of('test.snykattr', String) + +dependencies.attributesSchema { + attribute(testAttribute) +} + +configurations { + api { + attributes { + attribute(testAttribute, 'api') + } + } +} + +dependencies { + api 'org.jooq:jooq:3.18.6' + implementation 'com.google.guava:guava:23.0' + testImplementation 'junit:junit:4.+' +} diff --git a/test/fixtures/gradle-configurations/test-init.gradle b/test/fixtures/gradle-configurations/test-init.gradle new file mode 100644 index 0000000000..e58bac063b --- /dev/null +++ b/test/fixtures/gradle-configurations/test-init.gradle @@ -0,0 +1,7 @@ +allprojects { + plugins.withType(JavaPlugin) { + dependencies { + implementation 'org.apache.logging.log4j:log4j-api:2.20.0' + } + } +} diff --git a/test/fixtures/gradle-multi-project/app/build.gradle b/test/fixtures/gradle-multi-project/app/build.gradle new file mode 100644 index 0000000000..4107276d19 --- /dev/null +++ b/test/fixtures/gradle-multi-project/app/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' +} + +group = 'com.example' +version = '1.0' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':lib') + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' +} diff --git a/test/fixtures/gradle-multi-project/build.gradle b/test/fixtures/gradle-multi-project/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/gradle-multi-project/lib/build.gradle b/test/fixtures/gradle-multi-project/lib/build.gradle new file mode 100644 index 0000000000..ad7421f553 --- /dev/null +++ b/test/fixtures/gradle-multi-project/lib/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java-library' +} + +group = 'com.example' +version = '1.0' + +repositories { + mavenCentral() +} + +dependencies { + api 'org.jooq:jooq:3.18.6' + implementation 'com.google.guava:guava:23.0' + testImplementation 'junit:junit:4.+' +} \ No newline at end of file diff --git a/test/fixtures/gradle-multi-project/settings.gradle b/test/fixtures/gradle-multi-project/settings.gradle new file mode 100644 index 0000000000..a1b0e74981 --- /dev/null +++ b/test/fixtures/gradle-multi-project/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'example-multi-project' +include 'app' +include 'lib' \ No newline at end of file diff --git a/test/fixtures/maven-aggregate-project/core/pom.xml b/test/fixtures/maven-aggregate-project/core/pom.xml new file mode 100644 index 0000000000..de39a516a0 --- /dev/null +++ b/test/fixtures/maven-aggregate-project/core/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + parent-project + io.snyk + 1.0-SNAPSHOT + + + io.snyk + core + 1.0-SNAPSHOT + + core + + http://www.example.com + + + UTF-8 + + + + + com.google.guava + guava + 29.0-jre + + + diff --git a/test/fixtures/maven-aggregate-project/extra/pom.xml b/test/fixtures/maven-aggregate-project/extra/pom.xml new file mode 100644 index 0000000000..99adcdbd74 --- /dev/null +++ b/test/fixtures/maven-aggregate-project/extra/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + parent-project + io.snyk + 1.0-SNAPSHOT + + + io.snyk + extra + 1.0-SNAPSHOT + + extra + + http://www.example.com + + + UTF-8 + + diff --git a/test/fixtures/maven-aggregate-project/pom.xml b/test/fixtures/maven-aggregate-project/pom.xml new file mode 100644 index 0000000000..99c776813a --- /dev/null +++ b/test/fixtures/maven-aggregate-project/pom.xml @@ -0,0 +1,23 @@ + + + + 4.0.0 + io.snyk + parent-project + 1.0-SNAPSHOT + pom + parent-project + + http://www.example.com + + UTF-8 + 1.8 + 1.8 + + + + service + core + extra + + diff --git a/test/fixtures/maven-aggregate-project/service/pom.xml b/test/fixtures/maven-aggregate-project/service/pom.xml new file mode 100644 index 0000000000..63ef128adf --- /dev/null +++ b/test/fixtures/maven-aggregate-project/service/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + parent-project + io.snyk + 1.0-SNAPSHOT + + + io.snyk + service + 1.0-SNAPSHOT + + service + + http://www.example.com + + + UTF-8 + + diff --git a/test/fixtures/maven-jars/keycloak-core-5.0.0.jar b/test/fixtures/maven-jars/keycloak-core-5.0.0.jar new file mode 100644 index 0000000000..504e0a3daf Binary files /dev/null and b/test/fixtures/maven-jars/keycloak-core-5.0.0.jar differ diff --git a/test/fixtures/maven-jars/tomcat-http11-4.1.34.jar b/test/fixtures/maven-jars/tomcat-http11-4.1.34.jar new file mode 100644 index 0000000000..28ba8d177c Binary files /dev/null and b/test/fixtures/maven-jars/tomcat-http11-4.1.34.jar differ diff --git a/test/jest/acceptance/snyk-sbom/common.ts b/test/jest/acceptance/snyk-sbom/common.ts new file mode 100644 index 0000000000..d8cef91314 --- /dev/null +++ b/test/jest/acceptance/snyk-sbom/common.ts @@ -0,0 +1,31 @@ +import { createProjectFromFixture } from '../../util/createProject'; +import { runSnykCLI } from '../../util/runSnykCLI'; + +export async function runSnykSbomCliCycloneDxJsonForFixture( + fixtureName: string, + options: string, + env: Record, +): Promise { + const project = await createProjectFromFixture(fixtureName); + + const { code, stdout, stderr } = await runSnykCLI( + `sbom --org=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --format=cyclonedx1.4+json --debug ${options}`, + { + cwd: project.path(), + env, + }, + ); + + if (code) { + console.log(stderr); + } + + expect(code).toEqual(0); + + let sbom; + expect(() => { + sbom = JSON.parse(stdout); + }).not.toThrow(); + + return sbom; +} diff --git a/test/jest/acceptance/snyk-sbom/gradle-options.spec.ts b/test/jest/acceptance/snyk-sbom/gradle-options.spec.ts new file mode 100644 index 0000000000..a95ffb37ec --- /dev/null +++ b/test/jest/acceptance/snyk-sbom/gradle-options.spec.ts @@ -0,0 +1,119 @@ +import { fakeServer } from '../../../acceptance/fake-server'; +import { runSnykSbomCliCycloneDxJsonForFixture } from './common'; + +jest.setTimeout(1000 * 60 * 5); + +describe('snyk sbom: gradle options (mocked server only)', () => { + let server; + let env: Record; + + beforeAll((done) => { + const port = process.env.PORT || process.env.SNYK_PORT || '58584'; + const baseApi = '/api/v1'; + env = { + ...process.env, + SNYK_API: 'http://localhost:' + port + baseApi, + SNYK_HOST: 'http://localhost:' + port, + SNYK_TOKEN: '123456789', + SNYK_DISABLE_ANALYTICS: '1', + }; + server = fakeServer(baseApi, env.SNYK_TOKEN); + server.listen(port, () => { + done(); + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + server.restore(); + }); + + afterAll((done) => { + server.close(() => { + done(); + }); + }); + + test('`sbom --sub-project=` generates an SBOM for selected sub-project', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-multi-project', + '--sub-project=lib', + env, + ); + + expect(sbom.metadata.component.name).toEqual('gradle-multi-project/lib'); + expect(sbom.components.length).toBeGreaterThan(1); + }); + + test('`sbom --gradle-sub-project=` generates an SBOM for selected sub-project', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-multi-project', + '--gradle-sub-project=lib', + env, + ); + + expect(sbom.metadata.component.name).toEqual('gradle-multi-project/lib'); + expect(sbom.components.length).toBeGreaterThan(1); + }); + + test('`sbom --all-sub-projects` generates an SBOM for all sub-projects', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-multi-project', + '--all-sub-projects', + env, + ); + + expect(sbom.metadata.component.name).toEqual('gradle-multi-project'); + const listedComponentNames = sbom.components.map((it) => it.name); + expect(listedComponentNames).toContain('gradle-multi-project'); + expect(listedComponentNames).toContain('gradle-multi-project/lib'); + expect(listedComponentNames).toContain('gradle-multi-project/app'); + }); + + test('`sbom --configuration-matching=` generates an SBOM only for matching configuration', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-configurations', + '--configuration-matching=^runtimeClasspath$', + env, + ); + + const listedComponentNames = sbom.components.map((it) => it.name); + expect(listedComponentNames).toContain('org.jooq:jooq'); + expect(listedComponentNames).toContain('com.google.guava:guava'); + expect(listedComponentNames).not.toContain('junit:junit'); + }); + + test('`sbom --configuration-attributes=[,]...` generates an SBOM only for matching variant', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-configurations', + '--configuration-attributes=snykattr:api', + env, + ); + + // I failed to prepare an example where this works, + // so I will just test here that CLI executes successfully, + // meaning the command does not fail due to unrecognized config. + // To prepare the example I tried to set attribute on a configuration and filter by that, + // but I was always getting a full list of dependencies instead of a subset. + // The same I was getting when trying `snyk test` command. + // I was adding attributes as documented here: https://docs.gradle.org/current/userguide/variant_attributes.html + // I also tried using existing attribute `usage:java-api`, but also no luck. + expect(sbom.metadata.component.name).toEqual('gradle-configurations'); + // const listedComponentNames = sbom.components.map((it) => it.name); + // expect(listedComponentNames).toContain('org.jooq:jooq'); + // expect(listedComponentNames).not.toContain('com.google.guava:guava'); + }); + + test('`sbom --init-script=` applies given init script', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'gradle-configurations', + '--init-script=test-init.gradle', + env, + ); + + const listedComponentNames = sbom.components.map((it) => it.name); + expect(listedComponentNames).toContain( + 'org.apache.logging.log4j:log4j-api', + ); // Dep added by init script + }); +}); diff --git a/test/jest/acceptance/snyk-sbom/maven-options.spec.ts b/test/jest/acceptance/snyk-sbom/maven-options.spec.ts new file mode 100644 index 0000000000..776afd3056 --- /dev/null +++ b/test/jest/acceptance/snyk-sbom/maven-options.spec.ts @@ -0,0 +1,69 @@ +import { fakeServer } from '../../../acceptance/fake-server'; +import { runSnykSbomCliCycloneDxJsonForFixture } from './common'; + +jest.setTimeout(1000 * 60 * 5); + +describe('snyk sbom: maven options (mocked server only)', () => { + let server; + let env: Record; + + beforeAll((done) => { + const port = process.env.PORT || process.env.SNYK_PORT || '58584'; + const baseApi = '/api/v1'; + env = { + ...process.env, + SNYK_API: 'http://localhost:' + port + baseApi, + SNYK_HOST: 'http://localhost:' + port, + SNYK_TOKEN: '123456789', + SNYK_DISABLE_ANALYTICS: '1', + }; + server = fakeServer(baseApi, env.SNYK_TOKEN); + server.listen(port, () => { + done(); + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + server.restore(); + }); + + afterAll((done) => { + server.close(() => { + done(); + }); + }); + + test('`sbom --maven-aggregate-project` generates an SBOM for the multi-module maven project', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'maven-aggregate-project', + '--maven-aggregate-project', + env, + ); + + expect(sbom.metadata.component.name).toEqual('maven-aggregate-project'); + expect(sbom.components.length).toBeGreaterThanOrEqual(11); + }); + + test('`sbom --scan-unmanaged --file=` generates an SBOM for the specific JAR file', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'maven-jars', + '--scan-unmanaged --file=tomcat-http11-4.1.34.jar', + env, + ); + + expect(sbom.metadata.component.name).toMatch(/^snyk-test-maven-jars-/); + expect(sbom.components.length).toBeGreaterThanOrEqual(2); + }); + + test('`sbom --scan-all-unmanaged` generates an SBOM for all the available JARs', async () => { + const sbom = await runSnykSbomCliCycloneDxJsonForFixture( + 'maven-jars', + '--scan-all-unmanaged', + env, + ); + + expect(sbom.metadata.component.name).toMatch(/^snyk-test-maven-jars-/); + expect(sbom.components.length).toBeGreaterThanOrEqual(3); + }); +});