Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cliv2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.34.0
github.com/snyk/cli-extension-agent-scan v0.0.0-20260312152423-bc36193ecaa8
github.com/snyk/cli-extension-ai-bom v0.0.0-20260303103300-ea9a5a717cbb
github.com/snyk/cli-extension-ai-bom v0.0.0-20260312142851-4a3ed1abe853
github.com/snyk/cli-extension-dep-graph v0.27.0
github.com/snyk/cli-extension-iac v0.0.0-20260206082514-00c443ccee80
github.com/snyk/cli-extension-iac-rules v0.0.0-20260206080712-9cbb5f95465d
Expand Down Expand Up @@ -207,7 +207,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/samber/lo v1.52.0 // indirect
github.com/samber/lo v1.53.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down
8 changes: 4 additions & 4 deletions cliv2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,8 @@ github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+e
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
Expand All @@ -533,8 +533,8 @@ github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28Jjd
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
github.com/snyk/cli-extension-agent-scan v0.0.0-20260312152423-bc36193ecaa8 h1:Ky+ZlFDH26kJ9QXeZPhQMG+1+M1EtNfa1BHBHL0zkh8=
github.com/snyk/cli-extension-agent-scan v0.0.0-20260312152423-bc36193ecaa8/go.mod h1:+Znlgu2v7sOTNAVjsoldFjDZUIo8tpdKnFlMptZHzz0=
github.com/snyk/cli-extension-ai-bom v0.0.0-20260303103300-ea9a5a717cbb h1:ZgBgiMMtY8XK8WJZpmnlt08wKPMitDEU1TS99OK+k8A=
github.com/snyk/cli-extension-ai-bom v0.0.0-20260303103300-ea9a5a717cbb/go.mod h1:eIq61+KliPjLwhaAZT87FfeyfK/4mJaGP0wqyFtf8pQ=
github.com/snyk/cli-extension-ai-bom v0.0.0-20260312142851-4a3ed1abe853 h1:6CTQnacsK4/AXtijRhtwg/6TTGMCPwEL9ivUE7FxXn0=
github.com/snyk/cli-extension-ai-bom v0.0.0-20260312142851-4a3ed1abe853/go.mod h1:RnMP+tFTeKygfXSx7z+heyMZoOps67u5HFytjptHjuk=
github.com/snyk/cli-extension-dep-graph v0.27.0 h1:yVy/QFeKdQUVL0PHZtPDSTk7icY2QrQPGKPjMFoCJwQ=
github.com/snyk/cli-extension-dep-graph v0.27.0/go.mod h1:JQ37TXutjFa585Ocak1jfBRN6+QPppmFIlJ6+nrfgaY=
github.com/snyk/cli-extension-iac v0.0.0-20260206082514-00c443ccee80 h1:JHbnSkgGc2oUejjzdWdeTghl0BZV7QamcRuyh7ornVo=
Expand Down
29 changes: 29 additions & 0 deletions test/acceptance/fake-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,35 @@ export const fakeServer = (basePath: string, snykToken: string): FakeServer => {
});
});

// AI-BOM CLI policy test (aibom test command)
app.post(
`/api/hidden/orgs/:orgId/ai_boms/cli_policy_test`,
(req: express.Request, res: express.Response) => {
res.status(200);
res.setHeader('Content-Type', 'application/vnd.api+json');
res.send({
jsonapi: { version: '1.0' },
data: {
id: 'cli-policy-test-run-1',
type: 'test',
attributes: {
issues: [
{
id: 'issue-1',
description: 'Disallowed model',
severity: 'high',
policy_id: 'pol-123',
state: 'open',
source: 'policy',
remediation_advice: 'Use an allowed model',
},
],
},
},
});
},
);

// Unified Test API endpoints for uv acceptance tests
const testJobId = 'aaaaaaaa-bbbb-cccc-dddd-000000000001';
const testId = 'aaaaaaaa-bbbb-cccc-dddd-000000000002';
Expand Down
80 changes: 80 additions & 0 deletions test/jest/acceptance/snyk-aibom/aibom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '../../../acceptance/fake-server';
import { getServerPort } from '../../util/getServerPort';
import { resolve } from 'path';
import * as os from 'os';

jest.setTimeout(1000 * 60 * 5);

Expand All @@ -14,6 +15,8 @@ function aiBomRestEndpointRequests(requests: Request[]): string[] {
for (const request of requests) {
if (request.url.includes('/ai_boms/upload')) {
res.push(`${request.method}:/ai_boms/upload`);
} else if (request.url.includes('cli_policy_test')) {
res.push(`${request.method}:/ai_boms/cli_policy_test`);
} else if (request.url.includes('/ai_boms')) {
res.push(`${request.method}:/ai_boms`);
} else if (request.url.includes('/ai_bom_jobs')) {
Expand Down Expand Up @@ -263,4 +266,81 @@ describe('snyk aibom (mocked servers only)', () => {
expect(stdout).toContain('Authentication error (SNYK-0005)');
});
});

describe('snyk aibom test', () => {
test('`aibom test` runs policy test and returns open issues (exit code 1)', async () => {
expect(server.getRequests().length).toEqual(0);
const { code, stdout } = await runSnykCLI(
`aibom test ${pythonChatbotProject} --experimental`,
{
env,
},
);
expect(code).toEqual(1);

const aiBomRequests = aiBomRestEndpointRequests(server.getRequests());
expect(aiBomRequests).toContain('POST:/ai_boms/cli_policy_test');
expect(aiBomRequests).toEqual([
'POST:/ai_boms',
'POST:/upload_revisions',
'POST:/upload_revisions/:uploadRevisionId/files',
'PATCH:/upload_revisions/:uploadRevisionId',
'POST:/ai_boms',
'GET:/ai_bom_jobs',
'GET:/ai_boms',
'POST:/ai_boms/cli_policy_test',
]);

expect(stdout).toContain('AI BOM policy test');
expect(stdout).toContain('Test summary');
expect(stdout).toContain('Disallowed model');
expect(stdout).toContain('Open');
});

test('`aibom test` requires --experimental', async () => {
const { code, stdout } = await runSnykCLI(
`aibom test ${pythonChatbotProject}`,
{
env,
},
);
expect(code).toEqual(2);
expect(stdout).toContain('Command is experimental (SNYK-CLI-0015)');
});

test('`aibom test` fails if api is unavailable', async () => {
expect(server.getRequests().length).toEqual(0);
server.setStatusCode(404);
const { code } = await runSnykCLI(
`aibom test ${pythonChatbotProject} --experimental`,
{
env,
},
);
expect(code).toEqual(2);
});

test('`aibom test` with --json-file-output writes results to file', async () => {
const outputPath = resolve(
os.tmpdir(),
`aibom-test-output-${Date.now()}.json`,
);
const { code } = await runSnykCLI(
`aibom test ${pythonChatbotProject} --experimental --json-file-output=${outputPath}`,
{
env,
},
);
expect(code).toEqual(1);
const fs = await import('fs');
const content = fs.readFileSync(outputPath, 'utf8');
const result = JSON.parse(content);
expect(result.data).toBeDefined();
expect(result.data.attributes.issues).toHaveLength(1);
expect(result.data.attributes.issues[0].description).toEqual(
'Disallowed model',
);
fs.unlinkSync(outputPath);
});
});
});
Loading