Skip to content

Commit 24e96c3

Browse files
committed
feat: add sbom monitor command
1 parent 22fadf7 commit 24e96c3

File tree

5 files changed

+153
-3
lines changed

5 files changed

+153
-3
lines changed

cliv2/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/snyk/cli-extension-dep-graph v0.0.0-20250321153619-9390ab5e348e
1414
github.com/snyk/cli-extension-iac v0.0.0-20250417092850-bfa35aacddfc
1515
github.com/snyk/cli-extension-iac-rules v0.0.0-20250227121450-6e14346dbd1a
16-
github.com/snyk/cli-extension-sbom v0.0.0-20241016065306-0df2be5b3b8f
16+
github.com/snyk/cli-extension-sbom v0.0.0-20250422133603-a5ae6fdf0934
1717
github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7
1818
github.com/snyk/error-catalog-golang-public v0.0.0-20250310083934-7ac627e3451f
1919
github.com/snyk/go-application-framework v0.0.0-20250423203408-8884fd0a504f

cliv2/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,8 +800,8 @@ github.com/snyk/cli-extension-iac v0.0.0-20250417092850-bfa35aacddfc h1:Kg1f+GX1
800800
github.com/snyk/cli-extension-iac v0.0.0-20250417092850-bfa35aacddfc/go.mod h1:7O90So5PPqTRMpcs8BY14+jX+J8BixqZQyXQiRZW7ws=
801801
github.com/snyk/cli-extension-iac-rules v0.0.0-20250227121450-6e14346dbd1a h1:SJ+Ts7e1EYcGJXeENR5inTGwPNRlNVgmMN2itO3+yj8=
802802
github.com/snyk/cli-extension-iac-rules v0.0.0-20250227121450-6e14346dbd1a/go.mod h1:IqfQCIkyC26mkwa+aM6d6yxIh5+tCm4fSQG+Ogq3Qbc=
803-
github.com/snyk/cli-extension-sbom v0.0.0-20241016065306-0df2be5b3b8f h1:dlL+f+5sjHj4JCzW/Evl1x9UREXLyc3M4KjoZvQx0Bs=
804-
github.com/snyk/cli-extension-sbom v0.0.0-20241016065306-0df2be5b3b8f/go.mod h1:5CaY1bgvJY/uoG/1plLOf8T8o9AkwoBIGvw34RfRLZw=
803+
github.com/snyk/cli-extension-sbom v0.0.0-20250422133603-a5ae6fdf0934 h1:0RCTH9C0zaTrnqpKLaLXTmP7suwWEHBNVwQSaR8Aifo=
804+
github.com/snyk/cli-extension-sbom v0.0.0-20250422133603-a5ae6fdf0934/go.mod h1:Q8dmRgcpHTk711dkLVtpkTF5RvLkQVcExGuv1cyx/zU=
805805
github.com/snyk/code-client-go v1.20.1 h1:38nEGzrQIh/aVLjR99jiTUQM0sL9SQAvhMfZGmd9G0w=
806806
github.com/snyk/code-client-go v1.20.1/go.mod h1:WH6lNkJc785hfXmwhixxWHix3O6z+1zwz40oK8vl/zg=
807807
github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7 h1:/2+2piwQtB9fEJCkXEOjboZjY+77lQfnvqBZ/60xNHk=

test/acceptance/fake-server.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const featureFlagDefaults = (): Map<string, boolean> => {
1515
['iacNewEngine', false],
1616
['containerCliAppVulnsEnabled', true],
1717
['enablePnpmCli', false],
18+
['sbomMonitorBeta', false],
1819
]);
1920
};
2021

@@ -791,6 +792,20 @@ export const fakeServer = (basePath: string, snykToken: string): FakeServer => {
791792
res.send(JSON.parse(body));
792793
});
793794

