Skip to content

Commit

Permalink
Merge pull request #3501 from snyk/fix/fix-iac-test-exit-code
Browse files Browse the repository at this point in the history
chore: Fix IaC test exit code when issues are found
  • Loading branch information
ofekatr authored Jul 28, 2022
2 parents 53bb090 + 99b8ee0 commit f83eaf9
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 34 deletions.
26 changes: 26 additions & 0 deletions src/lib/iac/test/v2/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { jsonStringifyLargeObject } from '../../../json';
import { IacOrgSettings } from '../../../../cli/commands/test/iac/local-execution/types';
import { SnykIacTestError } from './errors';
import { convertEngineToSarifResults } from './sarif';
import { CustomError } from '../../../errors';

export function buildOutput({
scanResult,
Expand Down Expand Up @@ -91,6 +92,11 @@ function buildTestCommandResultData({
responseData = buildTextOutput({ scanResult, projectName, orgSettings });
}

const isFoundIssues = !!scanResult.results?.vulnerabilities?.length;
if (isFoundIssues) {
throw new FoundIssuesError({ responseData, jsonData, sarifData });
}

return { responseData, jsonData, sarifData };
}

Expand Down Expand Up @@ -141,3 +147,23 @@ function buildTextOutput({

return response;
}

interface FoundIssuesErrorProps {
responseData: string;
jsonData: string;
sarifData: string;
}

export class FoundIssuesError extends CustomError {
public jsonStringifiedResults: string;
public sarifStringifiedResults: string;

constructor(props: FoundIssuesErrorProps) {
super(props.responseData);
this.code = 'VULNS' as any;
this.strCode = 'VULNS';
this.userMessage = props.responseData;
this.jsonStringifiedResults = props.jsonData;
this.sarifStringifiedResults = props.sarifData;
}
}
104 changes: 70 additions & 34 deletions test/jest/unit/cli/commands/test/iac/v2/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { test } from '../../../../../../../../src/cli/commands/test/iac/v2/index
import { Options, TestOptions } from '../../../../../../../../src/lib/types';
import { isValidJSONString } from '../../../../../../acceptance/iac/helpers';
import { IacOrgSettings } from '../../../../../../../../src/cli/commands/test/iac/local-execution/types';
import { FoundIssuesError } from '../../../../../../../../src/lib/iac/test/v2/output';

jest.setTimeout(1000 * 10);

Expand Down Expand Up @@ -70,44 +71,79 @@ describe('test', () => {
.spyOn(orgSettingsLib, 'getIacOrgSettings')
.mockResolvedValue(orgSettings);

it('without any flags outputs the test results', async () => {
const result = await test(['path/to/test'], defaultOptions);
const output = result.getDisplayResults();

expect(output).toContain('Issues');
expect(output).toContain('Medium Severity Issues: ');
expect(output).toContain('High Severity Issues: ');
expect(output).toContain(`Organization: ${orgSettings.meta.org}`);
expect(output).toContain(`Project name: ${path.basename(projectRoot)}`);
expect(output).toContain('Files without issues: 1');
expect(output).toContain('Files with issues: 2');
expect(output).toContain('Total issues: 3');
expect(output).toContain('[ 0 critical, 2 high, 1 medium, 0 low ]');
it('outputs the test results', async () => {
// Arrange
let output: string;

// Act
try {
await test(['path/to/test'], defaultOptions);
} catch (error) {
output = error.message;
}

// Assert
expect(output!).toContain('Issues');
expect(output!).toContain('Medium Severity Issues: ');
expect(output!).toContain('High Severity Issues: ');
expect(output!).toContain(`Organization: ${orgSettings.meta.org}`);
expect(output!).toContain(`Project name: ${path.basename(projectRoot)}`);
expect(output!).toContain('Files without issues: 1');
expect(output!).toContain('Files with issues: 2');
expect(output!).toContain('Total issues: 3');
expect(output!).toContain('[ 0 critical, 2 high, 1 medium, 0 low ]');
});

it('with `--json` flag', async () => {
const result = (
await test(['path/to/test'], {
...defaultOptions,
json: true,
})
).getJsonResult();
describe('with issues', () => {
it('throws the expected error', async () => {
// Act + Assert
await expect(test(['path/to/test'], defaultOptions)).rejects.toThrowError(
FoundIssuesError,
);
});
});

expect(isValidJSONString(result)).toBe(true);
expect(result).toContain(`"ok": false`);
describe('with `--json` flag', () => {
it('outputs the test results in JSON format', async () => {
// Arrange
let result: string;

// Act
try {
await test(['path/to/test'], {
...defaultOptions,
json: true,
});
} catch (error) {
result = error.jsonStringifiedResults;
}

// Assert
expect(isValidJSONString(result!)).toBe(true);
expect(result!).toContain(`"ok": false`);
});
});

it('with `--sarif` flag', async () => {
const result = (
await test(['path/to/test'], {
...defaultOptions,
sarif: true,
})
).getSarifResult();

expect(isValidJSONString(result)).toBe(true);
expect(result).toContain(
`"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"`,
);
describe('with `--sarif` flag', () => {
it('outputs the test results in SARIF format', async () => {
// Arrange
let result: string;

// Act
try {
await test(['path/to/test'], {
...defaultOptions,
sarif: true,
});
} catch (error) {
result = error.sarifStringifiedResults;
}

// Assert
expect(isValidJSONString(result!)).toBe(true);
expect(result!).toContain(
`"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"`,
);
});
});
});

0 comments on commit f83eaf9

Please sign in to comment.