ts-node-client 2.0.0 → 2.1.0
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/README.md +55 -48
- package/bin/ts-node-client.js +32 -24
- package/lib/cli.js +18 -0
- package/lib/convertor.js +244 -0
- package/lib/dependency.js +4 -2
- package/lib/pkg.js +32 -0
- package/lib/rest-client.js +56 -76
- package/package.json +12 -11
- package/test/error-test.js +1 -1
- package/test/rest-test.js +3 -3
package/README.md
CHANGED
|
@@ -7,14 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://nodei.co/npm/ts-node-client/)
|
|
9
9
|
|
|
10
|
-
TrustSource node client
|
|
11
|
-
|
|
12
|
-
> node module to transfer dependency information to TrustSource server.
|
|
10
|
+
> TrustSource node client - node module to transfer dependency information to TrustSource server.
|
|
13
11
|
|
|
14
12
|
## Requirements
|
|
15
|
-
|
|
16
13
|
* node >= 8.9.0
|
|
17
|
-
*
|
|
14
|
+
* npm < 8.0.0 use **ts-node-client@1.***
|
|
15
|
+
* npm >= 8.0.0 use **ts-node-client@2.***
|
|
18
16
|
|
|
19
17
|
## Installation
|
|
20
18
|
Run: `npm install --save-dev ts-node-client` or `yarn add --dev ts-node-client`
|
|
@@ -47,8 +45,8 @@ You also may initiate transfer to TrustSource server manually by executing follo
|
|
|
47
45
|
|
|
48
46
|
```
|
|
49
47
|
node_modules/.bin/ts-node-client
|
|
50
|
-
node_modules/.bin/ts-node-client -k apiKey -p Project
|
|
51
|
-
node_modules/.bin/ts-node-client -c config.json
|
|
48
|
+
node_modules/.bin/ts-node-client -k apiKey -p Project --breakOnWarnings false --breakOnViolations true
|
|
49
|
+
node_modules/.bin/ts-node-client -c config.json
|
|
52
50
|
```
|
|
53
51
|
```
|
|
54
52
|
npm / node module to transfer dependency information to TrustSource server.
|
|
@@ -61,93 +59,102 @@ Options:
|
|
|
61
59
|
--binaryLinks Binary links separated by comma [default: null]
|
|
62
60
|
--url url [default: null]
|
|
63
61
|
--config, -c Config path [default: null]
|
|
64
|
-
--proxy Proxy url like 'https://user:password@host:port'
|
|
65
|
-
--version
|
|
62
|
+
--proxy Proxy url like 'https://user:password@host:port' [default: null]
|
|
63
|
+
--version Prints a version [default: null]
|
|
64
|
+
--saveAs, -o Save as file (file name prefix) [default: null]
|
|
65
|
+
--saveAsFormat, -f Save as format (scan / cydx / spdx) [default: null]
|
|
66
66
|
--debug [default: null]
|
|
67
67
|
--simulate [default: null]
|
|
68
68
|
--includeDevDependencies [default: null]
|
|
69
69
|
--meteor [default: null]
|
|
70
|
-
--
|
|
71
|
-
--
|
|
70
|
+
--breakOnWarnings [default: null]
|
|
71
|
+
--breakOnViolations [default: null]
|
|
72
72
|
--help Prints a usage statement [boolean]
|
|
73
|
+
|
|
73
74
|
```
|
|
75
|
+
PLEASE NOTE: if you want to pass param into function
|
|
76
|
+
you should add value, for example:
|
|
77
|
+
|
|
78
|
+
`--breakOnViolations true` or `--saveAs sbom`
|
|
79
|
+
|
|
80
|
+
## Software bill of materials
|
|
81
|
+
|
|
82
|
+
[View SBOM <img alt="TrustSource" src="https://app.trustsource.io/logo.png" width="70"/>](https://app.trustsource.io/api/v1/public-BoM/ae0832c6-5a55-4aa8-8c45-75528d0833fb)
|
|
83
|
+
|
|
84
|
+
## Known problems
|
|
85
|
+
|
|
86
|
+
#### Error: The programmatic API was removed in npm v8.0.0
|
|
87
|
+
You should upgrade to 2.* versions of ts-node-client
|
|
74
88
|
|
|
75
89
|
## Changelog
|
|
76
90
|
|
|
77
|
-
#### 2.
|
|
78
|
-
|
|
91
|
+
#### 2.1.*
|
|
92
|
+
- Migrate 1.6.* - 1.8.* changes to version 2.1
|
|
93
|
+
|
|
94
|
+
#### 2.0.*
|
|
95
|
+
- Support new scan tool and fix problem with programmatic API for >= npm@8.0.0
|
|
79
96
|
- Stop usage of [`global-npm`](https://github.com/dracupid/global-npm) until we find new resolution
|
|
80
97
|
- Get back `npm` as local dependency
|
|
81
98
|
|
|
82
|
-
#### 1.
|
|
83
|
-
-
|
|
99
|
+
#### 1.8.*
|
|
100
|
+
- SBOM
|
|
101
|
+
- **--saveAs** and **--saveAsFormat**
|
|
102
|
+
- Bump minimist from 1.2.5 to 1.2.6
|
|
103
|
+
- Bump urijs from 1.19.10 to 1.19.11
|
|
104
|
+
- replace packageurl-js with simple local function
|
|
105
|
+
- improve docs
|
|
106
|
+
|
|
107
|
+
#### 1.7.*
|
|
108
|
+
- request -> axios
|
|
109
|
+
- fix dependencies
|
|
110
|
+
- doc fixes
|
|
111
|
+
|
|
112
|
+
#### 1.6.*
|
|
113
|
+
- **--breakOnWarnings** and **--breakOnViolations**
|
|
114
|
+
- Bump devDependencies
|
|
84
115
|
|
|
85
|
-
#### 1.5
|
|
116
|
+
#### 1.5.*
|
|
117
|
+
- Describe `Error: The programmatic API was removed in npm v8.0.0`
|
|
86
118
|
- Bump devDependencies
|
|
87
119
|
- Introduce sonarjs
|
|
88
120
|
|
|
89
|
-
#### 1.4
|
|
121
|
+
#### 1.4.*
|
|
90
122
|
- Bump glob-parent from 5.1.1 to 5.1.2
|
|
91
123
|
- Bump path-parse from 1.0.6 to 1.0.7
|
|
92
|
-
|
|
93
|
-
#### 1.4.2
|
|
94
124
|
- Bump lodash from 4.17.19 to 4.17.21
|
|
95
|
-
|
|
96
|
-
#### 1.4.1
|
|
97
125
|
- Bump y18n from 4.0.0 to 4.0.1
|
|
98
|
-
|
|
99
|
-
#### 1.4.0
|
|
100
126
|
- Added:
|
|
101
127
|
- option **--includeDevDependencies**. It is allow to scan dev dependencies
|
|
102
128
|
|
|
103
|
-
#### 1.3
|
|
129
|
+
#### 1.3.*
|
|
104
130
|
- Use [`global-npm`](https://github.com/dracupid/global-npm) (meaning `npm` is no longer a dependency of `ts-node-client`)
|
|
105
131
|
|
|
106
|
-
#### 1.2
|
|
132
|
+
#### 1.2.*
|
|
107
133
|
- Added:
|
|
108
134
|
- option **--brakeOnViolations**. It is fail build in case any violations after scan transferred.
|
|
109
135
|
- option **--brakeOnWarnings**. It is fail build in case any warning after scan transferred.
|
|
110
136
|
|
|
111
|
-
#### 1.1
|
|
137
|
+
#### 1.1.*
|
|
112
138
|
- userName is not required param for scans
|
|
113
139
|
- Support usage of scan meta param binaryLinks inside Options definition
|
|
114
140
|
|
|
115
|
-
|
|
116
|
-
#### 1.0.0
|
|
141
|
+
#### 1.0.*
|
|
117
142
|
- Node JS and dependencies updates "node": ">= 8.12.0"
|
|
118
143
|
|
|
119
|
-
#### 0.3
|
|
144
|
+
#### 0.3.*
|
|
120
145
|
- Improve variable usage and tasks migration
|
|
121
|
-
|
|
122
|
-
#### 0.3.3
|
|
123
146
|
- Support usage of scan meta params: branch and tag inside Options definition
|
|
124
|
-
|
|
125
|
-
#### 0.3.2
|
|
126
147
|
- Skip npmDependency without names
|
|
127
|
-
|
|
128
|
-
#### 0.3.1
|
|
129
148
|
- Update travis config
|
|
130
|
-
|
|
131
|
-
#### 0.3.0
|
|
132
149
|
- Update dependency to resolve vulnerabilities
|
|
133
150
|
|
|
134
|
-
#### 0.2
|
|
151
|
+
#### 0.2.*
|
|
135
152
|
- Added proxy support and config
|
|
136
|
-
|
|
137
|
-
#### 0.2.4
|
|
138
153
|
- Update travis config
|
|
139
|
-
|
|
140
|
-
#### 0.2.3
|
|
141
154
|
- Updated README.md with `app.trustsource.io`
|
|
142
|
-
|
|
143
|
-
#### 0.2.2
|
|
144
155
|
- Updated default url to `app.trustsource.io`
|
|
145
|
-
|
|
146
|
-
#### 0.2.1
|
|
147
156
|
- Added windows support
|
|
148
157
|
- Fixed json
|
|
149
|
-
|
|
150
|
-
#### 0.2.0
|
|
151
158
|
- **Removed:**
|
|
152
159
|
- options: **--credentials** and **--credentialsFile** instead you should use **--config**.
|
|
153
160
|
- option **--baseUrl** instead you should use **--url**.
|
package/bin/ts-node-client.js
CHANGED
|
@@ -54,12 +54,17 @@ const getOptions = () => {
|
|
|
54
54
|
},
|
|
55
55
|
proxy: {
|
|
56
56
|
default: null,
|
|
57
|
-
describe: 'Proxy url'
|
|
57
|
+
describe: 'Proxy url like \'https://user:password@host:port\''
|
|
58
58
|
},
|
|
59
|
-
|
|
60
|
-
alias: '
|
|
61
|
-
default:
|
|
62
|
-
describe: '
|
|
59
|
+
saveAs: {
|
|
60
|
+
alias: 'o',
|
|
61
|
+
default: null,
|
|
62
|
+
describe: 'Save as file (file name prefix)'
|
|
63
|
+
},
|
|
64
|
+
saveAsFormat: {
|
|
65
|
+
alias: 'f',
|
|
66
|
+
default: null,
|
|
67
|
+
describe: 'Save as format (scan / cydx / spdx)'
|
|
63
68
|
},
|
|
64
69
|
debug: {
|
|
65
70
|
default: null,
|
|
@@ -73,19 +78,20 @@ const getOptions = () => {
|
|
|
73
78
|
default: null,
|
|
74
79
|
describe: 'meteor'
|
|
75
80
|
},
|
|
76
|
-
|
|
81
|
+
breakOnWarnings: {
|
|
77
82
|
default: null,
|
|
78
|
-
describe: '
|
|
83
|
+
describe: 'breakOnWarnings'
|
|
79
84
|
},
|
|
80
|
-
|
|
85
|
+
breakOnViolations: {
|
|
81
86
|
default: null,
|
|
82
|
-
describe: '
|
|
87
|
+
describe: 'breakOnViolations'
|
|
83
88
|
},
|
|
84
89
|
includeDevDependencies: {
|
|
85
90
|
default: null,
|
|
86
91
|
describe: 'includeDevDependencies'
|
|
87
92
|
}
|
|
88
93
|
})
|
|
94
|
+
.version()
|
|
89
95
|
.usage(pckgJson.description)
|
|
90
96
|
.help('help', 'Prints a usage statement')
|
|
91
97
|
.fail((msg, err, yargsObject) => {
|
|
@@ -100,10 +106,10 @@ const getOptions = () => {
|
|
|
100
106
|
}
|
|
101
107
|
options = (({
|
|
102
108
|
// eslint-disable-next-line max-len
|
|
103
|
-
apiKey, project, branch, tag, binaryLinks, config, debug, simulate, meteor, url, proxy,
|
|
109
|
+
apiKey, project, branch, tag, binaryLinks, config, debug, saveAs, saveAsFormat, simulate, meteor, url, proxy, breakOnWarnings, breakOnViolations, includeDevDependencies
|
|
104
110
|
}) => ({
|
|
105
111
|
// eslint-disable-next-line max-len
|
|
106
|
-
apiKey, project, branch, tag, binaryLinks, config, debug, simulate, scanMeteor: meteor, url, proxy,
|
|
112
|
+
apiKey, project, branch, tag, binaryLinks, config, debug, saveAs, saveAsFormat, simulate, scanMeteor: meteor, url, proxy, breakOnWarnings, breakOnViolations, includeDevDependencies
|
|
107
113
|
}))(options);
|
|
108
114
|
Object.keys(options).forEach((key) => options[key] === null && delete options[key]);
|
|
109
115
|
return options;
|
|
@@ -144,19 +150,21 @@ validateOptions(options);
|
|
|
144
150
|
|
|
145
151
|
if (options.debug) {
|
|
146
152
|
console.log('invoking ts-node-client: ');
|
|
147
|
-
console.log(`${FILL}debug
|
|
148
|
-
console.log(`${FILL}simulate
|
|
149
|
-
console.log(`${FILL}includeDevDependencies
|
|
150
|
-
console.log(`${FILL}scanMeteor
|
|
151
|
-
console.log(`${FILL}
|
|
152
|
-
console.log(`${FILL}
|
|
153
|
-
console.log(`${FILL}
|
|
154
|
-
console.log(`${FILL}
|
|
155
|
-
console.log(`${FILL}
|
|
156
|
-
console.log(`${FILL}
|
|
157
|
-
console.log(`${FILL}
|
|
158
|
-
console.log(`${FILL}
|
|
159
|
-
console.log(`${FILL}
|
|
153
|
+
console.log(`${FILL}debug = %s`, options.debug);
|
|
154
|
+
console.log(`${FILL}simulate = %s`, options.simulate);
|
|
155
|
+
console.log(`${FILL}includeDevDependencies = %s`, options.includeDevDependencies);
|
|
156
|
+
console.log(`${FILL}scanMeteor = %s`, options.scanMeteor);
|
|
157
|
+
console.log(`${FILL}saveAs = %s`, options.saveAs);
|
|
158
|
+
console.log(`${FILL}saveAsFormat = %s`, options.saveAsFormat);
|
|
159
|
+
console.log(`${FILL}breakOnViolations = %s`, options.breakOnViolations);
|
|
160
|
+
console.log(`${FILL}breakOnWarnings = %s`, options.breakOnWarnings);
|
|
161
|
+
console.log(`${FILL}apiKey = %s`, options.apiKey);
|
|
162
|
+
console.log(`${FILL}project = %s`, options.project);
|
|
163
|
+
console.log(`${FILL}branch = %s`, options.branch);
|
|
164
|
+
console.log(`${FILL}tag = %s`, options.tag);
|
|
165
|
+
console.log(`${FILL}binaryLinks = %s`, options.binaryLinks);
|
|
166
|
+
console.log(`${FILL}url = %s`, options.url);
|
|
167
|
+
console.log(`${FILL}proxy = %s`, options.proxy);
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
let exitCode = 0;
|
package/lib/cli.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
/* eslint-enable */
|
|
8
8
|
|
|
9
9
|
const debuglog = (require('debuglog'))('ts-node-client');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const Convertor = require('./convertor');
|
|
10
12
|
|
|
11
13
|
const stdlog = console.log;
|
|
12
14
|
const MODULE_NAME = 'ts-node-client:cli';
|
|
@@ -47,6 +49,22 @@ function scan(options, scanDone) {
|
|
|
47
49
|
return undefined;
|
|
48
50
|
}
|
|
49
51
|
return scanResult;
|
|
52
|
+
}).then((scanResult) => {
|
|
53
|
+
if (options.saveAs) {
|
|
54
|
+
const date = new Date();
|
|
55
|
+
let printData = JSON.stringify(scanResult, 0, 2);
|
|
56
|
+
let printExt = 'scan';
|
|
57
|
+
if (options.saveAsFormat) {
|
|
58
|
+
const allowedTypes = ['scan', 'spdx', 'cydx'];
|
|
59
|
+
if (allowedTypes.indexOf(options.saveAsFormat) > 0) {
|
|
60
|
+
printData = JSON.stringify(Convertor.scanTo(options.saveAsFormat, scanResult), 0, 2);
|
|
61
|
+
printExt = options.saveAsFormat;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const formatedDate = date.toISOString().substr(0, 19).split(':').join('-').split('T').join('-');
|
|
65
|
+
fs.writeFileSync(`${options.saveAs || 'ts-scan'}-${formatedDate}-${options.saveAsFormat ? printExt : 'scan'}.json`, printData);
|
|
66
|
+
}
|
|
67
|
+
return scanResult;
|
|
50
68
|
}).then((scanResult) => {
|
|
51
69
|
if (scanResult) {
|
|
52
70
|
return npmScanner.transfer(scanResult);
|
package/lib/convertor.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**********************************************************
|
|
3
|
+
* Copyright (c) 2017. Enterprise Architecture Group, EACG
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*********************************************************/
|
|
7
|
+
/* eslint-enable */
|
|
8
|
+
const PackageURL = require('./pkg');
|
|
9
|
+
|
|
10
|
+
const Convertor = {};
|
|
11
|
+
|
|
12
|
+
Convertor.scanTo = function scanTo(type, scan) {
|
|
13
|
+
if (type.toLowerCase() === 'cydx') {
|
|
14
|
+
return Convertor.scanToCydx(scan);
|
|
15
|
+
}
|
|
16
|
+
if (type.toLowerCase() === 'spdx') {
|
|
17
|
+
return Convertor.scanToSpdx(scan);
|
|
18
|
+
}
|
|
19
|
+
return scan;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function ComponentKey(key, parts) {
|
|
23
|
+
if (!key || typeof key.split !== 'function') {
|
|
24
|
+
// throw new Error('key must be a string');
|
|
25
|
+
} else {
|
|
26
|
+
parts = parts || { mgr: true, component: true, version: true };
|
|
27
|
+
|
|
28
|
+
const partsCnt = (parts.mgr ? 1 : 0) + (parts.component ? 1 : 0) + (parts.version ? 1 : 0);
|
|
29
|
+
const splitParts = key.split(':');
|
|
30
|
+
// component may exists of more than one part
|
|
31
|
+
if ((parts.component && splitParts.length < partsCnt) || (!parts.component && splitParts.length !== partsCnt)) {
|
|
32
|
+
// throw new Error('invalid key format:' + key);
|
|
33
|
+
} else {
|
|
34
|
+
let compStartIdx = 0; let
|
|
35
|
+
compEndIdx = splitParts.length;
|
|
36
|
+
if (parts.mgr) {
|
|
37
|
+
// eslint-disable-next-line prefer-destructuring
|
|
38
|
+
this.manager = splitParts[0];
|
|
39
|
+
// eslint-disable-next-line no-plusplus
|
|
40
|
+
compStartIdx++;
|
|
41
|
+
}
|
|
42
|
+
if (parts.version) {
|
|
43
|
+
this.version = splitParts[splitParts.length - 1];
|
|
44
|
+
// eslint-disable-next-line no-plusplus
|
|
45
|
+
compEndIdx--;
|
|
46
|
+
}
|
|
47
|
+
if (parts.component) {
|
|
48
|
+
this.component = '';
|
|
49
|
+
// eslint-disable-next-line no-plusplus
|
|
50
|
+
for (let i = compStartIdx; i < compEndIdx; i++) {
|
|
51
|
+
if (this.component) {
|
|
52
|
+
this.component += ':';
|
|
53
|
+
}
|
|
54
|
+
this.component += splitParts[i];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getPackageUrl(componentKey, version) {
|
|
62
|
+
const result = new ComponentKey(componentKey, { mgr: true, component: true });
|
|
63
|
+
if (result && result.component && result.manager !== 'im') {
|
|
64
|
+
const parts = result.component.split(':');
|
|
65
|
+
const org = parts.length > 1 ? parts[0] : null;
|
|
66
|
+
const key = parts.length > 1 ? parts[1] : parts[0];
|
|
67
|
+
if (key) {
|
|
68
|
+
return PackageURL.get(result.manager, org, key, version);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getSpdxFormattedKey(componentKey) {
|
|
76
|
+
return componentKey.split(':').join('-');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function dependencyToCydxComponent(dependency) {
|
|
80
|
+
const {
|
|
81
|
+
name, key, description, homepageUrl, repoUrl, licenses, versions
|
|
82
|
+
} = dependency;
|
|
83
|
+
const version = versions && versions[0];
|
|
84
|
+
const purl = getPackageUrl(key, version);
|
|
85
|
+
const comp = {
|
|
86
|
+
type: 'library',
|
|
87
|
+
'bom-ref': purl,
|
|
88
|
+
name,
|
|
89
|
+
version,
|
|
90
|
+
description,
|
|
91
|
+
purl,
|
|
92
|
+
externalReferences: []
|
|
93
|
+
};
|
|
94
|
+
if (licenses && licenses[0] && licenses[0].name) {
|
|
95
|
+
comp.licenses = [
|
|
96
|
+
{
|
|
97
|
+
license: {
|
|
98
|
+
id: licenses[0].name
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
if (repoUrl) {
|
|
104
|
+
comp.externalReferences.push({
|
|
105
|
+
type: 'vcs',
|
|
106
|
+
url: repoUrl
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (homepageUrl) {
|
|
110
|
+
comp.externalReferences.push({
|
|
111
|
+
type: 'website',
|
|
112
|
+
url: homepageUrl
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return comp;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function dependencyToSpdxComponent(dependency) {
|
|
119
|
+
const {
|
|
120
|
+
name, key, homepageUrl, repoUrl, licenses, versions
|
|
121
|
+
} = dependency;
|
|
122
|
+
const version = versions && versions[0];
|
|
123
|
+
const comp = {
|
|
124
|
+
SPDXID: `SPDXRef-${getSpdxFormattedKey(key)}`,
|
|
125
|
+
// TODO implement Copyright meta
|
|
126
|
+
copyrightText: '',
|
|
127
|
+
filesAnalyzed: false,
|
|
128
|
+
name,
|
|
129
|
+
versionInfo: version
|
|
130
|
+
};
|
|
131
|
+
if (licenses && licenses[0] && licenses[0].name) {
|
|
132
|
+
comp.licenseConcluded = licenses[0].name;
|
|
133
|
+
comp.licenseDeclared = licenses[0].name;
|
|
134
|
+
comp.licenseInfoFromFiles = [licenses[0].name];
|
|
135
|
+
}
|
|
136
|
+
if (repoUrl) {
|
|
137
|
+
comp.downloadLocation = repoUrl;
|
|
138
|
+
}
|
|
139
|
+
if (homepageUrl) {
|
|
140
|
+
comp.homepage = homepageUrl;
|
|
141
|
+
}
|
|
142
|
+
return comp;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function handleDependency(list, dependency, type, relationships, parent) {
|
|
146
|
+
let component;
|
|
147
|
+
if (type === 'cydx') {
|
|
148
|
+
component = dependencyToCydxComponent(dependency);
|
|
149
|
+
} else if (type === 'spdx') {
|
|
150
|
+
component = dependencyToSpdxComponent(dependency);
|
|
151
|
+
} else {
|
|
152
|
+
component = dependency;
|
|
153
|
+
}
|
|
154
|
+
if (component) {
|
|
155
|
+
const hasComponent = list.find((item) => (item.SPDXID && item.SPDXID === component.SPDXID)
|
|
156
|
+
|| (item['bom-ref'] && item['bom-ref'] === component['bom-ref']));
|
|
157
|
+
if (!hasComponent) {
|
|
158
|
+
list.push(component);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (relationships && parent) {
|
|
162
|
+
if (parent.creationInfo) {
|
|
163
|
+
relationships.push({
|
|
164
|
+
spdxElementId: parent.SPDXID,
|
|
165
|
+
relatedSpdxElement: component.SPDXID,
|
|
166
|
+
relationshipType: 'DESCRIBES'
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
relationships.push({
|
|
170
|
+
spdxElementId: parent.SPDXID,
|
|
171
|
+
relatedSpdxElement: component.SPDXID,
|
|
172
|
+
relationshipType: 'CONTAINS'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (dependency.dependencies) {
|
|
176
|
+
dependency.dependencies.forEach((child) => {
|
|
177
|
+
handleDependency(list, child, type, relationships, component);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
Convertor.scanToCydx = function scanTo(scan) {
|
|
183
|
+
const date = new Date();
|
|
184
|
+
const cydx = {
|
|
185
|
+
bomFormat: 'CycloneDX',
|
|
186
|
+
specVersion: '1.3',
|
|
187
|
+
serialNumber: 'urn:uuid:ea788421-7eb0-448b-833e-b32dd0f39d0c',
|
|
188
|
+
version: 1,
|
|
189
|
+
metadata: {
|
|
190
|
+
timestamp: date.toISOString(),
|
|
191
|
+
tools: [
|
|
192
|
+
{
|
|
193
|
+
vendor: 'CycloneDX',
|
|
194
|
+
name: 'Node.js module',
|
|
195
|
+
version: '3.6.0'
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
components: []
|
|
200
|
+
};
|
|
201
|
+
if (scan.dependencies && scan.dependencies[0]) {
|
|
202
|
+
cydx.components = [];
|
|
203
|
+
handleDependency(cydx.components, scan.dependencies[0], 'cydx');
|
|
204
|
+
if (cydx.components.length > 0) {
|
|
205
|
+
// eslint-disable-next-line prefer-destructuring
|
|
206
|
+
cydx.metadata.component = cydx.components[0];
|
|
207
|
+
cydx.components.shift();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return cydx;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
Convertor.scanToSpdx = function scanTo(scan) {
|
|
214
|
+
const date = new Date();
|
|
215
|
+
const spdx = {
|
|
216
|
+
SPDXID: 'SPDXRef-DOCUMENT',
|
|
217
|
+
spdxVersion: 'SPDX-2.0',
|
|
218
|
+
creationInfo: {
|
|
219
|
+
created: date.toISOString(),
|
|
220
|
+
creators: [
|
|
221
|
+
'Tool: ts-node-client > 1.8.1',
|
|
222
|
+
'Organization: TrustSource'
|
|
223
|
+
],
|
|
224
|
+
licenseListVersion: '2.5'
|
|
225
|
+
},
|
|
226
|
+
dataLicense: 'CC0-1.0'
|
|
227
|
+
};
|
|
228
|
+
if (scan.dependencies && scan.dependencies[0]) {
|
|
229
|
+
spdx.packages = [];
|
|
230
|
+
spdx.relationships = [];
|
|
231
|
+
handleDependency(spdx.packages, scan.dependencies[0], 'spdx', spdx.relationships, spdx);
|
|
232
|
+
if (spdx.packages.length > 0) {
|
|
233
|
+
const first = spdx.packages[0];
|
|
234
|
+
spdx.name = first.name;
|
|
235
|
+
spdx.documentDescribes = [first.SPDXID];
|
|
236
|
+
spdx.documentNamespace = `https://app.trustsource.io/spdx/${spdx.name}`;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return spdx;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
module.exports = Convertor;
|
|
244
|
+
|
package/lib/dependency.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**********************************************************
|
|
3
3
|
* Copyright (c) 2017. Enterprise Architecture Group, EACG
|
|
4
4
|
*
|
|
5
|
-
* SPDX-License-Identifier:
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*********************************************************/
|
|
7
7
|
/* eslint-enable */
|
|
8
8
|
|
|
@@ -21,7 +21,9 @@ function Dependency(name, version, keyPrefix, description, priv, licenses, homep
|
|
|
21
21
|
this.dependencies = [];
|
|
22
22
|
|
|
23
23
|
Object.defineProperty(this, 'versions', {
|
|
24
|
-
get() {
|
|
24
|
+
get() {
|
|
25
|
+
return versions.sort(compare);
|
|
26
|
+
},
|
|
25
27
|
enumerable: true
|
|
26
28
|
});
|
|
27
29
|
|
package/lib/pkg.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**********************************************************
|
|
3
|
+
* Copyright (c) 2022. Enterprise Architecture Group, EACG
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*********************************************************/
|
|
7
|
+
/* eslint-enable */
|
|
8
|
+
|
|
9
|
+
const PackageURL = {};
|
|
10
|
+
|
|
11
|
+
PackageURL.get = function get(manager, org, key, version) {
|
|
12
|
+
// scheme:type/namespace/name@version?qualifiers#subpath
|
|
13
|
+
const parts = [];
|
|
14
|
+
let partVersion;
|
|
15
|
+
if (manager) {
|
|
16
|
+
parts.push(encodeURI(manager));
|
|
17
|
+
}
|
|
18
|
+
if (org) {
|
|
19
|
+
parts.push(encodeURI(org).replace('%3A', ':'));
|
|
20
|
+
}
|
|
21
|
+
if (key) {
|
|
22
|
+
parts.push(encodeURI(key).replace('%3A', ':'));
|
|
23
|
+
}
|
|
24
|
+
if (version) {
|
|
25
|
+
partVersion = `@${encodeURI(version).replace('%3A', ':')}`;
|
|
26
|
+
}
|
|
27
|
+
return `pkg:${parts.join('/')}${partVersion}`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
module.exports = PackageURL;
|
|
32
|
+
|
package/lib/rest-client.js
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*********************************************************/
|
|
7
7
|
/* eslint-enable */
|
|
8
|
-
|
|
9
|
-
const request = require('request');
|
|
8
|
+
const axios = require('axios').default;
|
|
10
9
|
const debuglog = (require('debuglog'))('ts-rest-client');
|
|
11
10
|
const pckgJson = require('../package.json');
|
|
12
11
|
|
|
@@ -21,7 +20,7 @@ function RestClient(options) {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
function checkWarnings(options) {
|
|
24
|
-
return options.
|
|
23
|
+
return options.breakOnWarnings;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
function hasWarnings(analysis) {
|
|
@@ -30,7 +29,7 @@ function hasWarnings(analysis) {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
function checkViolations(options) {
|
|
33
|
-
return options.
|
|
32
|
+
return options.breakOnViolations;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
function hasViolations(analysis) {
|
|
@@ -38,45 +37,42 @@ function hasViolations(analysis) {
|
|
|
38
37
|
return stats ? stats.legal.violations + stats.vulnerability.violations + stats.viability.violations + stats.versioning.violations : 0;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
function checkAnalysisResults(options, getReqOpts, cb,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
debuglog('unexpected getResponse: error=', getError, 'response=', getResponse);
|
|
62
|
-
const result = {
|
|
63
|
-
message: 'unexpected getResponse'
|
|
64
|
-
};
|
|
65
|
-
if (getError) {
|
|
66
|
-
result.error = getError;
|
|
67
|
-
}
|
|
68
|
-
if (getResponse && getResponse.statusCode) {
|
|
69
|
-
result.code = getResponse.statusCode;
|
|
70
|
-
}
|
|
71
|
-
if (getBody) {
|
|
72
|
-
result.body = getBody;
|
|
40
|
+
function checkAnalysisResults(options, getReqOpts, cb, i) {
|
|
41
|
+
axios(getReqOpts)
|
|
42
|
+
.then((response) => {
|
|
43
|
+
const scanData = response && response.data;
|
|
44
|
+
if (response && response.status === 200 && scanData && scanData.analysisStatus === 'Finished') {
|
|
45
|
+
if (checkViolations(options) && hasViolations(scanData)) {
|
|
46
|
+
cb(`Analysis found ${hasViolations(scanData)} violations, see more details: ${response.data.url}`);
|
|
47
|
+
}
|
|
48
|
+
if (checkWarnings(options) && hasWarnings(scanData)) {
|
|
49
|
+
cb(`Analysis found ${hasWarnings(scanData)} warnings, see more details: ${response.data.url}`);
|
|
50
|
+
}
|
|
51
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
52
|
+
cb(null, { scanId: scanData._id });
|
|
53
|
+
} else {
|
|
54
|
+
const retryPeriod = i * 5000;
|
|
55
|
+
console.log('Analysis is', ((scanData && scanData.analysisStatus) || 'Scheduled'), 'Retry in ', retryPeriod / 1000, 'sec');
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
i += 1;
|
|
58
|
+
checkAnalysisResults(options, getReqOpts, cb, i);
|
|
59
|
+
}, retryPeriod);
|
|
73
60
|
}
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
})
|
|
62
|
+
.catch((error) => {
|
|
63
|
+
const errorData = error && typeof error.toJSON === 'function' ? error.toJSON() : false;
|
|
64
|
+
if (errorData && (errorData.status === 404)) {
|
|
65
|
+
const retryPeriod = i * 5000;
|
|
66
|
+
console.log('Analysis is Scheduled', 'Retry in ', retryPeriod / 1000, 'sec');
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
i += 1;
|
|
69
|
+
checkAnalysisResults(options, getReqOpts, cb, i);
|
|
70
|
+
}, retryPeriod);
|
|
71
|
+
} else {
|
|
72
|
+
debuglog('unexpected getResponse: error=', error);
|
|
73
|
+
cb(JSON.stringify(error));
|
|
76
74
|
}
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
});
|
|
75
|
+
});
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
exports.RestClient = RestClient;
|
|
@@ -86,8 +82,8 @@ RestClient.prototype.transfer = function transfer(scan, cb) {
|
|
|
86
82
|
debuglog('transfer started, options:', options);
|
|
87
83
|
|
|
88
84
|
const reqOpts = options.requestOptions || {};
|
|
89
|
-
reqOpts.method = '
|
|
90
|
-
reqOpts.
|
|
85
|
+
reqOpts.method = 'post';
|
|
86
|
+
reqOpts.url = `${options.url}/api/v1/scans`;
|
|
91
87
|
if (options.proxy) {
|
|
92
88
|
reqOpts.proxy = options.proxy;
|
|
93
89
|
}
|
|
@@ -97,12 +93,12 @@ RestClient.prototype.transfer = function transfer(scan, cb) {
|
|
|
97
93
|
'X-ApiKey': options.apiKey
|
|
98
94
|
};
|
|
99
95
|
reqOpts.json = true;
|
|
100
|
-
reqOpts.
|
|
96
|
+
reqOpts.data = scan;
|
|
101
97
|
if (options.branch) {
|
|
102
|
-
reqOpts.
|
|
98
|
+
reqOpts.data.branch = options.branch;
|
|
103
99
|
}
|
|
104
100
|
if (options.tag) {
|
|
105
|
-
reqOpts.
|
|
101
|
+
reqOpts.data.tag = options.tag;
|
|
106
102
|
}
|
|
107
103
|
if (options.binaryLinks) {
|
|
108
104
|
let links = options.binaryLinks.split(',');
|
|
@@ -110,40 +106,24 @@ RestClient.prototype.transfer = function transfer(scan, cb) {
|
|
|
110
106
|
links = links.map((value) => ({
|
|
111
107
|
name: value
|
|
112
108
|
}));
|
|
113
|
-
reqOpts.
|
|
109
|
+
reqOpts.data.binaryLinks = links;
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (options.brakeOnWarnings || options.brakeOnViolations) {
|
|
112
|
+
return axios(reqOpts)
|
|
113
|
+
.then((response) => {
|
|
114
|
+
if (options.breakOnWarnings || options.breakOnViolations) {
|
|
120
115
|
const getReqOpts = reqOpts;
|
|
121
|
-
getReqOpts.method = '
|
|
122
|
-
getReqOpts.
|
|
123
|
-
delete getReqOpts.
|
|
116
|
+
getReqOpts.method = 'get';
|
|
117
|
+
getReqOpts.url += `/${response.data.scanId}`;
|
|
118
|
+
delete getReqOpts.data;
|
|
124
119
|
let i = 1; // eslint-disable-line prefer-const
|
|
125
|
-
checkAnalysisResults(options, getReqOpts, cb,
|
|
120
|
+
checkAnalysisResults(options, getReqOpts, cb, i);
|
|
126
121
|
} else {
|
|
127
|
-
cb(null,
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
debuglog('unexpected response: error=', error, 'response=', response);
|
|
131
|
-
const result = {
|
|
132
|
-
message: 'unexpected response'
|
|
133
|
-
};
|
|
134
|
-
if (error) {
|
|
135
|
-
result.error = error;
|
|
136
|
-
}
|
|
137
|
-
if (response && response.statusCode) {
|
|
138
|
-
result.code = response.statusCode;
|
|
122
|
+
cb(null, response.data);
|
|
139
123
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
cb(result);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
124
|
+
})
|
|
125
|
+
.catch((error) => {
|
|
126
|
+
debuglog('unexpected response: error=', error);
|
|
127
|
+
cb(JSON.stringify(error));
|
|
128
|
+
});
|
|
149
129
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-node-client",
|
|
3
3
|
"description": "npm / node module to transfer dependency information to TrustSource server.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://app.trustsource.io/",
|
|
7
7
|
"author": {
|
|
@@ -22,28 +22,27 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"scan": "node ./bin/ts-node-client.js",
|
|
25
|
-
"scan-with-brakes": "ts-node-client --
|
|
26
|
-
"scan-
|
|
25
|
+
"scan-with-brakes": "node ./bin/ts-node-client.js --breakOnViolations true --breakOnWarnings false",
|
|
26
|
+
"scan-to-file": "node ./bin/ts-node-client.js --saveAs test --saveAsFormat cydx",
|
|
27
27
|
"lint": "eslint bin lib test",
|
|
28
28
|
"lint-fix": "eslint bin lib test --fix",
|
|
29
29
|
"test": "mocha test",
|
|
30
30
|
"precommit": "npm run lint && npm run test"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"semver": "
|
|
37
|
-
"yargs": "^
|
|
33
|
+
"npm": "6.14.17",
|
|
34
|
+
"axios": "0.26.1",
|
|
35
|
+
"debuglog": "1.0.1",
|
|
36
|
+
"semver": "7.3.5",
|
|
37
|
+
"yargs": "^17.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"eslint": "^7.32.0",
|
|
41
41
|
"eslint-config-airbnb-base": "^14.2.1",
|
|
42
42
|
"eslint-plugin-import": "^2.25.2",
|
|
43
43
|
"eslint-plugin-sonarjs": "^0.10.0",
|
|
44
|
-
"mocha": "^9.
|
|
45
|
-
"nock": "^12.0.3"
|
|
46
|
-
"ts-node-client": "1.5.2"
|
|
44
|
+
"mocha": "^9.2.2",
|
|
45
|
+
"nock": "^12.0.3"
|
|
47
46
|
},
|
|
48
47
|
"keywords": [
|
|
49
48
|
"node",
|
|
@@ -51,6 +50,8 @@
|
|
|
51
50
|
"meteor",
|
|
52
51
|
"plugin",
|
|
53
52
|
"opensource",
|
|
53
|
+
"cydx",
|
|
54
|
+
"spdx",
|
|
54
55
|
"dependency-analysis"
|
|
55
56
|
]
|
|
56
57
|
}
|
package/test/error-test.js
CHANGED
package/test/rest-test.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const assert = require('assert');
|
|
11
11
|
const nock = require('nock');
|
|
12
|
-
const { RestClient } = require('../lib/rest-client');
|
|
12
|
+
const { RestClient } = require('../lib/rest-client');
|
|
13
13
|
|
|
14
14
|
const JSON_TYPE = 'application/json';
|
|
15
15
|
const url = 'http://localhost:3000';
|
|
@@ -39,10 +39,10 @@ describe('RestClient', () => {
|
|
|
39
39
|
let restClient;
|
|
40
40
|
|
|
41
41
|
beforeEach(() => {
|
|
42
|
-
restClient = new RestClient({ url: 'http://localhost:3000' });
|
|
42
|
+
restClient = new RestClient({ url: 'http://localhost:3000', apiKey: 'test' });
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
it('should call callback with response data if no error
|
|
45
|
+
it('should call callback with response data if no error occurs', (done) => {
|
|
46
46
|
nock(url, {
|
|
47
47
|
reqheaders: {
|
|
48
48
|
'Content-Type': JSON_TYPE
|