@skaleagents/swarm 0.6.9 → 0.6.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/dist/iac/parse.d.ts +3 -1
- package/dist/iac/parse.js +189 -2
- package/dist/iac/rules.js +84 -0
- package/dist/server.js +7 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/iac/parse.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type IacFormat = "terraform" | "cloudformation" | "kubernetes" | "pulumi" | "compose" | "dockerfile" | "github";
|
|
1
|
+
export type IacFormat = "terraform" | "cloudformation" | "kubernetes" | "pulumi" | "compose" | "dockerfile" | "github" | "helm" | "ansible";
|
|
2
2
|
export type Path = (string | number)[];
|
|
3
3
|
export type Location = {
|
|
4
4
|
line: number;
|
|
@@ -25,6 +25,8 @@ export declare function looksLikeIac(content: string): boolean;
|
|
|
25
25
|
export declare function looksLikePulumiProgram(content: string): boolean;
|
|
26
26
|
export declare function looksLikeCompose(content: string): boolean;
|
|
27
27
|
export declare function looksLikeDockerfile(content: string): boolean;
|
|
28
|
+
export declare function looksLikeAnsible(content: string): boolean;
|
|
29
|
+
export declare function looksLikeHelm(content: string): boolean;
|
|
28
30
|
export declare function looksLikeGithubWorkflow(content: string): boolean;
|
|
29
31
|
export declare function looksLikePulumiYaml(content: string): boolean;
|
|
30
32
|
export declare function parseIac(content: string, requested?: IacFormat | "auto"): ParsedIac;
|
package/dist/iac/parse.js
CHANGED
|
@@ -42,7 +42,9 @@ export function looksLikeIac(content) {
|
|
|
42
42
|
looksLikePulumiProgram(content) ||
|
|
43
43
|
looksLikeCompose(content) ||
|
|
44
44
|
looksLikeDockerfile(content) ||
|
|
45
|
-
looksLikeGithubWorkflow(content)
|
|
45
|
+
looksLikeGithubWorkflow(content) ||
|
|
46
|
+
looksLikeHelm(content) ||
|
|
47
|
+
looksLikeAnsible(content));
|
|
46
48
|
}
|
|
47
49
|
export function looksLikePulumiProgram(content) {
|
|
48
50
|
return /(?:^|\n)\s*(?:import\s+\*\s+as\s+pulumi\b|import\s+pulumi\b|from\s+["']@pulumi\/)/.test(content);
|
|
@@ -56,6 +58,26 @@ export function looksLikeDockerfile(content) {
|
|
|
56
58
|
function topLevelKey(content, name) {
|
|
57
59
|
return new RegExp(`^(?:"${name}"|'${name}'|${name})\\s*:`, "m").test(content);
|
|
58
60
|
}
|
|
61
|
+
function playbookShape(content) {
|
|
62
|
+
if (topLevelKey(content, "hosts") && topLevelKey(content, "tasks"))
|
|
63
|
+
return true;
|
|
64
|
+
return /^\s*-\s+hosts\s*:/m.test(content) && /^\s*tasks\s*:/m.test(content);
|
|
65
|
+
}
|
|
66
|
+
export function looksLikeAnsible(content) {
|
|
67
|
+
if (looksLikeGithubWorkflow(content))
|
|
68
|
+
return false;
|
|
69
|
+
if (topLevelKey(content, "apiVersion") && topLevelKey(content, "kind"))
|
|
70
|
+
return false;
|
|
71
|
+
return playbookShape(content);
|
|
72
|
+
}
|
|
73
|
+
export function looksLikeHelm(content) {
|
|
74
|
+
if (looksLikeGithubWorkflow(content) || looksLikeAnsible(content))
|
|
75
|
+
return false;
|
|
76
|
+
const visible = maskNonCode(content);
|
|
77
|
+
if (!visible.includes("{{"))
|
|
78
|
+
return false;
|
|
79
|
+
return /^\s*(?:apiVersion|kind)\s*:/m.test(visible);
|
|
80
|
+
}
|
|
59
81
|
export function looksLikeGithubWorkflow(content) {
|
|
60
82
|
if (topLevelKey(content, "apiVersion") || topLevelKey(content, "kind"))
|
|
61
83
|
return false;
|
|
@@ -84,6 +106,14 @@ export function parseIac(content, requested = "auto") {
|
|
|
84
106
|
(requested === "auto" && looksLikeGithubWorkflow(content))) {
|
|
85
107
|
return parseGithubWorkflow(content);
|
|
86
108
|
}
|
|
109
|
+
if (requested === "ansible" ||
|
|
110
|
+
(requested === "auto" && looksLikeAnsible(content))) {
|
|
111
|
+
return parseAnsible(content);
|
|
112
|
+
}
|
|
113
|
+
if (requested === "helm" ||
|
|
114
|
+
(requested === "auto" && looksLikeHelm(content))) {
|
|
115
|
+
return parseHelm(content);
|
|
116
|
+
}
|
|
87
117
|
if (requested !== "terraform" &&
|
|
88
118
|
requested !== "compose" &&
|
|
89
119
|
looksLikePulumiProgram(content) &&
|
|
@@ -300,7 +330,7 @@ export function parseIac(content, requested = "auto") {
|
|
|
300
330
|
}
|
|
301
331
|
}
|
|
302
332
|
if (!format)
|
|
303
|
-
throw new ScanInputError("No IaC document found. Choose Terraform, CloudFormation, Kubernetes, Pulumi, Docker Compose, a Dockerfile,
|
|
333
|
+
throw new ScanInputError("No IaC document found. Choose Terraform, CloudFormation, Kubernetes, Pulumi, Docker Compose, a Dockerfile, a GitHub Actions workflow, a Helm template, or an Ansible playbook.");
|
|
304
334
|
if (!resources.length)
|
|
305
335
|
warnings.push("No resources found in the submitted document.");
|
|
306
336
|
if (/(?:!(?:Ref|Sub|GetAtt|If)\b|"(?:Ref|Fn::\w+)"\s*:|\$\{)/.test(content))
|
|
@@ -452,6 +482,41 @@ function runPipesToShell(text) {
|
|
|
452
482
|
/\b(?:curl|wget)\b/i.test(text) &&
|
|
453
483
|
/\|\s*(?:sudo\s+)?(?:\S*\/)?(?:ba)?sh\b/i.test(text));
|
|
454
484
|
}
|
|
485
|
+
function copySources(text) {
|
|
486
|
+
if (!/^(?:COPY|ADD)\b/i.test(text))
|
|
487
|
+
return undefined;
|
|
488
|
+
const rest = text.replace(/^(?:COPY|ADD)\b/i, "").trim();
|
|
489
|
+
if (rest.startsWith("[")) {
|
|
490
|
+
try {
|
|
491
|
+
const parsed = JSON.parse(rest);
|
|
492
|
+
if (!Array.isArray(parsed) || parsed.length < 2)
|
|
493
|
+
return [];
|
|
494
|
+
return parsed.slice(0, -1).map((item) => String(item));
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
return [];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const tokens = [];
|
|
501
|
+
for (const match of rest.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)) {
|
|
502
|
+
const token = match[1] ?? match[2] ?? match[3];
|
|
503
|
+
if (token.startsWith("--"))
|
|
504
|
+
continue;
|
|
505
|
+
tokens.push(token);
|
|
506
|
+
}
|
|
507
|
+
if (tokens.length < 2)
|
|
508
|
+
return [];
|
|
509
|
+
return tokens.slice(0, -1);
|
|
510
|
+
}
|
|
511
|
+
function sourceIsSecret(source) {
|
|
512
|
+
const path = source.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
513
|
+
const base = path.split("/").filter(Boolean).at(-1) ?? path;
|
|
514
|
+
if (base === ".env" || base === "id_rsa" || base === "id_ed25519")
|
|
515
|
+
return true;
|
|
516
|
+
if (base.endsWith(".pem"))
|
|
517
|
+
return true;
|
|
518
|
+
return base === "credentials";
|
|
519
|
+
}
|
|
455
520
|
function lineLocation(line, path) {
|
|
456
521
|
return { line, column: 1, path };
|
|
457
522
|
}
|
|
@@ -500,6 +565,17 @@ function parseDockerfile(content) {
|
|
|
500
565
|
locate: () => lineLocation(instruction.line, "run"),
|
|
501
566
|
});
|
|
502
567
|
});
|
|
568
|
+
instructions.forEach((instruction, index) => {
|
|
569
|
+
const sources = copySources(instruction.text);
|
|
570
|
+
if (!sources)
|
|
571
|
+
return;
|
|
572
|
+
resources.push({
|
|
573
|
+
id: `dockerfile.copy.${index + 1}`,
|
|
574
|
+
type: "dockerfile:copy",
|
|
575
|
+
value: { secret: sources.some(sourceIsSecret) },
|
|
576
|
+
locate: () => lineLocation(instruction.line, "copy"),
|
|
577
|
+
});
|
|
578
|
+
});
|
|
503
579
|
return {
|
|
504
580
|
format: "dockerfile",
|
|
505
581
|
resources,
|
|
@@ -526,6 +602,14 @@ function workflowChecksOutHead(jobs) {
|
|
|
526
602
|
}
|
|
527
603
|
return false;
|
|
528
604
|
}
|
|
605
|
+
function runPrintsSecret(script) {
|
|
606
|
+
const logical = script.replace(/\\\r?\n\s*/g, " ");
|
|
607
|
+
return logical.split(/\n/).some((line) => {
|
|
608
|
+
const code = line.replace(/(^|\s)#[^\n]*$/, "");
|
|
609
|
+
return (/\b(?:echo|printf|print)\b/i.test(code) &&
|
|
610
|
+
/\$\{\{\s*secrets\./.test(code));
|
|
611
|
+
});
|
|
612
|
+
}
|
|
529
613
|
function permissionsAreBroad(top, jobs, pullRequestTarget) {
|
|
530
614
|
const values = [
|
|
531
615
|
top,
|
|
@@ -609,6 +693,17 @@ function parseGithubWorkflow(content) {
|
|
|
609
693
|
locate: () => position(["jobs", jobName, "steps", index, "uses"]),
|
|
610
694
|
});
|
|
611
695
|
});
|
|
696
|
+
array(object(job).steps).forEach((step, index) => {
|
|
697
|
+
const run = object(step).run;
|
|
698
|
+
if (typeof run !== "string")
|
|
699
|
+
return;
|
|
700
|
+
resources.push({
|
|
701
|
+
id: `jobs.${jobName}.steps.${index}.run`,
|
|
702
|
+
type: "github:run",
|
|
703
|
+
value: { printsSecret: runPrintsSecret(run) },
|
|
704
|
+
locate: () => position(["jobs", jobName, "steps", index, "run"]),
|
|
705
|
+
});
|
|
706
|
+
});
|
|
612
707
|
}
|
|
613
708
|
}
|
|
614
709
|
if (!found)
|
|
@@ -649,3 +744,95 @@ function pulumiResourceMap(root) {
|
|
|
649
744
|
function escapeRegex(value) {
|
|
650
745
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
651
746
|
}
|
|
747
|
+
function maskNonCode(content) {
|
|
748
|
+
const withoutBlockComments = content
|
|
749
|
+
.replace(/\{\{-?\s*\/\*[\s\S]*?\*\/\s*-?\}\}/g, (match) => match.replace(/[^\n]/g, " "))
|
|
750
|
+
.replace(/\{#[\s\S]*?#\}/g, (match) => match.replace(/[^\n]/g, " "));
|
|
751
|
+
return withoutBlockComments
|
|
752
|
+
.split("\n")
|
|
753
|
+
.map((line) => {
|
|
754
|
+
let out = "";
|
|
755
|
+
let templates = 0;
|
|
756
|
+
let quote;
|
|
757
|
+
for (let index = 0; index < line.length; index++) {
|
|
758
|
+
const char = line[index];
|
|
759
|
+
const next = line[index + 1];
|
|
760
|
+
if (quote) {
|
|
761
|
+
out += char;
|
|
762
|
+
if (char === quote && line[index - 1] !== "\\")
|
|
763
|
+
quote = undefined;
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (templates === 0 && (char === '"' || char === "'")) {
|
|
767
|
+
quote = char;
|
|
768
|
+
out += char;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (char === "{" && next === "{") {
|
|
772
|
+
templates += 1;
|
|
773
|
+
out += char;
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
if (char === "}" && next === "}" && templates > 0) {
|
|
777
|
+
templates -= 1;
|
|
778
|
+
out += char;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (templates === 0 && char === "#") {
|
|
782
|
+
return out + " ".repeat(line.length - out.length);
|
|
783
|
+
}
|
|
784
|
+
out += char;
|
|
785
|
+
}
|
|
786
|
+
return out;
|
|
787
|
+
})
|
|
788
|
+
.join("\n");
|
|
789
|
+
}
|
|
790
|
+
function lineAt(content, index) {
|
|
791
|
+
return content.slice(0, index).split("\n").length;
|
|
792
|
+
}
|
|
793
|
+
function firstLiteral(content, pattern) {
|
|
794
|
+
const match = pattern.exec(content);
|
|
795
|
+
if (!match || match.index === undefined)
|
|
796
|
+
return { present: false, line: 1 };
|
|
797
|
+
return { present: true, line: lineAt(content, match.index) };
|
|
798
|
+
}
|
|
799
|
+
function literalResource(id, type, match, path) {
|
|
800
|
+
return {
|
|
801
|
+
id,
|
|
802
|
+
type,
|
|
803
|
+
value: { present: match.present },
|
|
804
|
+
locate: () => lineLocation(match.line, path),
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const helmTemplateWarning = "Helm templates are not executed. Only literal text outside comments is checked.";
|
|
808
|
+
const ansibleFactsWarning = "Ansible facts are not executed. Only literal text outside comments is checked.";
|
|
809
|
+
function parseHelm(content) {
|
|
810
|
+
const visible = maskNonCode(content);
|
|
811
|
+
const privileged = firstLiteral(visible, /privileged\s*:\s*["']?true["']?\b/);
|
|
812
|
+
const hostNetwork = firstLiteral(visible, /hostNetwork\s*:\s*["']?true["']?\b/);
|
|
813
|
+
const latest = firstLiteral(visible, /:latest\b/);
|
|
814
|
+
const socket = firstLiteral(visible, /(?:hostPath|volumes?)\b[\s\S]{0,500}docker\.sock|docker\.sock[\s\S]{0,200}\b(?:hostPath|volumes?)\b/);
|
|
815
|
+
return {
|
|
816
|
+
format: "helm",
|
|
817
|
+
resources: [
|
|
818
|
+
literalResource("helm.privileged", "helm:privileged", privileged, "privileged"),
|
|
819
|
+
literalResource("helm.hostNetwork", "helm:hostNetwork", hostNetwork, "hostNetwork"),
|
|
820
|
+
literalResource("helm.image", "helm:image", latest, "image"),
|
|
821
|
+
literalResource("helm.socket", "helm:socket", socket, "volume"),
|
|
822
|
+
],
|
|
823
|
+
warnings: [helmTemplateWarning],
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function parseAnsible(content) {
|
|
827
|
+
const visible = maskNonCode(content);
|
|
828
|
+
const become = firstLiteral(visible, /(?:^|\n)\s*(?:-\s+)?become\s*:\s*["']?(?:true|yes)["']?(?=\s|$)/);
|
|
829
|
+
const cidr = firstLiteral(visible, /(?:cidr|source)\s*:\s*(?:\|\s*)?(?:\n\s*-\s*)?["']?0\.0\.0\.0\/0\b/);
|
|
830
|
+
return {
|
|
831
|
+
format: "ansible",
|
|
832
|
+
resources: [
|
|
833
|
+
literalResource("ansible.become", "ansible:become", become, "become"),
|
|
834
|
+
literalResource("ansible.cidr", "ansible:cidr", cidr, "cidr"),
|
|
835
|
+
],
|
|
836
|
+
warnings: [ansibleFactsWarning],
|
|
837
|
+
};
|
|
838
|
+
}
|
package/dist/iac/rules.js
CHANGED
|
@@ -239,6 +239,62 @@ export const rules = {
|
|
|
239
239
|
detail: "A uses value is pinned to @main or @master.",
|
|
240
240
|
remediation: "Pin the action to a release tag or a full commit SHA.",
|
|
241
241
|
},
|
|
242
|
+
DF004: {
|
|
243
|
+
category: "security",
|
|
244
|
+
severity: "high",
|
|
245
|
+
title: "Secret file copied into the image",
|
|
246
|
+
detail: "A COPY or ADD instruction uses a secret file as a source.",
|
|
247
|
+
remediation: "Keep secret files out of the image. Mount them at runtime from a secret store.",
|
|
248
|
+
},
|
|
249
|
+
GH004: {
|
|
250
|
+
category: "security",
|
|
251
|
+
severity: "high",
|
|
252
|
+
title: "Workflow prints a secret",
|
|
253
|
+
detail: "A run script echoes or prints a secrets expression.",
|
|
254
|
+
remediation: "Remove the printed secret from the script. Pass secrets through an environment variable that the step does not print.",
|
|
255
|
+
},
|
|
256
|
+
HELM001: {
|
|
257
|
+
category: "security",
|
|
258
|
+
severity: "critical",
|
|
259
|
+
title: "Privileged container in a Helm template",
|
|
260
|
+
detail: "The template sets privileged to true.",
|
|
261
|
+
remediation: "Set privileged to false. Isolate workloads that genuinely require elevated privileges.",
|
|
262
|
+
},
|
|
263
|
+
HELM002: {
|
|
264
|
+
category: "security",
|
|
265
|
+
severity: "high",
|
|
266
|
+
title: "Host network in a Helm template",
|
|
267
|
+
detail: "The template sets hostNetwork to true.",
|
|
268
|
+
remediation: "Disable hostNetwork unless a reviewed node-level component requires it.",
|
|
269
|
+
},
|
|
270
|
+
HELM003: {
|
|
271
|
+
category: "reliability",
|
|
272
|
+
severity: "medium",
|
|
273
|
+
title: "Unpinned image in a Helm template",
|
|
274
|
+
detail: "An image tag uses latest.",
|
|
275
|
+
remediation: "Pin an immutable digest or a release tag.",
|
|
276
|
+
},
|
|
277
|
+
HELM004: {
|
|
278
|
+
category: "security",
|
|
279
|
+
severity: "high",
|
|
280
|
+
title: "Docker socket mounted in a Helm template",
|
|
281
|
+
detail: "A hostPath or volume contains docker.sock.",
|
|
282
|
+
remediation: "Remove the Docker socket mount. Use a scoped volume or a reviewed runtime API instead.",
|
|
283
|
+
},
|
|
284
|
+
ANS001: {
|
|
285
|
+
category: "security",
|
|
286
|
+
severity: "high",
|
|
287
|
+
title: "Ansible task runs as root",
|
|
288
|
+
detail: "A play or task sets become to true or yes.",
|
|
289
|
+
remediation: "Set become to false unless the task has a reviewed need to run as root.",
|
|
290
|
+
},
|
|
291
|
+
ANS002: {
|
|
292
|
+
category: "security",
|
|
293
|
+
severity: "high",
|
|
294
|
+
title: "Broad network exposure",
|
|
295
|
+
detail: "A cidr or source allows 0.0.0.0/0.",
|
|
296
|
+
remediation: "Restrict the cidr or source to approved addresses.",
|
|
297
|
+
},
|
|
242
298
|
};
|
|
243
299
|
export function resourceFindings(resources, format) {
|
|
244
300
|
const findings = [];
|
|
@@ -262,6 +318,14 @@ export function resourceFindings(resources, format) {
|
|
|
262
318
|
checkGithub(resource, check);
|
|
263
319
|
continue;
|
|
264
320
|
}
|
|
321
|
+
if (format === "helm") {
|
|
322
|
+
checkHelm(resource, check);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (format === "ansible") {
|
|
326
|
+
checkAnsible(resource, check);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
265
329
|
walk(resource.value, (key, value, path, parent) => {
|
|
266
330
|
if (typeof value !== "string")
|
|
267
331
|
return;
|
|
@@ -289,6 +353,8 @@ function checkDockerfile(resource, check) {
|
|
|
289
353
|
check("DF002", resource.value.unpinned === true, ["image"]);
|
|
290
354
|
else if (resource.type === "dockerfile:run")
|
|
291
355
|
check("DF003", resource.value.piped === true, ["run"]);
|
|
356
|
+
else if (resource.type === "dockerfile:copy")
|
|
357
|
+
check("DF004", resource.value.secret === true, ["copy"]);
|
|
292
358
|
}
|
|
293
359
|
function checkGithub(resource, check) {
|
|
294
360
|
if (resource.type === "github:trigger")
|
|
@@ -298,6 +364,24 @@ function checkGithub(resource, check) {
|
|
|
298
364
|
check("GH002", resource.value.broad === true, ["permissions"]);
|
|
299
365
|
else if (resource.type === "github:action")
|
|
300
366
|
check("GH003", resource.value.floating === true, ["uses"]);
|
|
367
|
+
else if (resource.type === "github:run")
|
|
368
|
+
check("GH004", resource.value.printsSecret === true, ["run"]);
|
|
369
|
+
}
|
|
370
|
+
function checkHelm(resource, check) {
|
|
371
|
+
if (resource.type === "helm:privileged")
|
|
372
|
+
check("HELM001", resource.value.present === true, ["privileged"]);
|
|
373
|
+
else if (resource.type === "helm:hostNetwork")
|
|
374
|
+
check("HELM002", resource.value.present === true, ["hostNetwork"]);
|
|
375
|
+
else if (resource.type === "helm:image")
|
|
376
|
+
check("HELM003", resource.value.present === true, ["image"]);
|
|
377
|
+
else if (resource.type === "helm:socket")
|
|
378
|
+
check("HELM004", resource.value.present === true, ["volume"]);
|
|
379
|
+
}
|
|
380
|
+
function checkAnsible(resource, check) {
|
|
381
|
+
if (resource.type === "ansible:become")
|
|
382
|
+
check("ANS001", resource.value.present === true, ["become"]);
|
|
383
|
+
else if (resource.type === "ansible:cidr")
|
|
384
|
+
check("ANS002", resource.value.present === true, ["cidr"]);
|
|
301
385
|
}
|
|
302
386
|
function walk(value, visit, path = []) {
|
|
303
387
|
if (Array.isArray(value))
|
package/dist/server.js
CHANGED
|
@@ -75,12 +75,14 @@ export function createServer(remote = false) {
|
|
|
75
75
|
"compose",
|
|
76
76
|
"dockerfile",
|
|
77
77
|
"github",
|
|
78
|
+
"helm",
|
|
79
|
+
"ansible",
|
|
78
80
|
"application",
|
|
79
81
|
"auto",
|
|
80
82
|
])
|
|
81
83
|
.optional()
|
|
82
84
|
.default("auto")
|
|
83
|
-
.describe("Content format: terraform, cloudformation, kubernetes, pulumi, compose,
|
|
85
|
+
.describe("Content format: terraform, cloudformation, kubernetes, pulumi, compose, dockerfile, github, helm, ansible, application, auto."),
|
|
84
86
|
},
|
|
85
87
|
}, async ({ content, focus, format, minSeverity, maxFindings }) => {
|
|
86
88
|
const botHints = fetchPublicBotHints();
|
|
@@ -128,7 +130,7 @@ export function createServer(remote = false) {
|
|
|
128
130
|
description: (name === "scan_iac_stub"
|
|
129
131
|
? "Compatibility alias for scan_iac; runs the full scanner. "
|
|
130
132
|
: "") +
|
|
131
|
-
"Parse Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes manifests, Pulumi YAML, Pulumi programs, or Docker Compose. Includes Dockerfile and
|
|
133
|
+
"Parse Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes manifests, Pulumi YAML, Pulumi programs, or Docker Compose. Includes Dockerfile, GitHub Actions, Helm templates, and Ansible playbooks. Helm templates and Ansible facts are not executed. Check security, reliability and cost rules with resource locations and remediation. Pulumi programs are scanned as text.",
|
|
132
134
|
annotations: {
|
|
133
135
|
readOnlyHint: true,
|
|
134
136
|
destructiveHint: false,
|
|
@@ -136,7 +138,7 @@ export function createServer(remote = false) {
|
|
|
136
138
|
},
|
|
137
139
|
_meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
|
|
138
140
|
inputSchema: {
|
|
139
|
-
content: contentSchema.describe("Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes YAML/JSON, Pulumi YAML or program text, or Docker Compose. Includes Dockerfile and
|
|
141
|
+
content: contentSchema.describe("Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes YAML/JSON, Pulumi YAML or program text, or Docker Compose. Includes Dockerfile, GitHub Actions, Helm templates, and Ansible playbooks."),
|
|
140
142
|
format: z
|
|
141
143
|
.enum([
|
|
142
144
|
"terraform",
|
|
@@ -146,6 +148,8 @@ export function createServer(remote = false) {
|
|
|
146
148
|
"compose",
|
|
147
149
|
"dockerfile",
|
|
148
150
|
"github",
|
|
151
|
+
"helm",
|
|
152
|
+
"ansible",
|
|
149
153
|
"auto",
|
|
150
154
|
])
|
|
151
155
|
.default("auto"),
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.6.
|
|
1
|
+
export declare const VERSION = "0.6.10";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.6.
|
|
1
|
+
export const VERSION = "0.6.10";
|