mobbdev 0.0.9 → 0.0.10
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/index.mjs +1 -1
- package/package.json +6 -6
- package/src/github.mjs +19 -26
- package/src/gql.mjs +19 -15
- package/src/index.mjs +11 -1
- package/src/upload-file.mjs +7 -5
package/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobbdev",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Automated secure code remediation tool",
|
|
5
|
-
"main": "index.
|
|
5
|
+
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "prettier --check . && eslint **/*.mjs",
|
|
8
8
|
"lint:fix": "prettier --write . && eslint --fix **/*.mjs",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"configstore": "6.0.0",
|
|
20
20
|
"dotenv": "16.0.3",
|
|
21
21
|
"extract-zip": "2.0.1",
|
|
22
|
-
"
|
|
23
|
-
"got": "12.6.0",
|
|
22
|
+
"node-fetch": "3.3.1",
|
|
24
23
|
"open": "8.4.2",
|
|
24
|
+
"semver": "7.5.0",
|
|
25
25
|
"snyk": "1.1118.0",
|
|
26
26
|
"tmp": "0.2.1",
|
|
27
27
|
"zod": "3.21.4"
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"prettier": "2.8.4"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
36
|
+
"node": ">=12.20.0"
|
|
37
37
|
},
|
|
38
|
+
"type": "module",
|
|
38
39
|
"files": [
|
|
39
40
|
"bin",
|
|
40
41
|
"src",
|
|
41
|
-
"index.mjs",
|
|
42
42
|
".env",
|
|
43
43
|
"README.md",
|
|
44
44
|
"LICENSE",
|
package/src/github.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import got from 'got';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
|
-
import { promisify } from 'node:util';
|
|
4
2
|
import stream from 'node:stream';
|
|
5
|
-
import extract from 'extract-zip';
|
|
6
3
|
import path from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import extract from 'extract-zip';
|
|
7
7
|
|
|
8
8
|
const pipeline = promisify(stream.pipeline);
|
|
9
9
|
|
|
@@ -18,44 +18,37 @@ export async function getDefaultBranch(repoUrl) {
|
|
|
18
18
|
slug = slug.substring(0, slug.length - '.git'.length);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return repoInfo.default_branch;
|
|
31
|
-
} catch (e) {
|
|
21
|
+
const response = await fetch(`https://api.github.com/repos/${slug}`, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
headers: {
|
|
24
|
+
Accept: 'application/vnd.github+json',
|
|
25
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
32
30
|
throw new Error(
|
|
33
31
|
`Can't get default branch, make sure the repository is public: ${repoUrl}.`
|
|
34
32
|
);
|
|
35
33
|
}
|
|
34
|
+
|
|
35
|
+
const repoInfo = await response.json();
|
|
36
|
+
|
|
37
|
+
return repoInfo.default_branch;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export async function downloadRepo(repoUrl, reference, dirname) {
|
|
39
41
|
const zipFilePath = path.join(dirname, 'repo.zip');
|
|
40
|
-
const
|
|
42
|
+
const response = await fetch(`${repoUrl}/zipball/${reference}`);
|
|
41
43
|
const fileWriterStream = fs.createWriteStream(zipFilePath);
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
if (transferred > 0) {
|
|
45
|
-
console.log(
|
|
46
|
-
`Progress: ${transferred} (${Math.round(percent * 100)}%) ...`
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await pipeline(downloadStream, fileWriterStream);
|
|
45
|
+
await pipeline(response.body, fileWriterStream);
|
|
52
46
|
await extract(zipFilePath, { dir: dirname });
|
|
53
47
|
|
|
54
48
|
const repoRoot = fs
|
|
55
49
|
.readdirSync(dirname, { withFileTypes: true })
|
|
56
50
|
.filter((dirent) => dirent.isDirectory())
|
|
57
|
-
.map((dirent) => dirent.name)
|
|
58
|
-
.at(0);
|
|
51
|
+
.map((dirent) => dirent.name)[0];
|
|
59
52
|
|
|
60
53
|
return path.join(dirname, repoRoot);
|
|
61
54
|
}
|
package/src/gql.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
2
|
import { API_URL } from './constants.mjs';
|
|
3
3
|
|
|
4
4
|
const ME = `
|
|
@@ -48,40 +48,44 @@ mutation SubmitVulnerabilityReport($vulnerabilityReportFileName: String!, $fixRe
|
|
|
48
48
|
`;
|
|
49
49
|
|
|
50
50
|
export class GQLClient {
|
|
51
|
-
#token;
|
|
52
|
-
|
|
53
51
|
constructor(token) {
|
|
54
|
-
this
|
|
52
|
+
this._token = token;
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
async
|
|
58
|
-
const response = await
|
|
55
|
+
async _apiCall(query, variables = {}) {
|
|
56
|
+
const response = await fetch(API_URL, {
|
|
59
57
|
method: 'POST',
|
|
60
58
|
headers: {
|
|
61
|
-
authorization: `Bearer ${this
|
|
59
|
+
authorization: `Bearer ${this._token}`,
|
|
62
60
|
},
|
|
63
61
|
body: JSON.stringify({
|
|
64
62
|
query,
|
|
65
63
|
variables,
|
|
66
64
|
}),
|
|
67
|
-
})
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(`API call failed: ${response.status}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const data = await response.json();
|
|
68
72
|
|
|
69
|
-
if (
|
|
73
|
+
if (data.errors) {
|
|
70
74
|
throw new Error(`API error: ${response.errors[0].message}`);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
if (!
|
|
77
|
+
if (!data.data) {
|
|
74
78
|
throw new Error('No data returned for the API query.');
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
return
|
|
81
|
+
return data.data;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
async verifyToken() {
|
|
81
85
|
await this.createCommunityUser();
|
|
82
86
|
|
|
83
87
|
try {
|
|
84
|
-
await this
|
|
88
|
+
await this._apiCall(ME);
|
|
85
89
|
} catch (e) {
|
|
86
90
|
return false;
|
|
87
91
|
}
|
|
@@ -90,14 +94,14 @@ export class GQLClient {
|
|
|
90
94
|
|
|
91
95
|
async createCommunityUser() {
|
|
92
96
|
try {
|
|
93
|
-
await this
|
|
97
|
+
await this._apiCall(CREATE_COMMUNITY_USER);
|
|
94
98
|
} catch (e) {
|
|
95
99
|
// Ignore errors
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
async uploadS3BucketInfo() {
|
|
100
|
-
const data = await this
|
|
104
|
+
const data = await this._apiCall(UPLOAD_S3_BUCKET_INFO, {
|
|
101
105
|
fileName: 'report.json',
|
|
102
106
|
});
|
|
103
107
|
|
|
@@ -112,7 +116,7 @@ export class GQLClient {
|
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
async submitVulnerabilityReport(fixReportId, repoUrl, reference) {
|
|
115
|
-
await this
|
|
119
|
+
await this._apiCall(SUBMIT_VULNERABILITY_REPORT, {
|
|
116
120
|
fixReportId,
|
|
117
121
|
repoUrl,
|
|
118
122
|
reference,
|
package/src/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import semver from 'semver';
|
|
4
5
|
import open from 'open';
|
|
5
6
|
import Configstore from 'configstore';
|
|
6
7
|
import { GQLClient } from './gql.mjs';
|
|
@@ -14,6 +15,14 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
14
15
|
const packageJson = JSON.parse(
|
|
15
16
|
fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8')
|
|
16
17
|
);
|
|
18
|
+
|
|
19
|
+
if (!semver.satisfies(process.version, packageJson.engines.node)) {
|
|
20
|
+
console.error(
|
|
21
|
+
`${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
const config = new Configstore(packageJson.name, { token: '' });
|
|
18
27
|
|
|
19
28
|
export async function main(dirname, repoUrl) {
|
|
@@ -66,7 +75,8 @@ export async function main(dirname, repoUrl) {
|
|
|
66
75
|
reference
|
|
67
76
|
);
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
const results = ((report.runs || [])[0] || {}).results || [];
|
|
79
|
+
if (results.length === 0) {
|
|
70
80
|
console.log('Snyk has not found any vulnerabilities — nothing to fix.');
|
|
71
81
|
} else {
|
|
72
82
|
console.log(
|
package/src/upload-file.mjs
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import FormData from '
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import got from 'got';
|
|
1
|
+
import fetch, { FormData, fileFrom } from 'node-fetch';
|
|
4
2
|
|
|
5
3
|
export async function uploadFile(reportPath, url, uploadKey, uploadFields) {
|
|
6
4
|
const form = new FormData();
|
|
@@ -10,10 +8,14 @@ export async function uploadFile(reportPath, url, uploadKey, uploadFields) {
|
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
form.append('key', uploadKey);
|
|
13
|
-
form.append('file',
|
|
11
|
+
form.append('file', await fileFrom(reportPath));
|
|
14
12
|
|
|
15
|
-
await
|
|
13
|
+
const response = await fetch(url, {
|
|
16
14
|
method: 'POST',
|
|
17
15
|
body: form,
|
|
18
16
|
});
|
|
17
|
+
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`Failed to upload the report: ${response.status}`);
|
|
20
|
+
}
|
|
19
21
|
}
|