Skip to content

Commit

Permalink
feat: add report upload to code test
Browse files Browse the repository at this point in the history
  • Loading branch information
patricia-v committed Feb 20, 2023
1 parent bff04f4 commit 047d667
Show file tree
Hide file tree
Showing 8 changed files with 1,960 additions and 50 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -63,7 +63,7 @@
"@sentry/node": "^7.34.0",
"@snyk/cli-interface": "2.11.0",
"@snyk/cloud-config-parser": "^1.14.5",
"@snyk/code-client": "^4.15.0",
"@snyk/code-client": "^4.16.0",
"@snyk/dep-graph": "^1.27.1",
"@snyk/docker-registry-v2-client": "^2.7.3",
"@snyk/fix": "file:packages/snyk-fix",
Expand Down
66 changes: 52 additions & 14 deletions src/lib/plugins/sast/analysis.ts
Expand Up @@ -2,14 +2,16 @@ import {
analyzeFolders,
AnalysisSeverity,
MAX_FILE_SIZE,
FileAnalysis,
AnalysisResultSarif,
} from '@snyk/code-client';
import { ReportingDescriptor, Result } from 'sarif';
import { SEVERITY } from '../../snyk-test/legacy';
import { getAuthHeader } from '../../api-token';
import config from '../../config';
import { spinner } from '../../spinner';
import { Options } from '../../types';
import { SastSettings, Log } from './types';
import { SastSettings, Log, CodeTestResults } from './types';
import { analysisProgressUpdate } from './utils';
import {
FeatureNotSupportedBySnykCodeError,
Expand All @@ -23,12 +25,12 @@ import { getCodeClientProxyUrl } from '../../code-config';

const debug = debugLib('snyk-code');

export async function getCodeAnalysisAndParseResults(
export async function getCodeTestResults(
root: string,
options: Options,
sastSettings: SastSettings,
requestId: string,
): Promise<Log | null> {
): Promise<CodeTestResults | null> {
await spinner.clearAll();
analysisProgressUpdate();
const codeAnalysis = await getCodeAnalysis(
Expand All @@ -38,15 +40,23 @@ export async function getCodeAnalysisAndParseResults(
requestId,
);
spinner.clearAll();
return parseSecurityResults(codeAnalysis);

if (!codeAnalysis) {
return null;
}

return {
reportResults: codeAnalysis.reportResults,
analysisResults: codeAnalysis.analysisResults as AnalysisResultSarif,
};
}

async function getCodeAnalysis(
root: string,
options: Options,
sastSettings: SastSettings,
requestId: string,
): Promise<Log | null> {
): Promise<FileAnalysis | null> {
const isLocalCodeEngineEnabled = isLocalCodeEngine(sastSettings);
if (isLocalCodeEngineEnabled) {
validateLocalCodeEngineUrl(sastSettings.localCodeEngine.url);
Expand Down Expand Up @@ -82,9 +92,21 @@ async function getCodeAnalysis(
? severityToAnalysisSeverity(options.severityThreshold)
: AnalysisSeverity.info;
const result = await analyzeFolders({
connection: { baseURL, sessionToken, source, requestId },
connection: {
baseURL,
sessionToken,
source,
requestId,
},
analysisOptions: { severity },
fileOptions: { paths: [root] },
...(options.report && {
reportOptions: {
enabled: options.report ?? false,
projectName: options['project-name'],
targetRef: options['target-reference'],
},
}),
analysisContext: {
initiator: 'CLI',
flow: source,
Expand All @@ -110,10 +132,30 @@ async function getCodeAnalysis(
);
}

if (result?.analysisResults.type === 'sarif') {
return result.analysisResults.sarif;
if (!result || result?.analysisResults.type !== 'sarif') {
return null;
}
return null;

result.analysisResults.sarif = parseSecurityResults(
result.analysisResults.sarif,
);

// Filter ignored issues when using report
if (options.report) {
result.analysisResults.sarif = filterIgnoredIssues(
result.analysisResults.sarif,
);
}

return result;
}

function filterIgnoredIssues(codeAnalysis: Log): Log {
const results = codeAnalysis.runs[0].results;
codeAnalysis.runs[0].results = results?.filter(
(rule) => (rule.suppressions?.length ?? 0) === 0,
);
return codeAnalysis;
}

function severityToAnalysisSeverity(severity: SEVERITY): AnalysisSeverity {
Expand All @@ -128,13 +170,9 @@ function severityToAnalysisSeverity(severity: SEVERITY): AnalysisSeverity {
return severityLevel[severity];
}

function parseSecurityResults(codeAnalysis: Log | null): Log | null {
function parseSecurityResults(codeAnalysis: Log): Log {
let securityRulesMap;

if (!codeAnalysis) {
return codeAnalysis;
}

const rules = codeAnalysis.runs[0].tool.driver.rules;
const results = codeAnalysis.runs[0].results;

Expand Down
41 changes: 32 additions & 9 deletions src/lib/plugins/sast/format/output-format.ts
@@ -1,27 +1,29 @@
import { EOL } from 'os';
import * as Sarif from 'sarif';
import * as Debug from 'debug';
import chalk from 'chalk';
import { icon, color } from '../../../theme';
import { colorTextBySeverity, SEVERITY } from '../../../snyk-test/common';
import { rightPadWithSpaces } from '../../../right-pad';
import { Options } from '../../../types';
import { Log } from '../types';
import { CodeTestResults } from '../types';

const debug = Debug('code-output');

export function getCodeDisplayedOutput(
codeTest: Log,
testResults: CodeTestResults,
meta: string,
prefix: string,
): string {
let issues: { [index: string]: string[] } = {};

if (codeTest.runs[0].results) {
const results: Sarif.Result[] = codeTest.runs[0].results;
const sarif = testResults.analysisResults.sarif;
if (sarif.runs[0].results) {
const results: Sarif.Result[] = sarif.runs[0].results;

const rulesMap: {
[ruleId: string]: Sarif.ReportingDescriptor;
} = getRulesMap(codeTest.runs[0].tool.driver.rules || []);
} = getRulesMap(sarif.runs[0].tool.driver.rules || []);

issues = getIssues(results, rulesMap);
}
Expand All @@ -31,7 +33,7 @@ export function getCodeDisplayedOutput(
const summaryOKText = color.status.success(`${icon.VALID} Test completed`);
const codeIssueSummary = getCodeIssuesSummary(issues);

return (
let summary =
prefix +
issuesText +
'\n' +
Expand All @@ -42,8 +44,15 @@ export function getCodeDisplayedOutput(
chalk.bold('Summary:') +
'\n\n' +
codeIssueSummary +
'\n\n'
);
'\n\n';

if (testResults.reportResults) {
summary +=
getCodeReportDisplayedOutput(testResults.reportResults.reportUrl) +
'\n\n';
}

return summary;
}

function getCodeIssuesSummary(issues: { [index: string]: string[] }): string {
Expand Down Expand Up @@ -106,7 +115,10 @@ function getIssues(
const artifactLocationUri = location.artifactLocation.uri;
const startLine = location.region.startLine;
const text = res.message.text;
const title = ruleIdSeverityText;
let title = ruleIdSeverityText;
if (res.fingerprints?.['identity']) {
title += `\n ID: ${res.fingerprints['identity']}`;
}
const path = ` Path: ${artifactLocationUri}, line ${startLine}`;
const info = ` Info: ${text}`;
acc[severity.toLowerCase()].push(`${title} \n ${path} \n ${info}\n\n`);
Expand Down Expand Up @@ -162,3 +174,14 @@ export function getMeta(options: Options, path: string): string {
export function getPrefix(path: string): string {
return chalk.bold.white('\nTesting ' + path + ' ...\n\n');
}

export function getCodeReportDisplayedOutput(reportUrl: string): string {
return (
chalk.bold('Code Report Complete') +
EOL +
EOL +
'Your test results are available at:' +
EOL +
chalk.bold(reportUrl)
);
}
17 changes: 8 additions & 9 deletions src/lib/plugins/sast/index.ts
@@ -1,7 +1,7 @@
import chalk from 'chalk';
import * as debugLib from 'debug';
import { v4 as uuidv4 } from 'uuid';
import { getCodeAnalysisAndParseResults } from './analysis';
import { getCodeTestResults } from './analysis';
import { getSastSettings } from './settings';
import {
getCodeDisplayedOutput,
Expand Down Expand Up @@ -33,29 +33,28 @@ export const codePlugin: EcosystemPlugin = {
// Currently code supports only one path
const path = paths[0];

const sarifTypedResult = await getCodeAnalysisAndParseResults(
const testResults = await getCodeTestResults(
path,
options,
sastSettings,
requestId,
);

if (!sarifTypedResult) {
if (!testResults) {
throw new NoSupportedSastFiles();
}
const numOfIssues = sarifTypedResult!.runs?.[0].results?.length || 0;

const sarifTypedResult = testResults?.analysisResults?.sarif;

const numOfIssues = sarifTypedResult.runs?.[0].results?.length || 0;
analytics.add('sast-issues-found', numOfIssues);
let newOrg = options.org;
if (!newOrg && sastSettings.org) {
newOrg = sastSettings.org;
}
const meta = getMeta({ ...options, org: newOrg }, path);
const prefix = getPrefix(path);
let readableResult = getCodeDisplayedOutput(
sarifTypedResult!,
meta,
prefix,
);
let readableResult = getCodeDisplayedOutput(testResults, meta, prefix);
if (numOfIssues > 0 && options['no-markdown']) {
sarifTypedResult.runs?.[0].results?.forEach(({ message }) => {
delete message.markdown;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/plugins/sast/types.ts
@@ -1,4 +1,5 @@
export { Log, Tool, Result } from 'sarif';
import { AnalysisResultSarif, ReportResult } from '@snyk/code-client';

interface LocalCodeEngine {
enabled: boolean;
Expand All @@ -20,3 +21,8 @@ export interface TrackUsageResponse {
code?: number;
userMessage?: string;
}

export interface CodeTestResults {
reportResults?: ReportResult['uploadResult'];
analysisResults: AnalysisResultSarif;
}

0 comments on commit 047d667

Please sign in to comment.