795+
app.post(`/hidden/orgs/:orgId/sboms/convert`, (req, res) => {
796+
if (req.url.includes('/orgs/badaabad-badb-badb-badb-badbadbadbad/')) {
797+
res
798+
.status(400)
799+
.send(`{"errors":[{"title":"Bad Request","detail":"invalid SBOM"}]}`);
800+
}
801+
802+
const body = fs.readFileSync(
803+
path.resolve(getFixturePath('sbom'), 'sbom-convert-response.json'),
804+
'utf8',
805+
);
806+
res.send(JSON.parse(body));
807+
});
808+
794809
app.post(
795810
basePath.replace('v1', 'hidden') + '/orgs/:org/sbom',
796811
express.json(),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"scanResults":[{"name":"npm:development","facts":[{"type":"depGraph","data":{"graph":{"nodes":[{"deps":[{"nodeId":"[email protected]"}],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[{"nodeId":"[email protected]"},{"nodeId":"[email protected]"}],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[{"nodeId":"[email protected]"},{"nodeId":"[email protected]"},{"nodeId":"[email protected]"}],"nodeId":"root-node","pkgId":"[email protected]"},{"deps":[{"nodeId":"[email protected]"}],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[{"nodeId":"[email protected]"}],"nodeId":"[email protected]","pkgId":"[email protected]"},{"deps":[],"nodeId":"[email protected]","pkgId":"[email protected]"}],"rootNodeId":"root-node"},"pkgManager":{"name":"npm"},"pkgs":[{"id":"[email protected]","info":{"name":"development","version":"1.0.0"}},{"id":"[email protected]","info":{"name":"brace-expansion","version":"1.1.11"}},{"id":"[email protected]","info":{"name":"yallist","version":"4.0.0"}},{"id":"[email protected]","info":{"name":"minimatch","version":"3.0.4"}},{"id":"[email protected]","info":{"name":"balanced-match","version":"1.0.2"}},{"id":"[email protected]","info":{"name":"concat-map","version":"0.0.1"}},{"id":"[email protected]","info":{"name":"semver","version":"7.3.5"}},{"id":"[email protected]","info":{"name":"lru-cache","version":"6.0.0"}},{"id":"[email protected]","info":{"name":"fuzzball","version":"1.4.0"}}],"schemaVersion":"1.3.0"}}],"target":{"remoteUrl":""},"identity":{"type":"npm"}}],"warnings":null}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { runSnykCLI } from '../../util/runSnykCLI';
2+
import { fakeServer } from '../../../acceptance/fake-server';
3+
import { getServerPort } from '../../util/getServerPort';
4+
import { getFixturePath } from '../../util/getFixturePath';
5+
import * as path from 'path';
6+
7+
jest.setTimeout(1000 * 60);
8+
9+
describe('snyk sbom monitor (mocked server only)', () => {
10+
let server: ReturnType<typeof fakeServer>;
11+
let env: Record<string, string>;
12+
13+
beforeAll((done) => {
14+
const port = getServerPort(process);
15+
const baseApi = '/v1';
16+
env = {
17+
...process.env,
18+
SNYK_API: 'http://localhost:' + port + baseApi,
19+
SNYK_API_REST_URL: 'http://localhost:' + port + baseApi,
20+
SNYK_HOST: 'http://localhost:' + port,
21+
SNYK_TOKEN: '123456789',
22+
SNYK_DISABLE_ANALYTICS: '1',
23+
};
24+
server = fakeServer(baseApi, env.SNYK_TOKEN);
25+
server.listen(port, () => {
26+
done();
27+
});
28+
});
29+
30+
beforeEach(() => {
31+
server.setFeatureFlag('sbomMonitorBeta', true);
32+
});
33+
34+
afterEach(() => {
35+
jest.resetAllMocks();
36+
server.restore();
37+
});
38+
39+
afterAll((done) => {
40+
server.close(() => {
41+
done();
42+
});
43+
});
44+
45+
test('monitor success', async () => {
46+
const fileToTest = path.resolve(
47+
getFixturePath('sbom'),
48+
'npm-sbom-cdx15.json',
49+
);
50+
51+
const { code, stdout, stderr } = await runSnykCLI(
52+
`sbom monitor --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --experimental --file ${fileToTest}`,
53+
{ env },
54+
);
55+
56+
expect(code).toEqual(0);
57+
58+
expect(stderr).toEqual('');
59+
expect(stdout).toContain(`Monitoring 'test-project'...`);
60+
expect(stdout).toContain(
61+
`Explore this snapshot at http://example-url/project/project-public-id/history/snapshot-public-id`,
62+
);
63+
expect(stdout).toContain(
64+
`Notifications about newly disclosed issues related to these dependencies will be emailed to you.`,
65+
);
66+
});
67+
68+
test('monitor bad SBOM', async () => {
69+
const fileToTest = path.resolve(getFixturePath('sbom'), 'bad-sbom.json');
70+
71+
const { code, stdout, stderr } = await runSnykCLI(
72+
`sbom monitor --org badaabad-badb-badb-badb-badbadbadbad --experimental --file ${fileToTest}`,
73+
{ env },
74+
);
75+
76+
expect(code).toEqual(2);
77+
78+
expect(stderr).toEqual('');
79+
expect(stdout).toContain(`Bad Request (SNYK-0003)`);
80+
expect(stdout).toContain(`invalid SBOM`);
81+
});
82+
83+
test('missing experimental flag', async () => {
84+
const fileToTest = path.resolve(
85+
getFixturePath('sbom'),
86+
'npm-sbom-cdx15.json',
87+
);
88+
89+
const { stdout, stderr } = await runSnykCLI(
90+
`sbom monitor --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --file ${fileToTest}`,
91+
{ env },
92+
);
93+
94+
expect(stdout).toMatch(
95+
'Flag `--experimental` is required to execute this command.',
96+
);
97+
98+
expect(stderr).toEqual('');
99+
});
100+
101+
test('missing file flag', async () => {
102+
const { stdout, stderr } = await runSnykCLI(
103+
`sbom monitor --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --experimental`,
104+
{ env },
105+
);
106+
107+
expect(stdout).toContainText(
108+
'Flag `--file` is required to execute this command. Value should point to a valid SBOM document.',
109+
);
110+
111+
expect(stderr).toEqual('');
112+
});
113+
114+
test('feature flag sbomMonitorBeta is disabled', async () => {
115+
const fileToTest = path.resolve(
116+
getFixturePath('sbom'),
117+
'npm-sbom-cdx15.json',
118+
);
119+
120+
server.setFeatureFlag('sbomMonitorBeta', false);
121+
122+
const { code, stdout, stderr } = await runSnykCLI(
123+
`sbom monitor --org aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee --experimental --file ${fileToTest}`,
124+
{ env },
125+
);
126+
127+
expect(code).toEqual(2);
128+
129+
expect(stderr).toEqual('');
130+
expect(stdout).toContain(
131+
`The feature you are trying to use is not available for your organization.`,
132+
);
133+
});
134+
});

0 commit comments

Comments
 (0)