@contrast/contrast 2.1.6 → 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cliConstants.js +1 -0
- package/dist/common/baseRequest.js +1 -0
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +1 -0
- package/dist/index.js +4 -3
- package/dist/sarif/generateSarif.js +17 -0
- package/dist/sarif/sarifAssessRequests.js +39 -0
- package/dist/sarif/sarifIastClient.js +65 -0
- package/dist/sarif/sarifSCARequests.js +25 -0
- package/dist/sarif/sarifScaClient.js +35 -0
- package/dist/sarif/sarifWriter.js +156 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +9 -0
- package/dist/scaAnalysis/java/analysis.js +15 -1
- package/package.json +4 -3
package/dist/cliConstants.js
CHANGED
|
@@ -514,6 +514,7 @@ const mainUsageGuide = commandLineUsage([
|
|
|
514
514
|
{ name: i18n.__('lambdaName'), summary: i18n.__('helpLambdaSummary') },
|
|
515
515
|
{ name: i18n.__('helpName'), summary: i18n.__('helpSummary') },
|
|
516
516
|
{ name: i18n.__('learnName'), summary: i18n.__('helpLearnSummary') },
|
|
517
|
+
// { name: i18n.__('sarifName'), summary: i18n.__('sarifSummary') },
|
|
517
518
|
{
|
|
518
519
|
name: i18n.__('configGenerate'),
|
|
519
520
|
summary: i18n.__('configGenerateSummary')
|
|
@@ -17,7 +17,7 @@ export const HIGH = 'HIGH';
|
|
|
17
17
|
export const CRITICAL = 'CRITICAL';
|
|
18
18
|
// App
|
|
19
19
|
export const APP_NAME = 'contrast';
|
|
20
|
-
const APP_VERSION = '2.1.
|
|
20
|
+
const APP_VERSION = '2.1.7';
|
|
21
21
|
export const TIMEOUT = 120000;
|
|
22
22
|
export const CRITICAL_PRIORITY = 1;
|
|
23
23
|
export const HIGH_PRIORITY = 2;
|
|
@@ -126,6 +126,7 @@ export const en_locales = () => {
|
|
|
126
126
|
configName: 'config',
|
|
127
127
|
helpName: 'help',
|
|
128
128
|
learnName: 'learn',
|
|
129
|
+
sarifName: 'sarif',
|
|
129
130
|
configGenerate: 'generate-config',
|
|
130
131
|
helpLearnSummary: 'Launches Contrast’s Secure Code Learning Hub.',
|
|
131
132
|
configGenerateSummary: 'Generates contrast configuration yaml file.',
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { processLearn } from './commands/learn/processLearn.js';
|
|
|
14
14
|
import { sendTelemetryConfigAsConfObj } from './telemetry/telemetry.js';
|
|
15
15
|
import { findCommandOnError } from './common/errorHandling.js';
|
|
16
16
|
import { processAssess } from './assess/index.js';
|
|
17
|
+
import { processSarif } from './sarif/generateSarif.js';
|
|
17
18
|
import { logInfo } from './common/logging.js';
|
|
18
19
|
import { generateYamlConfiguration } from './generateYaml/index.js';
|
|
19
20
|
const config = localConfig(APP_NAME, getAppVersion());
|
|
@@ -70,15 +71,15 @@ const start = async () => {
|
|
|
70
71
|
if (command === 'assess') {
|
|
71
72
|
return await processAssess(config, argvMain);
|
|
72
73
|
}
|
|
73
|
-
if (command === 'assess') {
|
|
74
|
-
return await processAssess(config, argvMain);
|
|
75
|
-
}
|
|
76
74
|
if (command === 'generate-config') {
|
|
77
75
|
return await generateYamlConfiguration(config, argvMain);
|
|
78
76
|
}
|
|
79
77
|
if (command === 'fingerprint') {
|
|
80
78
|
return await processFingerprint(config, argvMain);
|
|
81
79
|
}
|
|
80
|
+
if (command === 'sarif') {
|
|
81
|
+
return processSarif(config, argvMain);
|
|
82
|
+
}
|
|
82
83
|
if (command === 'learn') {
|
|
83
84
|
return processLearn();
|
|
84
85
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAuditConfig } from '../audit/auditConfig.js';
|
|
2
|
+
import { scaCVES } from './sarifScaClient.js';
|
|
3
|
+
import { iastData } from './sarifIastClient.js';
|
|
4
|
+
import { writeCombinedSarif } from './sarifWriter.js';
|
|
5
|
+
// This filename could be set by a customer
|
|
6
|
+
// Defaulted to be ingested in a GH workflow
|
|
7
|
+
const outputFileName = 'contrast.sarif.json';
|
|
8
|
+
export const processSarif = async (contrastConf, argvMain) => {
|
|
9
|
+
console.log('Generating SARIF export');
|
|
10
|
+
let config = await getAuditConfig(contrastConf, 'audit', argvMain);
|
|
11
|
+
const scaVulns = await scaCVES(config);
|
|
12
|
+
console.log(`Found ${scaVulns.length} SCA vulnerabilities`);
|
|
13
|
+
const iastVulns = await iastData(config);
|
|
14
|
+
console.log(`Found ${iastVulns.length} IAST vulnerabilities`);
|
|
15
|
+
writeCombinedSarif(iastVulns, scaVulns, outputFileName);
|
|
16
|
+
console.log('SARIF generated');
|
|
17
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { buildBaseRequestOptions, ErrorType } from '../common/baseRequest.js';
|
|
2
|
+
import { got } from 'got';
|
|
3
|
+
const getAssessTraceIdsUrl = (config, offset, resultsLimit) => {
|
|
4
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/orgtraces/filter/?offset=${offset}&limit=${resultsLimit}`;
|
|
5
|
+
};
|
|
6
|
+
export function getAssessTraceIds(config, offset, resultsLimit) {
|
|
7
|
+
const options = buildBaseRequestOptions(config, ErrorType.GENERIC);
|
|
8
|
+
options.url = getAssessTraceIdsUrl(config, offset, resultsLimit);
|
|
9
|
+
options.json = {
|
|
10
|
+
applicationID: config.applicationId,
|
|
11
|
+
// metadataFilters: sessionMetadata,
|
|
12
|
+
severities: [config.severity]
|
|
13
|
+
};
|
|
14
|
+
return got.post(options);
|
|
15
|
+
}
|
|
16
|
+
const getAssessTraceDetailsUrl = (config, traceId) => {
|
|
17
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/traces/${config.applicationId}/filter/${traceId}?expand=events,request,sink,${traceId},skip_links`;
|
|
18
|
+
};
|
|
19
|
+
export function getAssessTraceDetails(config, traceId) {
|
|
20
|
+
const options = buildBaseRequestOptions(config, ErrorType.GENERIC);
|
|
21
|
+
options.url = getAssessTraceDetailsUrl(config, traceId);
|
|
22
|
+
return got.get(options);
|
|
23
|
+
}
|
|
24
|
+
const getAssessTraceEventsUrl = (config, traceId, eventId) => {
|
|
25
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/traces/${traceId}/events/${eventId}/details`;
|
|
26
|
+
};
|
|
27
|
+
export function getAssessTraceEvents(config, traceId, eventId) {
|
|
28
|
+
const options = buildBaseRequestOptions(config, ErrorType.GENERIC);
|
|
29
|
+
options.url = getAssessTraceEventsUrl(config, traceId, eventId);
|
|
30
|
+
return got.get(options);
|
|
31
|
+
}
|
|
32
|
+
const getAssessTraceRoutesUrl = (config, traceId) => {
|
|
33
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/traces/${config.applicationId}/trace/${traceId}/routes`;
|
|
34
|
+
};
|
|
35
|
+
export function getAssessTraceRoutes(config, traceId) {
|
|
36
|
+
const options = buildBaseRequestOptions(config, ErrorType.GENERIC);
|
|
37
|
+
options.url = getAssessTraceRoutesUrl(config, traceId);
|
|
38
|
+
return got.get(options);
|
|
39
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getAssessTraceDetails, getAssessTraceEvents, getAssessTraceIds, getAssessTraceRoutes } from './sarifAssessRequests.js';
|
|
2
|
+
const resultsLimit = 50;
|
|
3
|
+
let offset = 0;
|
|
4
|
+
export const iastData = async (config) => {
|
|
5
|
+
const filteredTraceIds = await getTraceIds(config, offset, resultsLimit);
|
|
6
|
+
console.log(`Found ${filteredTraceIds.length} traces`);
|
|
7
|
+
let completeTraces = [];
|
|
8
|
+
for (const traceId of filteredTraceIds) {
|
|
9
|
+
const traceDetails = await getTraceDetails(config, traceId);
|
|
10
|
+
const traceRoutes = await getTraceRoutes(config, traceId);
|
|
11
|
+
let eventsWithFrames = [];
|
|
12
|
+
if (traceDetails.events) {
|
|
13
|
+
for (const eventSummary of traceDetails.events) {
|
|
14
|
+
const eventDetails = await getTraceEvents(config, traceId, eventSummary.eventId);
|
|
15
|
+
eventsWithFrames.push(eventDetails);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
completeTraces.push({
|
|
19
|
+
trace: traceDetails,
|
|
20
|
+
events: eventsWithFrames,
|
|
21
|
+
routes: traceRoutes
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return completeTraces;
|
|
25
|
+
};
|
|
26
|
+
export async function getTraceIds(config, offset, resultsLimit) {
|
|
27
|
+
let hasResults = true;
|
|
28
|
+
let pivotedData = [];
|
|
29
|
+
while (hasResults) {
|
|
30
|
+
const response = getAssessTraceIds(config, offset, resultsLimit);
|
|
31
|
+
const responseBody = await response.json();
|
|
32
|
+
if (responseBody.traces == null) {
|
|
33
|
+
console.log('No libraries found');
|
|
34
|
+
return pivotedData;
|
|
35
|
+
}
|
|
36
|
+
const result = responseBody.traces;
|
|
37
|
+
const traceIds = result.map(trace => {
|
|
38
|
+
return trace.uuid;
|
|
39
|
+
});
|
|
40
|
+
pivotedData = pivotedData.concat(traceIds);
|
|
41
|
+
if (result.length < resultsLimit) {
|
|
42
|
+
hasResults = false;
|
|
43
|
+
}
|
|
44
|
+
offset += resultsLimit;
|
|
45
|
+
}
|
|
46
|
+
return pivotedData;
|
|
47
|
+
}
|
|
48
|
+
export async function getTraceDetails(config, traceId) {
|
|
49
|
+
console.log(`Getting trace details for ${traceId} of application ${config.applicationId}`);
|
|
50
|
+
const response = getAssessTraceDetails(config, traceId);
|
|
51
|
+
const responseBody = await response.json();
|
|
52
|
+
return responseBody.trace;
|
|
53
|
+
}
|
|
54
|
+
export async function getTraceEvents(config, traceId, eventId) {
|
|
55
|
+
const response = getAssessTraceEvents(config, traceId, eventId);
|
|
56
|
+
console.log(`Getting trace events for ${eventId} of trace ${traceId}`);
|
|
57
|
+
const responseBody = await response.json();
|
|
58
|
+
return responseBody.event;
|
|
59
|
+
}
|
|
60
|
+
export async function getTraceRoutes(config, traceId) {
|
|
61
|
+
console.log(`Getting trace route for ${traceId} of application ${config.applicationId}`);
|
|
62
|
+
const response = getAssessTraceRoutes(config, traceId);
|
|
63
|
+
const responseBody = await response.json();
|
|
64
|
+
return responseBody.routes;
|
|
65
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { buildBaseRequestOptions, ErrorType } from '../common/baseRequest.js';
|
|
2
|
+
import { got } from 'got';
|
|
3
|
+
const getSCAVulnsUrl = (config, offset, resultsLimit) => {
|
|
4
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/libraries/filter?expand=skip_links,apps,quickFilters,vulns,status,usage_counts&offset=${offset}&limit=${resultsLimit}&sort=score`;
|
|
5
|
+
};
|
|
6
|
+
export function getSCAVulns(config, offset, resultsLimit) {
|
|
7
|
+
const options = buildBaseRequestOptions(config, ErrorType.GENERIC);
|
|
8
|
+
options.url = getSCAVulnsUrl(config, offset, resultsLimit);
|
|
9
|
+
options.json = {
|
|
10
|
+
q: '',
|
|
11
|
+
quickFilter: 'VULNERABLE',
|
|
12
|
+
apps: [config.applicationId],
|
|
13
|
+
servers: [],
|
|
14
|
+
environments: [],
|
|
15
|
+
grades: [],
|
|
16
|
+
languages: [],
|
|
17
|
+
licenses: [],
|
|
18
|
+
status: [],
|
|
19
|
+
severities: [config.severity],
|
|
20
|
+
tags: [],
|
|
21
|
+
includeUnused: false,
|
|
22
|
+
includeUsed: false
|
|
23
|
+
};
|
|
24
|
+
return got.post(options);
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getSCAVulns } from './sarifSCARequests.js';
|
|
2
|
+
const resultsLimit = 50;
|
|
3
|
+
let offset = 0;
|
|
4
|
+
export const scaCVES = async (config) => {
|
|
5
|
+
let hasResults = true;
|
|
6
|
+
let pivotedData = [];
|
|
7
|
+
while (hasResults) {
|
|
8
|
+
const response = getSCAVulns(config, offset, resultsLimit);
|
|
9
|
+
const responseBody = await response.json();
|
|
10
|
+
if (responseBody.libraries == null) {
|
|
11
|
+
console.log('No libraries found');
|
|
12
|
+
return pivotedData;
|
|
13
|
+
}
|
|
14
|
+
// Finds all libraries with a vulnerability of specified severity
|
|
15
|
+
const result = responseBody.libraries.flatMap(item => {
|
|
16
|
+
const { vulns } = item;
|
|
17
|
+
return vulns.flatMap(vuln => {
|
|
18
|
+
vuln.library = item;
|
|
19
|
+
return vuln;
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
// All vulns associated with the library will be returned,
|
|
23
|
+
// so we must filter by severity again
|
|
24
|
+
for (const vuln of result) {
|
|
25
|
+
if (vuln.severityToUse.toLowerCase() === config.severity) {
|
|
26
|
+
pivotedData.push(vuln);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (result.length < resultsLimit) {
|
|
30
|
+
hasResults = false;
|
|
31
|
+
}
|
|
32
|
+
offset += resultsLimit;
|
|
33
|
+
}
|
|
34
|
+
return pivotedData;
|
|
35
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { SarifBuilder, SarifRunBuilder, SarifResultBuilder } from 'node-sarif-builder';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
export const mapSeverity = contrastSeverity => {
|
|
4
|
+
switch (contrastSeverity.toLowerCase()) {
|
|
5
|
+
case 'critical':
|
|
6
|
+
return 'error';
|
|
7
|
+
case 'high':
|
|
8
|
+
return 'warning';
|
|
9
|
+
case 'medium':
|
|
10
|
+
return 'note';
|
|
11
|
+
case 'low':
|
|
12
|
+
return 'note';
|
|
13
|
+
case 'note':
|
|
14
|
+
return 'note';
|
|
15
|
+
default:
|
|
16
|
+
return 'note';
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const getFileFromTSDescription = description => {
|
|
20
|
+
const regex = /\(([^)]+):\d*\)/;
|
|
21
|
+
const match = regex.exec(description);
|
|
22
|
+
return match ? match[1] : 'NOT AVAILABLE';
|
|
23
|
+
};
|
|
24
|
+
export const getLineFromTSDescription = description => {
|
|
25
|
+
const regex = /\(.*:(\d*)\)/;
|
|
26
|
+
const match = regex.exec(description);
|
|
27
|
+
return match ? parseInt(match[1]) : 0;
|
|
28
|
+
};
|
|
29
|
+
export const getLogicalLocationFromTSDescription = description => {
|
|
30
|
+
const regex = /(.*)\([^]+:\d*\)/;
|
|
31
|
+
const match = regex.exec(description);
|
|
32
|
+
return match ? match[1] + '()' : 'NOT AVAILABLE';
|
|
33
|
+
};
|
|
34
|
+
export const generateIastSarifRun = traces => {
|
|
35
|
+
const sarifRunBuilderIast = new SarifRunBuilder().initSimple({
|
|
36
|
+
toolDriverName: 'contrast-assess',
|
|
37
|
+
toolDriverVersion: '1.0.0',
|
|
38
|
+
url: 'https://contrastsecurity.com' // Url of your analyzer tool
|
|
39
|
+
});
|
|
40
|
+
if (traces.length === 0) {
|
|
41
|
+
return sarifRunBuilderIast;
|
|
42
|
+
}
|
|
43
|
+
for (const trace of traces) {
|
|
44
|
+
let extractedString = null;
|
|
45
|
+
if (trace.sink && trace.sink.label) {
|
|
46
|
+
extractedString = getFileFromTSDescription(trace.sink.label);
|
|
47
|
+
}
|
|
48
|
+
const sarifResultBuilder = new SarifResultBuilder().initSimple({
|
|
49
|
+
ruleId: trace.trace.rule_title,
|
|
50
|
+
messageText: trace.trace.title,
|
|
51
|
+
level: mapSeverity(trace.trace.severity)
|
|
52
|
+
});
|
|
53
|
+
if (extractedString) {
|
|
54
|
+
sarifResultBuilder.setLocationArtifactUri({ uri: extractedString });
|
|
55
|
+
}
|
|
56
|
+
if (trace.trace.request) {
|
|
57
|
+
sarifResultBuilder.result.webRequest = {
|
|
58
|
+
target: trace.trace.request.uri,
|
|
59
|
+
method: trace.trace.request.method,
|
|
60
|
+
protocol: trace.trace.request.protocol,
|
|
61
|
+
version: trace.trace.request.version
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
sarifResultBuilder.result.provenance = {
|
|
65
|
+
firstDetectionTimeUtc: new Date(trace.trace.discovered).toISOString(),
|
|
66
|
+
lastDetectionTimeUtc: new Date(trace.trace.last_time_seen).toISOString()
|
|
67
|
+
};
|
|
68
|
+
sarifResultBuilder.result.properties = {
|
|
69
|
+
vulnerability_id: trace.trace.uuid,
|
|
70
|
+
contrast_severity: trace.trace.severity,
|
|
71
|
+
contrast_status: trace.trace.status,
|
|
72
|
+
contrast_substatus: trace.trace.sub_status
|
|
73
|
+
};
|
|
74
|
+
// Add stack trace
|
|
75
|
+
sarifResultBuilder.result.codeFlows = [
|
|
76
|
+
{
|
|
77
|
+
threadFlows: trace.events.map(event => {
|
|
78
|
+
return {
|
|
79
|
+
locations: [
|
|
80
|
+
{
|
|
81
|
+
stack: {
|
|
82
|
+
frames: event.stacktraces.map(stacktrace => {
|
|
83
|
+
return {
|
|
84
|
+
location: {
|
|
85
|
+
physicalLocation: {
|
|
86
|
+
artifactLocation: {
|
|
87
|
+
uri: getFileFromTSDescription(stacktrace.description)
|
|
88
|
+
},
|
|
89
|
+
region: {
|
|
90
|
+
startLine: getLineFromTSDescription(stacktrace.description)
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
logicalLocations: [
|
|
94
|
+
{
|
|
95
|
+
fullyQualifiedName: getLogicalLocationFromTSDescription(stacktrace.description)
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
}),
|
|
106
|
+
properties: {
|
|
107
|
+
routeSignature: trace.routes[0] ? trace.routes[0].signature : ''
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
sarifRunBuilderIast.addResult(sarifResultBuilder);
|
|
112
|
+
}
|
|
113
|
+
return sarifRunBuilderIast;
|
|
114
|
+
};
|
|
115
|
+
export const generateScaSarifRun = cveList => {
|
|
116
|
+
const sarifRunBuilderSca = new SarifRunBuilder().initSimple({
|
|
117
|
+
toolDriverName: 'contrast-sca',
|
|
118
|
+
toolDriverVersion: '1.0.0',
|
|
119
|
+
url: 'https://contrastsecurity.com' // Url of your analyzer tool
|
|
120
|
+
});
|
|
121
|
+
if (cveList.length === 0) {
|
|
122
|
+
return sarifRunBuilderSca;
|
|
123
|
+
}
|
|
124
|
+
for (const cve of cveList) {
|
|
125
|
+
const sarifResultBuilder = new SarifResultBuilder().initSimple({
|
|
126
|
+
ruleId: cve.name,
|
|
127
|
+
messageText: cve.description,
|
|
128
|
+
level: mapSeverity(cve.severityToUse)
|
|
129
|
+
});
|
|
130
|
+
sarifResultBuilder.setLocationArtifactUri({ uri: cve.library.file_name });
|
|
131
|
+
sarifResultBuilder.result.properties = {
|
|
132
|
+
contrast_severity: cve.severityToUse,
|
|
133
|
+
library_version: cve.library.version,
|
|
134
|
+
cvss_score: cve.cvss_3_severity_value,
|
|
135
|
+
vector: cve.cvss_3_vector
|
|
136
|
+
};
|
|
137
|
+
sarifRunBuilderSca.addResult(sarifResultBuilder);
|
|
138
|
+
}
|
|
139
|
+
return sarifRunBuilderSca;
|
|
140
|
+
};
|
|
141
|
+
export const writeCombinedSarif = (traces, cveList, output) => {
|
|
142
|
+
const sarifBuilder = new SarifBuilder();
|
|
143
|
+
const sarifRunBuilderIast = generateIastSarifRun(traces);
|
|
144
|
+
sarifBuilder.addRun(sarifRunBuilderIast);
|
|
145
|
+
const sarifRunBuilderSca = generateScaSarifRun(cveList);
|
|
146
|
+
sarifBuilder.addRun(sarifRunBuilderSca);
|
|
147
|
+
const sarifJsonString = sarifBuilder.buildSarifJsonString({
|
|
148
|
+
indent: true
|
|
149
|
+
}); // indent:true if you like
|
|
150
|
+
if (output) {
|
|
151
|
+
fs.writeFileSync(output, sarifJsonString);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(sarifJsonString);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -78,6 +78,15 @@ export const noProjectUpload = async (analysis, config, reportSpinner) => {
|
|
|
78
78
|
doPoll = false;
|
|
79
79
|
const reportRes = await scaServiceReportNoProject(config, reportID);
|
|
80
80
|
const reportBody = reportRes.body;
|
|
81
|
+
for (let x in reportBody) {
|
|
82
|
+
let dateTime = new Date();
|
|
83
|
+
if (reportBody[x].vulnerabilities.length === 0) {
|
|
84
|
+
logDebug(config, `${dateTime.toISOString()} Unable to find vulnerabilities for ${reportBody[x].groupName}:${reportBody[x].artifactName}, ${reportBody[x].version}, ${reportBody[x].hash}. Environment: ${config.host}, OrgId: ${config.organizationId}, AppId: ${config.applicationId}`);
|
|
85
|
+
}
|
|
86
|
+
if (reportBody[x].remediationAdvice === null) {
|
|
87
|
+
logDebug(config, `${dateTime.toISOString()} unable to find remediation advice for ${reportBody[x].groupName}:${reportBody[x].artifactName}, ${reportBody[x].version}, ${reportBody[x].hash}. Environment: ${config.host}, OrgId: ${config.organizationId}, AppId: ${config.applicationId}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
81
90
|
if (config.saveResults !== undefined) {
|
|
82
91
|
fs.writeFileSync('audit-results.json', JSON.stringify(reportBody));
|
|
83
92
|
}
|
|
@@ -7,8 +7,11 @@ import { logDebug } from '../../common/logging.js';
|
|
|
7
7
|
import { GO, GRADLE, MAVEN, NODE, YARN } from '../../constants/constants.js';
|
|
8
8
|
export const determineProjectTypeAndCwd = (files, packageManager, config) => {
|
|
9
9
|
const projectData = {};
|
|
10
|
+
//clean up the path to be a folder not a file
|
|
11
|
+
projectData.cwd = config.file ? replacePathing(config) : config.file;
|
|
10
12
|
if (isMaven(files, packageManager)) {
|
|
11
13
|
projectData.projectType = MAVEN;
|
|
14
|
+
calculateMavenCommand(projectData);
|
|
12
15
|
}
|
|
13
16
|
else if (isGradle(files, packageManager)) {
|
|
14
17
|
projectData.projectType = GRADLE;
|
|
@@ -46,7 +49,7 @@ const replacePathing = config => config.file
|
|
|
46
49
|
.replace('build.gradle', '')
|
|
47
50
|
.replace('build.gradle.kts', '');
|
|
48
51
|
const buildMaven = (config, projectData, timeout) => {
|
|
49
|
-
let command =
|
|
52
|
+
let command = projectData.mvnCommand;
|
|
50
53
|
let args = ['dependency:tree', '-B', '-Dscope=runtime'];
|
|
51
54
|
if (config.mavenSettingsPath) {
|
|
52
55
|
args.push('-s');
|
|
@@ -61,6 +64,17 @@ const buildMaven = (config, projectData, timeout) => {
|
|
|
61
64
|
checkForErrors(cmdDepTree, config);
|
|
62
65
|
return cmdDepTree.stdout.toString();
|
|
63
66
|
};
|
|
67
|
+
export const calculateMavenCommand = projectData => {
|
|
68
|
+
if (fs.existsSync(projectData.cwd + '/mvnw')) {
|
|
69
|
+
projectData.mvnCommand = projectData.cwd + '/mvnw';
|
|
70
|
+
if (process.platform === 'win32') {
|
|
71
|
+
projectData.mvnCommand += '.cmd';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
projectData.mvnCommand = 'mvn';
|
|
76
|
+
}
|
|
77
|
+
};
|
|
64
78
|
export function checkForErrors(cmdDepTree, config) {
|
|
65
79
|
checkMavenExists(cmdDepTree, config);
|
|
66
80
|
if (cmdDepTree.error || cmdDepTree.status !== 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/contrast",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"description": "Contrast Security's command line tool",
|
|
5
5
|
"exports": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -77,14 +77,15 @@
|
|
|
77
77
|
"js-yaml": "4.1.0",
|
|
78
78
|
"lodash-es": "4.17.21",
|
|
79
79
|
"log-symbols": "4.1.0",
|
|
80
|
+
"node-sarif-builder": "^3.1.0",
|
|
80
81
|
"open": "8.4.2",
|
|
81
82
|
"ora": "6.3.1",
|
|
83
|
+
"pkginfo": "0.4.1",
|
|
82
84
|
"semver": "7.5.4",
|
|
83
85
|
"string-builder": "0.1.8",
|
|
84
86
|
"string-multiple-replace": "1.0.5",
|
|
85
87
|
"xml2js": "0.6.1",
|
|
86
|
-
"yarn-lockfile": "1.1.1"
|
|
87
|
-
"pkginfo": "0.4.1"
|
|
88
|
+
"yarn-lockfile": "1.1.1"
|
|
88
89
|
},
|
|
89
90
|
"devDependencies": {
|
|
90
91
|
"@babel/core": "7.21.8",
|