@fishawack/lab-env 5.4.0-beta.1 → 5.5.0-beta.1
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/CHANGELOG.md +44 -0
- package/_Ai/python-3.md +31 -28
- package/_Ai/workspace-@.md +4 -0
- package/_Test/_fixtures/content/config.json +36 -0
- package/_Test/_fixtures/content/content-0/blah.txt +1 -0
- package/_Test/content.js +517 -0
- package/cli.js +1 -0
- package/commands/content.js +115 -1
- package/commands/create/cmds/provision.js +1 -1
- package/commands/create/libs/aws-cloudfront-request.js +1 -1
- package/commands/create/libs/aws-cloudfront-response.js +1 -1
- package/commands/create/libs/vars.js +4 -0
- package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers-wordpress.conf +1 -1
- package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers.conf +1 -1
- package/commands/create/templates/elasticbeanstalk/.platform/nginx/conf.d/security_headers.conf +1 -1
- package/commands/helpers/content-pull.js +209 -0
- package/commands/helpers/content-request.js +223 -0
- package/commands/scan.js +49 -0
- package/commands/test.js +2 -15
- package/commands/workspace.js +160 -63
- package/package.json +3 -1
- package/python/0/docker-compose.yml +13 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Header set X-Content-Type-Options "nosniff"
|
|
2
|
-
Header set Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval'
|
|
2
|
+
Header set Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval' blob:;"
|
|
3
3
|
Header set X-Frame-Options 'sameorigin'
|
|
4
4
|
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
5
5
|
|
package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers.conf
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Header set X-Content-Type-Options "nosniff"
|
|
2
|
-
Header set Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline'
|
|
2
|
+
Header set Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' blob:;"
|
|
3
3
|
Header set X-Frame-Options 'sameorigin'
|
|
4
4
|
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
5
5
|
|
package/commands/create/templates/elasticbeanstalk/.platform/nginx/conf.d/security_headers.conf
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
add_header X-Content-Type-Options "nosniff";
|
|
2
|
-
add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline'
|
|
2
|
+
add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' blob:;";
|
|
3
3
|
add_header X-Frame-Options 'sameorigin';
|
|
4
4
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
const {
|
|
2
|
+
S3Client,
|
|
3
|
+
ListObjectsV2Command,
|
|
4
|
+
GetObjectCommand,
|
|
5
|
+
PutObjectCommand,
|
|
6
|
+
} = require("@aws-sdk/client-s3");
|
|
7
|
+
const { fromIni } = require("@aws-sdk/credential-providers");
|
|
8
|
+
const fs = require("fs-extra");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { colorize } = require("../create/libs/utilities");
|
|
11
|
+
|
|
12
|
+
function createClient(profile) {
|
|
13
|
+
return new S3Client({
|
|
14
|
+
region: process.env.AWS_REGION || "us-east-1",
|
|
15
|
+
credentials: fromIni({ profile }),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseBucketAndPrefix(location) {
|
|
20
|
+
const slashIndex = location.indexOf("/");
|
|
21
|
+
if (slashIndex === -1) {
|
|
22
|
+
return { Bucket: location, Prefix: "" };
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
Bucket: location.substring(0, slashIndex),
|
|
26
|
+
Prefix: location.substring(slashIndex + 1),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function listRemoteObjects(client, Bucket, Prefix) {
|
|
31
|
+
const objects = [];
|
|
32
|
+
let ContinuationToken = null;
|
|
33
|
+
|
|
34
|
+
do {
|
|
35
|
+
const res = await client.send(
|
|
36
|
+
new ListObjectsV2Command({
|
|
37
|
+
Bucket,
|
|
38
|
+
Prefix,
|
|
39
|
+
ContinuationToken,
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (res.Contents) {
|
|
44
|
+
for (const obj of res.Contents) {
|
|
45
|
+
objects.push({
|
|
46
|
+
Key: obj.Key,
|
|
47
|
+
LastModified: obj.LastModified,
|
|
48
|
+
Size: obj.Size,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ContinuationToken = res.NextContinuationToken;
|
|
54
|
+
} while (ContinuationToken);
|
|
55
|
+
|
|
56
|
+
return objects;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getLocalFiles(dir) {
|
|
60
|
+
const files = [];
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(dir)) {
|
|
63
|
+
return files;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const walk = (currentDir) => {
|
|
67
|
+
for (const entry of fs.readdirSync(currentDir, {
|
|
68
|
+
withFileTypes: true,
|
|
69
|
+
})) {
|
|
70
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
71
|
+
if (entry.isDirectory()) {
|
|
72
|
+
walk(fullPath);
|
|
73
|
+
} else if (entry.isFile()) {
|
|
74
|
+
const stat = fs.statSync(fullPath);
|
|
75
|
+
files.push({
|
|
76
|
+
path: fullPath,
|
|
77
|
+
relativePath: path.relative(dir, fullPath),
|
|
78
|
+
mtime: stat.mtime,
|
|
79
|
+
size: stat.size,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
walk(dir);
|
|
86
|
+
return files;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function uploadLocalToS3(client, Bucket, Prefix, saveTo) {
|
|
90
|
+
const localFiles = getLocalFiles(saveTo);
|
|
91
|
+
|
|
92
|
+
if (localFiles.length === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const remoteObjects = await listRemoteObjects(client, Bucket, Prefix);
|
|
97
|
+
const remoteMap = new Map();
|
|
98
|
+
for (const obj of remoteObjects) {
|
|
99
|
+
const relKey = Prefix ? obj.Key.substring(Prefix.length + 1) : obj.Key;
|
|
100
|
+
remoteMap.set(relKey, obj);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let uploaded = 0;
|
|
104
|
+
|
|
105
|
+
for (const local of localFiles) {
|
|
106
|
+
const remoteKey = Prefix
|
|
107
|
+
? `${Prefix}/${local.relativePath}`
|
|
108
|
+
: local.relativePath;
|
|
109
|
+
const remote = remoteMap.get(local.relativePath);
|
|
110
|
+
|
|
111
|
+
// rclone copy --update: skip if remote is newer or equal
|
|
112
|
+
if (remote && remote.LastModified >= local.mtime) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const body = fs.readFileSync(local.path);
|
|
117
|
+
|
|
118
|
+
await client.send(
|
|
119
|
+
new PutObjectCommand({
|
|
120
|
+
Bucket,
|
|
121
|
+
Key: remoteKey,
|
|
122
|
+
Body: body,
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
uploaded++;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (uploaded > 0) {
|
|
130
|
+
console.log(
|
|
131
|
+
colorize(` Uploaded ${uploaded} file(s) to S3`, "success"),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function downloadS3ToLocal(client, Bucket, Prefix, saveTo) {
|
|
137
|
+
const remoteObjects = await listRemoteObjects(client, Bucket, Prefix);
|
|
138
|
+
const localFiles = getLocalFiles(saveTo);
|
|
139
|
+
const localMap = new Map();
|
|
140
|
+
|
|
141
|
+
for (const local of localFiles) {
|
|
142
|
+
localMap.set(local.relativePath, local);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let downloaded = 0;
|
|
146
|
+
|
|
147
|
+
for (const obj of remoteObjects) {
|
|
148
|
+
// Skip "directory" markers
|
|
149
|
+
if (obj.Key.endsWith("/")) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const relPath = Prefix ? obj.Key.substring(Prefix.length + 1) : obj.Key;
|
|
154
|
+
|
|
155
|
+
if (!relPath) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const localPath = path.join(saveTo, relPath);
|
|
160
|
+
const local = localMap.get(relPath);
|
|
161
|
+
|
|
162
|
+
// rclone copy --update: skip if local is newer or equal
|
|
163
|
+
if (local && local.mtime >= obj.LastModified) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const res = await client.send(
|
|
168
|
+
new GetObjectCommand({ Bucket, Key: obj.Key }),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const chunks = [];
|
|
172
|
+
for await (const chunk of res.Body) {
|
|
173
|
+
chunks.push(chunk);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fs.mkdirpSync(path.dirname(localPath));
|
|
177
|
+
fs.writeFileSync(localPath, Buffer.concat(chunks));
|
|
178
|
+
downloaded++;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (downloaded > 0) {
|
|
182
|
+
console.log(
|
|
183
|
+
colorize(` Downloaded ${downloaded} file(s) from S3`, "success"),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function pullS3(contentItem, saveTo) {
|
|
189
|
+
const profile = contentItem["aws-s3"];
|
|
190
|
+
const client = createClient(profile);
|
|
191
|
+
const { Bucket, Prefix } = parseBucketAndPrefix(contentItem.location);
|
|
192
|
+
|
|
193
|
+
if (contentItem.sync) {
|
|
194
|
+
fs.mkdirpSync(saveTo);
|
|
195
|
+
await uploadLocalToS3(client, Bucket, Prefix, saveTo);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await downloadS3ToLocal(client, Bucket, Prefix, saveTo);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
pullS3,
|
|
203
|
+
parseBucketAndPrefix,
|
|
204
|
+
getLocalFiles,
|
|
205
|
+
listRemoteObjects,
|
|
206
|
+
uploadLocalToS3,
|
|
207
|
+
downloadS3ToLocal,
|
|
208
|
+
createClient,
|
|
209
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const glob = require("glob");
|
|
4
|
+
const { colorize } = require("../create/libs/utilities");
|
|
5
|
+
|
|
6
|
+
function urlJoin() {
|
|
7
|
+
return new URL(
|
|
8
|
+
path.join(...[].slice.call(arguments, 1)),
|
|
9
|
+
arguments[0],
|
|
10
|
+
).toString();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function image(src, options) {
|
|
14
|
+
let file = path.join(
|
|
15
|
+
options.saveTo,
|
|
16
|
+
"media",
|
|
17
|
+
src.replace(new RegExp(options.find), ""),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
let resolvedSrc = src;
|
|
21
|
+
|
|
22
|
+
// Fix for Contentful pathing not being a real URL
|
|
23
|
+
if (options.path.indexOf("contentful") > -1) {
|
|
24
|
+
resolvedSrc = `https:${resolvedSrc}`;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
new URL(resolvedSrc);
|
|
28
|
+
} catch {
|
|
29
|
+
resolvedSrc = urlJoin(options.path, resolvedSrc);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.mkdirpSync(path.dirname(file));
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(resolvedSrc);
|
|
36
|
+
console.log(
|
|
37
|
+
colorize(` Downloaded: ${path.basename(file)}`, "success"),
|
|
38
|
+
);
|
|
39
|
+
fs.writeFileSync(file, Buffer.from(await res.arrayBuffer()));
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(
|
|
42
|
+
colorize(
|
|
43
|
+
` Failed to download: ${resolvedSrc} - ${err.message}`,
|
|
44
|
+
"warning",
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function download(options) {
|
|
51
|
+
try {
|
|
52
|
+
const pLimit = (await import("p-limit")).default;
|
|
53
|
+
const limit = pLimit(5);
|
|
54
|
+
|
|
55
|
+
let arr = [];
|
|
56
|
+
|
|
57
|
+
glob.sync(
|
|
58
|
+
path.join(options.saveTo, options.bundle, `*.${options.ext}`),
|
|
59
|
+
).forEach((endpoint) => {
|
|
60
|
+
const data = fs.readFileSync(endpoint, { encoding: "utf8" });
|
|
61
|
+
|
|
62
|
+
// Find all values between quotes
|
|
63
|
+
const matches = data.match(/(["'])(?:(?=(\\?))\2.)*?\1/g);
|
|
64
|
+
if (matches) {
|
|
65
|
+
matches.forEach((d) => {
|
|
66
|
+
let value = JSON.parse(d);
|
|
67
|
+
|
|
68
|
+
if (new RegExp(options.find).test(value)) {
|
|
69
|
+
arr.push(value);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Make array of assets unique
|
|
76
|
+
arr = [...new Set(arr)];
|
|
77
|
+
|
|
78
|
+
await Promise.all(arr.map((d) => limit(() => image(d, options))));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log(
|
|
81
|
+
colorize(` Error downloading assets: ${e.message}`, "error"),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function rewrite(options) {
|
|
87
|
+
console.log(colorize(` Rewriting json to use local paths`, "success"));
|
|
88
|
+
|
|
89
|
+
glob.sync(
|
|
90
|
+
path.join(options.saveTo, options.bundle, `*.${options.ext}`),
|
|
91
|
+
).forEach((endpoint) => {
|
|
92
|
+
let data = fs.readFileSync(endpoint, { encoding: "utf8" });
|
|
93
|
+
|
|
94
|
+
// Find all values between quotes and rewrite matching URLs
|
|
95
|
+
data = data.replaceAll(/(["'])(?:(?=(\\?))\2.)*?\1/g, (d) => {
|
|
96
|
+
let value = JSON.parse(d);
|
|
97
|
+
|
|
98
|
+
if (new RegExp(options.find).test(value)) {
|
|
99
|
+
return `"${path.join("media/content", value.replace(new RegExp(options.find), ""))}"`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return d;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (options.type === "contentful") {
|
|
106
|
+
data = JSON.stringify(JSON.parse(data)[0]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fs.writeFileSync(endpoint, data, { encoding: "utf8" });
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function load(options) {
|
|
114
|
+
let data = [];
|
|
115
|
+
let index = 0;
|
|
116
|
+
let current;
|
|
117
|
+
|
|
118
|
+
do {
|
|
119
|
+
index++;
|
|
120
|
+
let uri = "";
|
|
121
|
+
|
|
122
|
+
if (options.type === "contentful") {
|
|
123
|
+
uri = urlJoin(
|
|
124
|
+
options.path,
|
|
125
|
+
`${options.api}${options.endpoint}&skip=${(index - 1) * 100}`,
|
|
126
|
+
);
|
|
127
|
+
} else {
|
|
128
|
+
uri = urlJoin(
|
|
129
|
+
options.path,
|
|
130
|
+
options.api,
|
|
131
|
+
`${options.endpoint}?per_page=1&page=${index}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const res = await fetch(uri);
|
|
136
|
+
const json = await res.json();
|
|
137
|
+
|
|
138
|
+
data = data.concat(json);
|
|
139
|
+
|
|
140
|
+
if (options.type === "contentful") {
|
|
141
|
+
current = Math.ceil(json.total / 100);
|
|
142
|
+
} else {
|
|
143
|
+
current = +res.headers.get("x-wp-totalpages");
|
|
144
|
+
}
|
|
145
|
+
} while (current && current !== index);
|
|
146
|
+
|
|
147
|
+
console.log(colorize(` Downloaded: ${options.endpoint}`, "success"));
|
|
148
|
+
|
|
149
|
+
const file = path.join(
|
|
150
|
+
options.saveTo,
|
|
151
|
+
options.bundle,
|
|
152
|
+
`${options.endpoint}.${options.ext}`,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
fs.mkdirpSync(path.dirname(file));
|
|
156
|
+
fs.writeFileSync(file, JSON.stringify(data));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function pullRequests(contentItems, srcBase) {
|
|
160
|
+
const pLimit = (await import("p-limit")).default;
|
|
161
|
+
const limit = pLimit(5);
|
|
162
|
+
|
|
163
|
+
// Load endpoints
|
|
164
|
+
const loadPromises = contentItems.flatMap((d, i) => {
|
|
165
|
+
if (!d.url) return [];
|
|
166
|
+
return d.endpoints.map((endpoint) =>
|
|
167
|
+
limit(() =>
|
|
168
|
+
load({
|
|
169
|
+
path: d.url,
|
|
170
|
+
api: d.api || "/wp-json/wp/v2/",
|
|
171
|
+
endpoint,
|
|
172
|
+
type: d.type || "wp",
|
|
173
|
+
ext: d.ext || "json",
|
|
174
|
+
saveTo:
|
|
175
|
+
d.saveTo ||
|
|
176
|
+
`${srcBase}/content/${d.key || `content-${i}`}`,
|
|
177
|
+
bundle: d.bundle ? "media/" : "",
|
|
178
|
+
}),
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
await Promise.all(loadPromises);
|
|
183
|
+
|
|
184
|
+
// Download assets referenced in json files
|
|
185
|
+
await Promise.all(
|
|
186
|
+
contentItems.map((d, i) => {
|
|
187
|
+
if (d.url && d.find !== null) {
|
|
188
|
+
return limit(() =>
|
|
189
|
+
download({
|
|
190
|
+
path: d.url,
|
|
191
|
+
ext: d.ext || "json",
|
|
192
|
+
saveTo:
|
|
193
|
+
d.saveTo ||
|
|
194
|
+
`${srcBase}/content/${d.key || `content-${i}`}`,
|
|
195
|
+
bundle: d.bundle ? "media/" : "",
|
|
196
|
+
find: d.find || `^https.*/wp-content/uploads`,
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Rewrite json files to use local paths
|
|
204
|
+
await Promise.all(
|
|
205
|
+
contentItems.map((d, i) => {
|
|
206
|
+
if (d.url && d.find !== null) {
|
|
207
|
+
return limit(() =>
|
|
208
|
+
rewrite({
|
|
209
|
+
ext: d.ext || "json",
|
|
210
|
+
type: d.type || "wp",
|
|
211
|
+
saveTo:
|
|
212
|
+
d.saveTo ||
|
|
213
|
+
`${srcBase}/content/${d.key || `content-${i}`}`,
|
|
214
|
+
bundle: d.bundle ? "media/" : "",
|
|
215
|
+
find: d.find || `^https.*/wp-content/uploads`,
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = { pullRequests, load, download, rewrite, image, urlJoin };
|
package/commands/scan.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const _ = require("../globals.js");
|
|
2
|
+
|
|
3
|
+
const run = () => {
|
|
4
|
+
_.up(() => {
|
|
5
|
+
const snykFlags = `--project-name=${_.repoSafe} --severity-threshold=high`;
|
|
6
|
+
|
|
7
|
+
const snykScan = (file, extra = "") =>
|
|
8
|
+
_.command(
|
|
9
|
+
"core",
|
|
10
|
+
`snyk test ${snykFlags} --file=${file} ${extra}`.trim(),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
_.command(
|
|
14
|
+
"core",
|
|
15
|
+
`if [ "$SNYK_TOKEN" ]; then snyk auth $SNYK_TOKEN; else echo 'SNYK_TOKEN is missing. Skipping Snyk scan.' && exit 0; fi`,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const scans = [["package-lock.json"]];
|
|
19
|
+
|
|
20
|
+
if (_.platform === "laravel") {
|
|
21
|
+
scans.push(["composer.lock", "--package-manager=composer"]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let failed = false;
|
|
25
|
+
|
|
26
|
+
scans.forEach(([file, extra = ""]) => {
|
|
27
|
+
try {
|
|
28
|
+
snykScan(file, extra);
|
|
29
|
+
} catch {
|
|
30
|
+
failed = true;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (failed) {
|
|
35
|
+
if (_.coreConfig.attributes.failOnScanErrors) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Scan found issues. Failing the build as failOnScanErrors flag is set to true.",
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
console.log(
|
|
41
|
+
"Scan found issues, but failOnScanErrors is not set. Continuing with execution.",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
module.exports = ["scan", "run security scan", () => {}, run];
|
|
49
|
+
module.exports.run = run;
|
package/commands/test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const _ = require("../globals.js");
|
|
2
|
+
const scan = require("./scan.js");
|
|
2
3
|
|
|
3
4
|
module.exports = [
|
|
4
5
|
"test",
|
|
@@ -23,21 +24,7 @@ module.exports = [
|
|
|
23
24
|
_.command("node", `node ace test`);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
_.command(
|
|
28
|
-
"core",
|
|
29
|
-
"if [ \"$SNYK_TOKEN\" ]; then snyk auth $SNYK_TOKEN && snyk test --severity-threshold=high; else echo 'SNYK_TOKEN is missing. Skipping Snyk scan.'; fi",
|
|
30
|
-
);
|
|
31
|
-
} catch (e) {
|
|
32
|
-
// Only stop execution if sync set to true in coreConfig; otherwise, log the error and continue
|
|
33
|
-
if (_.coreConfig.attributes.snyk) {
|
|
34
|
-
throw e;
|
|
35
|
-
} else {
|
|
36
|
-
console.log(
|
|
37
|
-
"Snyk found issues, but Snyk is not set to fail the build. Continuing with execution.",
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
27
|
+
scan.run();
|
|
41
28
|
});
|
|
42
29
|
},
|
|
43
30
|
];
|