@skaleagents/swarm 0.6.5 → 0.6.6
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/iac/parse.js +54 -16
- package/dist/iac/rules.js +34 -6
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/iac/parse.js
CHANGED
|
@@ -278,12 +278,31 @@ export function parseIac(content, requested = "auto") {
|
|
|
278
278
|
warnings.push("Intrinsic functions and expressions are not evaluated. Only literal configuration is checked.");
|
|
279
279
|
return { format, resources, warnings };
|
|
280
280
|
}
|
|
281
|
-
function
|
|
282
|
-
const
|
|
281
|
+
function programLocation(content, offset, path) {
|
|
282
|
+
const before = content.slice(0, offset);
|
|
283
|
+
return {
|
|
284
|
+
line: before.split("\n").length,
|
|
285
|
+
column: offset - before.lastIndexOf("\n"),
|
|
286
|
+
path,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function stripProgramComments(content) {
|
|
290
|
+
return content
|
|
283
291
|
.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " "))
|
|
284
292
|
.replace(/(^|[^:])\/\/[^\n]*/g, (match) => match.replace(/[^\n]/g, " "))
|
|
285
293
|
.replace(/(^|\n)\s*#[^\n]*/g, (match) => match.replace(/[^\n]/g, " "));
|
|
294
|
+
}
|
|
295
|
+
function parsePulumiProgram(content) {
|
|
296
|
+
const searchable = stripProgramComments(content);
|
|
286
297
|
const resources = [];
|
|
298
|
+
const addProgram = (id, type, value, offset, path) => {
|
|
299
|
+
resources.push({
|
|
300
|
+
id,
|
|
301
|
+
type,
|
|
302
|
+
value,
|
|
303
|
+
locate: () => programLocation(content, offset, path),
|
|
304
|
+
});
|
|
305
|
+
};
|
|
287
306
|
for (const match of searchable.matchAll(/(?:0\.0\.0\.0\/0|::\/0)/g)) {
|
|
288
307
|
const offset = match.index ?? 0;
|
|
289
308
|
const window = searchable.slice(Math.max(0, offset - 1500), offset);
|
|
@@ -291,26 +310,45 @@ function parsePulumiProgram(content) {
|
|
|
291
310
|
const egressAt = window.toLowerCase().lastIndexOf("egress");
|
|
292
311
|
if (ingressAt < 0 || egressAt > ingressAt)
|
|
293
312
|
continue;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
313
|
+
addProgram(`pulumi.program.ingress.${resources.length + 1}`, "pulumi:program/securityGroup:SecurityGroup", { properties: { ingress: [{ cidrBlocks: [match[0]] }] } }, offset, "ingress.cidrBlocks");
|
|
314
|
+
}
|
|
315
|
+
const database = {};
|
|
316
|
+
const publicDb = /publicly_?accessible\s*[:=]\s*true\b/i.exec(searchable);
|
|
317
|
+
const unencrypted = /storage_?encrypted\s*[:=]\s*false\b/i.exec(searchable);
|
|
318
|
+
const noBackup = /backup_?retention_?period\s*[:=]\s*0\b/i.exec(searchable);
|
|
319
|
+
if (publicDb)
|
|
320
|
+
database.publicly_accessible = true;
|
|
321
|
+
if (unencrypted)
|
|
322
|
+
database.storage_encrypted = false;
|
|
323
|
+
if (noBackup)
|
|
324
|
+
database.backup_retention_period = 0;
|
|
325
|
+
const databaseMatch = publicDb ?? unencrypted ?? noBackup;
|
|
326
|
+
if (databaseMatch) {
|
|
327
|
+
addProgram("pulumi.program.database", "aws_db_instance", { properties: database }, databaseMatch.index, "database");
|
|
328
|
+
}
|
|
329
|
+
const publicAcl = /(?:acl|accessControl)\s*[:=]\s*["']public-read(?:-write)?["']/i.exec(searchable);
|
|
330
|
+
if (publicAcl) {
|
|
331
|
+
addProgram("pulumi.program.bucket", "aws_s3_bucket", { properties: { acl: "public-read" } }, publicAcl.index, "acl");
|
|
332
|
+
}
|
|
333
|
+
for (const match of searchable.matchAll(/actions?\s*[:=]\s*(?:\[\s*)?["']\*["']/gi)) {
|
|
334
|
+
const offset = match.index ?? 0;
|
|
335
|
+
const before = searchable.slice(Math.max(0, offset - 300), offset);
|
|
336
|
+
const effects = [
|
|
337
|
+
...before.matchAll(/(?:effect|Effect)\s*[:=]\s*["']?(Allow|Deny)["']?/gi),
|
|
338
|
+
];
|
|
339
|
+
const nearest = effects.at(-1)?.[1] ?? "";
|
|
340
|
+
if (!/^allow$/i.test(nearest))
|
|
341
|
+
continue;
|
|
342
|
+
addProgram("pulumi.program.policy", "pulumi:program/iam:Policy", { properties: { effect: "Allow", actions: ["*"] } }, offset, "actions");
|
|
343
|
+
break;
|
|
305
344
|
}
|
|
345
|
+
addProgram("pulumi.program.source", "pulumi:program/source:Source", { program: searchable }, 0, "program");
|
|
306
346
|
return {
|
|
307
347
|
format: "pulumi",
|
|
308
348
|
resources,
|
|
309
349
|
warnings: [
|
|
310
350
|
"Pulumi programs are scanned as text. Computed values, stacks, and external modules are not evaluated.",
|
|
311
|
-
|
|
312
|
-
? []
|
|
313
|
-
: ["No literal ingress CIDRs were found in the Pulumi program."]),
|
|
351
|
+
"Database, storage, IAM, credential, and network checks use literal settings in the submitted program.",
|
|
314
352
|
],
|
|
315
353
|
};
|
|
316
354
|
}
|
package/dist/iac/rules.js
CHANGED
|
@@ -242,15 +242,43 @@ function walk(value, visit, path = []) {
|
|
|
242
242
|
walk(child, visit, [...path, key]);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
+
function composePortIsPublic(port) {
|
|
246
|
+
if (typeof port === "string") {
|
|
247
|
+
const value = port.trim();
|
|
248
|
+
if (/^(?:127\.0\.0\.1|localhost):/.test(value))
|
|
249
|
+
return false;
|
|
250
|
+
if (value.includes("0.0.0.0"))
|
|
251
|
+
return true;
|
|
252
|
+
return /^\d+:\d+(?:\/[A-Za-z0-9]+)?$/.test(value);
|
|
253
|
+
}
|
|
254
|
+
const mapping = object(port);
|
|
255
|
+
const host = mapping.host_ip ?? mapping.hostIp;
|
|
256
|
+
if (host === "127.0.0.1" || host === "localhost")
|
|
257
|
+
return false;
|
|
258
|
+
return host === "0.0.0.0" || mapping.published != null || mapping.target != null;
|
|
259
|
+
}
|
|
245
260
|
function checkCompose(resource, check) {
|
|
246
261
|
const service = resource.value;
|
|
262
|
+
const image = typeof service.image === "string" ? service.image : "";
|
|
263
|
+
const limits = object(object(object(service.deploy).resources).limits);
|
|
264
|
+
const user = service.user;
|
|
247
265
|
check("K8S001", service.privileged === true, ["privileged"]);
|
|
248
|
-
check("
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
266
|
+
check("K8S002", service.network_mode === "host", ["network_mode"]);
|
|
267
|
+
check("K8S003", array(service.volumes).some((volume) => {
|
|
268
|
+
const text = typeof volume === "string"
|
|
269
|
+
? volume
|
|
270
|
+
: String(object(volume).source ?? object(volume).target ?? "");
|
|
271
|
+
return (text.includes("docker.sock") ||
|
|
272
|
+
/(?:^|:)\/:/.test(text) ||
|
|
273
|
+
text.startsWith("/var/run/"));
|
|
274
|
+
}), ["volumes"]);
|
|
275
|
+
check("K8S004", user === "root" || user === "0" || user === 0, ["user"]);
|
|
276
|
+
check("K8S006", array(service.cap_add).some((cap) => ["ALL", "SYS_ADMIN", "NET_ADMIN", "SYS_PTRACE"].includes(String(cap).toUpperCase())), ["cap_add"]);
|
|
277
|
+
check("K8S007", image !== "" && (image.endsWith(":latest") || !/[:@]/.test(image)), ["image"]);
|
|
278
|
+
check("K8S008", service.mem_limit == null &&
|
|
279
|
+
limits.memory == null &&
|
|
280
|
+
service.mem_reservation == null, ["mem_limit"]);
|
|
281
|
+
check("NET001", array(service.ports).some(composePortIsPublic), ["ports"]);
|
|
254
282
|
}
|
|
255
283
|
function checkCloud(resource, resources, format, check) {
|
|
256
284
|
const tf = format === "terraform";
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.6.
|
|
1
|
+
export declare const VERSION = "0.6.6";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.6.
|
|
1
|
+
export const VERSION = "0.6.6";
|