@contrast/contrast 1.0.12 → 1.0.13

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.
Files changed (37) hide show
  1. package/dist/commands/audit/processAudit.js +10 -0
  2. package/dist/commands/scan/processScan.js +9 -0
  3. package/dist/commands/scan/sca/scaAnalysis.js +3 -0
  4. package/dist/common/HTTPClient.js +30 -2
  5. package/dist/common/fail.js +7 -3
  6. package/dist/common/versionChecker.js +11 -5
  7. package/dist/constants/constants.js +1 -1
  8. package/dist/constants/locales.js +2 -2
  9. package/dist/index.js +1 -1
  10. package/dist/lambda/lambda.js +7 -0
  11. package/dist/scaAnalysis/common/scaServicesUpload.js +52 -0
  12. package/dist/scaAnalysis/javascript/index.js +4 -0
  13. package/dist/scaAnalysis/javascript/scaServiceParser.js +109 -0
  14. package/dist/scaAnalysis/ruby/analysis.js +106 -9
  15. package/dist/scaAnalysis/ruby/index.js +6 -1
  16. package/dist/scan/scanResults.js +1 -1
  17. package/dist/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
  18. package/dist/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +14 -5
  19. package/package.json +1 -1
  20. package/src/commands/audit/processAudit.ts +8 -0
  21. package/src/commands/scan/processScan.js +14 -0
  22. package/src/commands/scan/sca/scaAnalysis.js +10 -0
  23. package/src/common/HTTPClient.js +44 -2
  24. package/src/common/fail.js +7 -3
  25. package/src/common/versionChecker.ts +16 -6
  26. package/src/constants/constants.js +1 -1
  27. package/src/constants/locales.js +2 -3
  28. package/src/index.ts +1 -2
  29. package/src/lambda/lambda.ts +12 -0
  30. package/src/scaAnalysis/common/scaServicesUpload.js +54 -0
  31. package/src/scaAnalysis/javascript/index.js +4 -0
  32. package/src/scaAnalysis/javascript/scaServiceParser.js +145 -0
  33. package/src/scaAnalysis/ruby/analysis.js +137 -9
  34. package/src/scaAnalysis/ruby/index.js +6 -1
  35. package/src/scan/scanResults.js +1 -1
  36. package/src/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
  37. package/src/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +16 -6
@@ -3,6 +3,7 @@ import { auditUsageGuide } from './help'
3
3
  import { processSca } from '../scan/sca/scaAnalysis'
4
4
  import { sendTelemetryConfigAsObject } from '../../telemetry/telemetry'
5
5
  import { ContrastConf } from '../../utils/getConfig'
6
+ import chalk from 'chalk'
6
7
 
7
8
  export type parameterInput = string[]
8
9
 
@@ -17,6 +18,7 @@ export const processAudit = async (
17
18
 
18
19
  const config = await getAuditConfig(contrastConf, 'audit', argv)
19
20
  await processSca(config)
21
+ postRunMessage()
20
22
  await sendTelemetryConfigAsObject(
21
23
  config,
22
24
  'audit',
@@ -30,3 +32,9 @@ export const processAudit = async (
30
32
  const printHelpMessage = () => {
31
33
  console.log(auditUsageGuide)
32
34
  }
35
+
36
+ const postRunMessage = () => {
37
+ console.log('\n' + chalk.underline.bold('Other Codesec Features:'))
38
+ console.log("'contrast scan' to run CodeSec’s industry leading SAST scanner")
39
+ console.log("'contrast lambda' to secure your AWS serverless functions\n")
40
+ }
@@ -6,10 +6,14 @@ const { formatScanOutput } = require('../../scan/formatScanOutput')
6
6
  const { processSca } = require('./sca/scaAnalysis')
7
7
  const common = require('../../common/fail')
8
8
  const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry')
9
+ const chalk = require('chalk')
10
+ const generalAPI = require('../../utils/generalAPI')
9
11
 
10
12
  const processScan = async (contrastConf, argv) => {
11
13
  let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv)
12
14
  let output = undefined
15
+ config.mode = await generalAPI.getMode(config)
16
+
13
17
  //try SCA analysis first
14
18
  if (config.experimental) {
15
19
  await processSca(config, argv)
@@ -35,6 +39,16 @@ const processScan = async (contrastConf, argv) => {
35
39
  if (config.fail) {
36
40
  common.processFail(config, output)
37
41
  }
42
+
43
+ postRunMessage()
44
+ }
45
+
46
+ const postRunMessage = () => {
47
+ console.log('\n' + chalk.underline.bold('Other Codesec Features:'))
48
+ console.log(
49
+ "'contrast audit' to find vulnerabilities in your open source dependencies"
50
+ )
51
+ console.log("'contrast lambda' to secure your AWS serverless functions\n")
38
52
  }
39
53
 
40
54
  module.exports = {
@@ -1,6 +1,7 @@
1
1
  const autoDetection = require('../../../scan/autoDetection')
2
2
  const javaAnalysis = require('../../../scaAnalysis/java')
3
3
  const treeUpload = require('../../../scaAnalysis/common/treeUpload')
4
+ const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload')
4
5
  const auditController = require('../../audit/auditController')
5
6
  const {
6
7
  supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET }
@@ -27,7 +28,11 @@ const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet')
27
28
  const { auditUsageGuide } = require('../../audit/help')
28
29
  const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames')
29
30
  const path = require('path')
31
+ const generalAPI = require('../../../utils/generalAPI')
32
+
30
33
  const processSca = async config => {
34
+ config.mode = await generalAPI.getMode(config)
35
+
31
36
  const startTime = performance.now()
32
37
  let filesFound
33
38
 
@@ -99,6 +104,10 @@ const processSca = async config => {
99
104
  config.applicationId = await auditController.dealWithNoAppId(config)
100
105
  }
101
106
 
107
+ // if (config.experimental) {
108
+ // await scaUpload.scaTreeUpload(messageToSend, config)
109
+ // } else
110
+ // {
102
111
  console.log('') //empty log for space before spinner
103
112
  //send message to TS
104
113
  const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
@@ -126,6 +135,7 @@ const processSca = async config => {
126
135
  console.log(
127
136
  `----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
128
137
  )
138
+ // }
129
139
  } else {
130
140
  if (filesFound.length === 0) {
131
141
  console.log(i18n.__('languageAnalysisNoLanguage'))
@@ -171,9 +171,9 @@ HTTPClient.prototype.getScanProjectById = function getScanProjectById(config) {
171
171
  return requestUtils.sendRequest({ method: 'get', options })
172
172
  }
173
173
 
174
- HTTPClient.prototype.getGlobalProperties = function getGlobalProperties() {
174
+ HTTPClient.prototype.getGlobalProperties = function getGlobalProperties(host) {
175
175
  const options = _.cloneDeep(this.requestOptions)
176
- let url = createGlobalPropertiesUrl(options.uri)
176
+ let url = createGlobalPropertiesUrl(host)
177
177
  options.url = url
178
178
  return requestUtils.sendRequest({ method: 'get', options })
179
179
  }
@@ -216,6 +216,36 @@ HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
216
216
  return requestUtils.sendRequest({ method: 'post', options })
217
217
  }
218
218
 
219
+ HTTPClient.prototype.scaServiceIngest = function scaServiceIngest(
220
+ requestBody,
221
+ config
222
+ ) {
223
+ const options = _.cloneDeep(this.requestOptions)
224
+ let url = createScaServiceIngestURL(config)
225
+ options.url = url
226
+ options.body = requestBody
227
+ return requestUtils.sendRequest({ method: 'post', options })
228
+ }
229
+ HTTPClient.prototype.scaServiceReport = function scaServiceReport(
230
+ config,
231
+ reportId
232
+ ) {
233
+ const options = _.cloneDeep(this.requestOptions)
234
+ let url = createScaServiceReportURL(config, reportId)
235
+ options.url = url
236
+ return requestUtils.sendRequest({ method: 'get', options })
237
+ }
238
+
239
+ HTTPClient.prototype.scaServiceReportStatus = function scaServiceReport(
240
+ config,
241
+ reportId
242
+ ) {
243
+ const options = _.cloneDeep(this.requestOptions)
244
+ let url = createScaServiceReportStatusURL(config, reportId)
245
+ options.url = url
246
+ return requestUtils.sendRequest({ method: 'get', options })
247
+ }
248
+
219
249
  HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
220
250
  const options = _.cloneDeep(this.requestOptions)
221
251
  if (config.ignoreDev) {
@@ -416,6 +446,18 @@ function createSnapshotURL(config) {
416
446
  return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots`
417
447
  }
418
448
 
449
+ function createScaServiceReportURL(config, reportId) {
450
+ return ``
451
+ }
452
+
453
+ function createScaServiceReportStatusURL(config, reportId) {
454
+ return ``
455
+ }
456
+
457
+ function createScaServiceIngestURL(config) {
458
+ return ``
459
+ }
460
+
419
461
  const createAppCreateURL = config => {
420
462
  return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/create`
421
463
  }
@@ -25,11 +25,15 @@ const isSeverityViolation = (severity, reportResults) => {
25
25
  count += reportResults.high + reportResults.critical
26
26
  break
27
27
  case 'medium':
28
- count += reportResults.medium + reportResults.low + reportResults.critical
28
+ count +=
29
+ reportResults.medium + reportResults.high + reportResults.critical
29
30
  break
30
31
  case 'low':
31
32
  count +=
32
- reportResults.high + reportResults.critical + reportResults.medium
33
+ reportResults.high +
34
+ reportResults.critical +
35
+ reportResults.medium +
36
+ reportResults.low
33
37
  break
34
38
  case 'note':
35
39
  if (reportResults.note == reportResults.total) {
@@ -51,7 +55,7 @@ const failPipeline = (message = '') => {
51
55
  ' *********************************\n' +
52
56
  i18n.__(message)
53
57
  )
54
- process.exit(1)
58
+ process.exit(2)
55
59
  }
56
60
 
57
61
  const parseSeverity = severity => {
@@ -6,7 +6,7 @@ import commonApi from '../utils/commonApi'
6
6
  import { constants } from 'http2'
7
7
  import { ContrastConf } from '../utils/getConfig'
8
8
 
9
- const getLatestVersion = async (config: any) => {
9
+ export const getLatestVersion = async (config: ContrastConf) => {
10
10
  const client = commonApi.getHttpClient(config)
11
11
  try {
12
12
  const res = await client.getLatestVersion()
@@ -14,18 +14,28 @@ const getLatestVersion = async (config: any) => {
14
14
  return res.body
15
15
  }
16
16
  } catch (e) {
17
- return
17
+ return undefined
18
18
  }
19
19
  }
20
20
 
21
21
  export async function findLatestCLIVersion(config: ContrastConf) {
22
22
  const isCI = process.env.CONTRAST_CODESEC_CI
23
- ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
23
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI.toLowerCase())
24
24
  : false
25
+
25
26
  if (!isCI) {
26
- let latestCLIVersion: string = await getLatestVersion(config)
27
- //strip key
28
- latestCLIVersion = latestCLIVersion.substring(8)
27
+ let latestCLIVersion = await getLatestVersion(config)
28
+
29
+ if (latestCLIVersion === undefined) {
30
+ config.set('numOfRuns', 0)
31
+ console.log(
32
+ 'Failed to retrieve latest version info. Continuing execution.'
33
+ )
34
+ return
35
+ }
36
+
37
+ //strip key and remove new lines
38
+ latestCLIVersion = latestCLIVersion.substring(8).replace('\n', '')
29
39
 
30
40
  if (semver.lt(APP_VERSION, latestCLIVersion)) {
31
41
  const updateAvailableMessage = `Update available ${chalk.yellow(
@@ -14,7 +14,7 @@ const HIGH = 'HIGH'
14
14
  const CRITICAL = 'CRITICAL'
15
15
  // App
16
16
  const APP_NAME = 'contrast'
17
- const APP_VERSION = '1.0.12'
17
+ const APP_VERSION = '1.0.13'
18
18
  const TIMEOUT = 120000
19
19
  const HIGH_COLOUR = '#ff9900'
20
20
  const CRITICAL_COLOUR = '#e35858'
@@ -26,8 +26,7 @@ const en_locales = () => {
26
26
  unauthenticatedErrorMessage:
27
27
  'Please check the following keys are correct:\n--organization-id, --api-key or --authorization',
28
28
  badRequestErrorHeader: '400 error - Bad Request',
29
- badRequestErrorMessage:
30
- 'Please check the following key is correct: \n--application-id',
29
+ badRequestErrorMessage: 'Please check your parameters and try again',
31
30
  badRequestCatalogueErrorMessage:
32
31
  'The application name already exists, please use a unique name',
33
32
  forbiddenRequestErrorHeader: '403 error - Forbidden',
@@ -318,7 +317,7 @@ const en_locales = () => {
318
317
  scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
319
318
  authSuccessMessage: 'Authentication successful',
320
319
  runAuthSuccessMessage:
321
- "Now you can use CodeSec by Contrast \nRun: \n'contrast scan' on your file \n'contrast audit' on a file or directory,\n'contrast lambda' on an AWS function.\nor 'contrast help' to learn more about the capabilities.",
320
+ "Now you can use CodeSec by Contrast \nRun: \n'contrast scan' to run CodeSec’s industry leading SAST scanner \n'contrast audit' to find vulnerabilities in your open source dependencies \n'contrast lambda' to secure your AWS serverless functions\nor 'contrast help' to learn more about the capabilities.",
322
321
  authWaitingMessage: 'Waiting for auth...',
323
322
  authTimedOutMessage: 'Auth Timed out, try again',
324
323
  zipErrorScan:
package/src/index.ts CHANGED
@@ -15,7 +15,6 @@ import {
15
15
  } from './common/versionChecker'
16
16
  import { findCommandOnError } from './common/errorHandling'
17
17
  import { sendTelemetryConfigAsConfObj } from './telemetry/telemetry'
18
-
19
18
  const {
20
19
  commandLineDefinitions: { mainUsageGuide, mainDefinition }
21
20
  } = constants
@@ -58,7 +57,7 @@ const start = async () => {
58
57
  config.set('numOfRuns', config.get('numOfRuns') + 1)
59
58
 
60
59
  // @ts-ignore
61
- if (config.get('numOfRuns') >= 1) {
60
+ if (config.get('numOfRuns') >= 10) {
62
61
  await findLatestCLIVersion(config)
63
62
  config.set('numOfRuns', 0)
64
63
  }
@@ -17,6 +17,7 @@ import ora from '../utils/oraWrapper'
17
17
  import { postAnalytics } from './analytics'
18
18
  import { LambdaOptions, AnalyticsOption, StatusType, EventType } from './types'
19
19
  import { APP_VERSION } from '../constants/constants'
20
+ import chalk from 'chalk'
20
21
 
21
22
  type ApiParams = {
22
23
  organizationId: string
@@ -114,6 +115,9 @@ const processLambda = async (argv: string[]) => {
114
115
  await postAnalytics(endCommandAnalytics).catch((error: Error) => {
115
116
  /* ignore */
116
117
  })
118
+
119
+ postRunMessage()
120
+
117
121
  if (errorMsg) {
118
122
  process.exit(1)
119
123
  }
@@ -221,4 +225,12 @@ const handleLambdaHelp = () => {
221
225
  process.exit(0)
222
226
  }
223
227
 
228
+ const postRunMessage = () => {
229
+ console.log('\n' + chalk.underline.bold('Other Codesec Features:'))
230
+ console.log("'contrast scan' to run CodeSec’s industry leading SAST scanner")
231
+ console.log(
232
+ "'contrast audit' to find vulnerabilities in your open source dependencies\n"
233
+ )
234
+ }
235
+
224
236
  export { processLambda, LambdaOptions, ApiParams, getAvailableFunctions }
@@ -0,0 +1,54 @@
1
+ const commonApi = require('../../utils/commonApi')
2
+ const { APP_VERSION } = require('../../constants/constants')
3
+ const requestUtils = require('../../utils/requestUtils')
4
+
5
+ const scaTreeUpload = async (analysis, config) => {
6
+ const requestBody = {
7
+ applicationId: config.applicationId,
8
+ dependencyTree: analysis,
9
+ organizationId: config.organizationId,
10
+ language: 'NODE',
11
+ tool: {
12
+ name: 'Contrast Codesec',
13
+ version: APP_VERSION
14
+ }
15
+ }
16
+
17
+ const client = commonApi.getHttpClient(config)
18
+ const reportID = await client
19
+ .scaServiceIngest(requestBody, config)
20
+ .then(res => {
21
+ if (res.statusCode === 201) {
22
+ return res.body.libraryIngestJobId
23
+ } else {
24
+ throw new Error(res.statusCode + ` error ingesting dependencies`)
25
+ }
26
+ })
27
+ .catch(err => {
28
+ throw err
29
+ })
30
+
31
+ let keepChecking = true
32
+ while (keepChecking) {
33
+ keepChecking = await client
34
+ .scaServiceReportStatus(config, reportID)
35
+ .then(res => {
36
+ console.log(res.body)
37
+ if (res.body.status == 'COMPLETED') {
38
+ client.scaServiceReport(config, reportID).then(res => {
39
+ console.log(res.statusCode)
40
+ console.log(res.body)
41
+ })
42
+
43
+ return (keepChecking = false)
44
+ } else {
45
+ return (keepChecking = true)
46
+ }
47
+ })
48
+ await requestUtils.sleep(5000)
49
+ }
50
+ }
51
+
52
+ module.exports = {
53
+ scaTreeUpload
54
+ }
@@ -1,6 +1,7 @@
1
1
  const analysis = require('./analysis')
2
2
  const i18n = require('i18n')
3
3
  const formatMessage = require('../common/formatMessage')
4
+ const scaServiceParser = require('./scaServiceParser')
4
5
 
5
6
  const jsAnalysis = async (config, languageFiles) => {
6
7
  checkForCorrectFiles(languageFiles)
@@ -13,6 +14,9 @@ const jsAnalysis = async (config, languageFiles) => {
13
14
  const buildNodeTree = async (config, files) => {
14
15
  let analysis = await readFiles(config, files)
15
16
  const rawNode = await parseFiles(config, files, analysis)
17
+ if (config.experimental) {
18
+ return scaServiceParser.parseJS(rawNode)
19
+ }
16
20
  return formatMessage.createJavaScriptTSMessage(rawNode)
17
21
  }
18
22
 
@@ -0,0 +1,145 @@
1
+ const parseJS = rawNode => {
2
+ let dependencyTree = {}
3
+ let combinedPackageJSONDep = {
4
+ ...rawNode.packageJSON?.dependencies,
5
+ ...rawNode.packageJSON?.devDependencies
6
+ }
7
+ let analyseLock = chooseLockFile(rawNode)
8
+
9
+ if (analyseLock.type === 'yarn') {
10
+ dependencyTree = yarnCreateDepTree(
11
+ dependencyTree,
12
+ combinedPackageJSONDep,
13
+ analyseLock.lockFile,
14
+ rawNode
15
+ )
16
+ }
17
+
18
+ if (analyseLock.type === 'npm') {
19
+ dependencyTree = npmCreateDepTree(
20
+ dependencyTree,
21
+ combinedPackageJSONDep,
22
+ analyseLock.lockFile,
23
+ rawNode
24
+ )
25
+ }
26
+
27
+ return dependencyTree
28
+ }
29
+
30
+ const npmCreateDepTree = (
31
+ dependencyTree,
32
+ combinedPackageJSONDep,
33
+ packageLock,
34
+ rawNode
35
+ ) => {
36
+ for (const [key, value] of Object.entries(packageLock)) {
37
+ dependencyTree[key] = {
38
+ name: key,
39
+ version: getResolvedVersion(key, packageLock),
40
+ group: null,
41
+ isProduction: checkIfInPackageJSON(rawNode.packageJSON.dependencies, key),
42
+ directDependency: checkIfInPackageJSON(combinedPackageJSONDep, key),
43
+ dependencies: createNPMChildDependencies(packageLock, key)
44
+ }
45
+ }
46
+ return dependencyTree
47
+ }
48
+
49
+ const yarnCreateDepTree = (
50
+ dependencyTree,
51
+ combinedPackageJSONDep,
52
+ packageLock,
53
+ rawNode
54
+ ) => {
55
+ for (const [key, value] of Object.entries(packageLock)) {
56
+ let gav = getNameFromGAV(key)
57
+ let nag = getDepNameWithoutVersion(key)
58
+ dependencyTree[key] = {
59
+ name: gav,
60
+ version: getResolvedVersion(key, packageLock),
61
+ group: null,
62
+ isProduction: checkIfInPackageJSON(rawNode.packageJSON.dependencies, nag),
63
+ directDependency: checkIfInPackageJSON(combinedPackageJSONDep, nag),
64
+ dependencies: createChildDependencies(packageLock, key)
65
+ }
66
+ }
67
+ return dependencyTree
68
+ }
69
+
70
+ const chooseLockFile = rawNode => {
71
+ if (rawNode?.yarn?.yarnLockFile !== undefined) {
72
+ return { lockFile: rawNode?.yarn?.yarnLockFile?.object, type: 'yarn' }
73
+ } else if (rawNode.npmLockFile !== undefined) {
74
+ return { lockFile: rawNode?.npmLockFile?.dependencies, type: 'npm' }
75
+ } else {
76
+ return undefined
77
+ }
78
+ }
79
+
80
+ const createKeyName = (dep, version) => {
81
+ return dep + '@' + version
82
+ }
83
+
84
+ const checkIfInPackageJSON = (list, dep) => {
85
+ return Object.keys(list).includes(dep)
86
+ }
87
+
88
+ const createChildDependencies = (lockFileDep, currentDep) => {
89
+ let depArray = []
90
+ if (lockFileDep[currentDep]?.dependencies) {
91
+ for (const [key, value] of Object.entries(
92
+ lockFileDep[currentDep]?.dependencies
93
+ )) {
94
+ depArray.push(createKeyName(key, value))
95
+ }
96
+ }
97
+ return depArray
98
+ }
99
+
100
+ const createNPMChildDependencies = (lockFileDep, currentDep) => {
101
+ let depArray = []
102
+ if (lockFileDep[currentDep]?.requires) {
103
+ for (const [key, value] of Object.entries(
104
+ lockFileDep[currentDep]?.requires
105
+ )) {
106
+ depArray.push(key)
107
+ }
108
+ }
109
+ return depArray
110
+ }
111
+
112
+ const getDepNameWithoutVersion = depKey => {
113
+ let dependency = depKey.split('@')
114
+ if (dependency.length - 1 > 1) {
115
+ return '@' + dependency[1]
116
+ }
117
+ return dependency[0]
118
+ }
119
+
120
+ const getNameFromGAV = depKey => {
121
+ let dependency = depKey.split('/')
122
+ if (dependency.length == 2) {
123
+ dependency = getDepNameWithoutVersion(dependency[1])
124
+ return dependency
125
+ }
126
+ if (dependency.length == 1) {
127
+ dependency = getDepNameWithoutVersion(depKey)
128
+ return dependency
129
+ }
130
+ //what should we do if there's no version? The service will fall over but do we want to throw error for only one wrong version?
131
+ return depKey
132
+ }
133
+
134
+ const getResolvedVersion = (depKey, packageLock) => {
135
+ return packageLock[depKey]?.version
136
+ }
137
+
138
+ module.exports = {
139
+ parseJS,
140
+ checkIfInPackageJSON,
141
+ getNameFromGAV,
142
+ getResolvedVersion,
143
+ chooseLockFile,
144
+ createNPMChildDependencies
145
+ }