@skaleagents/swarm 0.6.1 → 0.6.3
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 +2 -1
- package/dist/iac/parse.js +45 -10
- package/dist/iac/rules.js +13 -5
- package/dist/server.js +5 -4
- 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";
|
|
1
|
+
export type IacFormat = "terraform" | "cloudformation" | "kubernetes" | "pulumi";
|
|
2
2
|
export type Path = (string | number)[];
|
|
3
3
|
export type Location = {
|
|
4
4
|
line: number;
|
|
@@ -22,4 +22,5 @@ export declare class ScanInputError extends Error {
|
|
|
22
22
|
export declare function object(value: unknown): Record<string, unknown>;
|
|
23
23
|
export declare function array(value: unknown): unknown[];
|
|
24
24
|
export declare function looksLikeIac(content: string): boolean;
|
|
25
|
+
export declare function looksLikePulumiYaml(content: string): boolean;
|
|
25
26
|
export declare function parseIac(content: string, requested?: IacFormat | "auto"): ParsedIac;
|
package/dist/iac/parse.js
CHANGED
|
@@ -37,11 +37,20 @@ export function looksLikeIac(content) {
|
|
|
37
37
|
}
|
|
38
38
|
return (/^\s*(?:resource|module|terraform|variable|provider)\s+["{]/m.test(content) ||
|
|
39
39
|
/^\s*(?:["']?Resources["']?\s*:|apiVersion\s*:)/m.test(content) ||
|
|
40
|
-
/^\s*\{\s*"(?:resource|Resources|apiVersion|AWSTemplateFormatVersion)"\s*:/.test(content)
|
|
40
|
+
/^\s*\{\s*"(?:resource|Resources|apiVersion|AWSTemplateFormatVersion)"\s*:/.test(content) ||
|
|
41
|
+
looksLikePulumiYaml(content));
|
|
42
|
+
}
|
|
43
|
+
export function looksLikePulumiYaml(content) {
|
|
44
|
+
return (/^\s*resources\s*:/m.test(content) &&
|
|
45
|
+
/^\s+type\s*:\s*[A-Za-z0-9_.-]+:\S+/m.test(content));
|
|
41
46
|
}
|
|
42
47
|
export function parseIac(content, requested = "auto") {
|
|
43
48
|
if (!content.trim())
|
|
44
49
|
throw new ScanInputError("Content must not be empty or whitespace.");
|
|
50
|
+
if (requested !== "terraform" &&
|
|
51
|
+
/(?:^|\n)\s*(?:import\s+\*\s+as\s+pulumi\b|import\s+pulumi\b|from\s+["']@pulumi\/)/.test(content)) {
|
|
52
|
+
throw new ScanInputError("Pulumi TypeScript, Python, and Go programs are not parsed yet. Submit Pulumi YAML.");
|
|
53
|
+
}
|
|
45
54
|
const warnings = [];
|
|
46
55
|
const resources = [];
|
|
47
56
|
const isHcl = (requested === "terraform" && !content.trimStart().startsWith("{")) ||
|
|
@@ -142,13 +151,22 @@ export function parseIac(content, requested = "auto") {
|
|
|
142
151
|
continue;
|
|
143
152
|
checkShape(data);
|
|
144
153
|
const root = object(data);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
const pulumiResources = pulumiResourceMap(root);
|
|
155
|
+
let detected;
|
|
156
|
+
if (requested === "pulumi" || (requested === "auto" && pulumiResources)) {
|
|
157
|
+
if (!pulumiResources)
|
|
158
|
+
throw new ScanInputError("Pulumi YAML requires a resources mapping. Each resource needs a type token. Pulumi TypeScript, Python, and Go programs are not parsed yet.");
|
|
159
|
+
detected = "pulumi";
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
detected = root.Resources
|
|
163
|
+
? "cloudformation"
|
|
164
|
+
: root.apiVersion && root.kind
|
|
165
|
+
? "kubernetes"
|
|
166
|
+
: root.resource
|
|
167
|
+
? "terraform"
|
|
168
|
+
: undefined;
|
|
169
|
+
}
|
|
152
170
|
format ??= detected;
|
|
153
171
|
if (!format || (detected && detected !== format))
|
|
154
172
|
throw new ScanInputError("Input format is unsupported or mixed. Submit one IaC format per scan.");
|
|
@@ -170,7 +188,13 @@ export function parseIac(content, requested = "auto") {
|
|
|
170
188
|
},
|
|
171
189
|
});
|
|
172
190
|
};
|
|
173
|
-
if (format === "
|
|
191
|
+
if (format === "pulumi") {
|
|
192
|
+
for (const [name, raw] of Object.entries(pulumiResources ?? {})) {
|
|
193
|
+
const value = object(raw);
|
|
194
|
+
add(name, String(value.type), value, ["resources", name]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (format === "cloudformation") {
|
|
174
198
|
if (!root.Resources ||
|
|
175
199
|
Array.isArray(root.Resources) ||
|
|
176
200
|
typeof root.Resources !== "object")
|
|
@@ -227,13 +251,24 @@ export function parseIac(content, requested = "auto") {
|
|
|
227
251
|
}
|
|
228
252
|
}
|
|
229
253
|
if (!format)
|
|
230
|
-
throw new ScanInputError("No IaC document found. Choose Terraform, CloudFormation, or
|
|
254
|
+
throw new ScanInputError("No IaC document found. Choose Terraform, CloudFormation, Kubernetes, or Pulumi YAML.");
|
|
231
255
|
if (!resources.length)
|
|
232
256
|
warnings.push("No resources found in the submitted document.");
|
|
233
257
|
if (/(?:!(?:Ref|Sub|GetAtt|If)\b|"(?:Ref|Fn::\w+)"\s*:|\$\{)/.test(content))
|
|
234
258
|
warnings.push("Intrinsic functions and expressions are not evaluated. Only literal configuration is checked.");
|
|
235
259
|
return { format, resources, warnings };
|
|
236
260
|
}
|
|
261
|
+
function pulumiResourceMap(root) {
|
|
262
|
+
const resources = object(root.resources);
|
|
263
|
+
const entries = Object.entries(resources);
|
|
264
|
+
if (entries.length > 0 &&
|
|
265
|
+
entries.every(([, raw]) => {
|
|
266
|
+
const type = object(raw).type;
|
|
267
|
+
return typeof type === "string" && type.includes(":");
|
|
268
|
+
}))
|
|
269
|
+
return resources;
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
237
272
|
function escapeRegex(value) {
|
|
238
273
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
239
274
|
}
|
package/dist/iac/rules.js
CHANGED
|
@@ -242,15 +242,23 @@ function walk(value, visit, path = []) {
|
|
|
242
242
|
}
|
|
243
243
|
function checkCloud(resource, resources, format, check) {
|
|
244
244
|
const tf = format === "terraform";
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
const pulumi = format === "pulumi";
|
|
246
|
+
const value = tf
|
|
247
|
+
? resource.value
|
|
248
|
+
: pulumi
|
|
249
|
+
? object(resource.value.properties)
|
|
250
|
+
: object(resource.value.Properties);
|
|
251
|
+
const prefix = tf ? [] : pulumi ? ["properties"] : ["Properties"];
|
|
252
|
+
const property = (hcl, cfn) => value[tf || pulumi ? hcl : cfn];
|
|
253
|
+
const path = (hcl, cfn) => [
|
|
254
|
+
...prefix,
|
|
255
|
+
tf || pulumi ? hcl : cfn,
|
|
256
|
+
];
|
|
249
257
|
walk(value, (key, child, at, parent) => {
|
|
250
258
|
const isIngress = !at.some((part) => /^(?:egress|SecurityGroupEgress)$/i.test(String(part))) &&
|
|
251
259
|
!/egress/i.test(resource.type) &&
|
|
252
260
|
value.type !== "egress";
|
|
253
|
-
if (/^(?:cidr_blocks|ipv6_cidr_blocks|cidr_ipv4|cidr_ipv6|CidrIp|CidrIpv6)$/.test(key) &&
|
|
261
|
+
if (/^(?:cidr_blocks|ipv6_cidr_blocks|cidr_ipv4|cidr_ipv6|cidrBlocks|ipv6CidrBlocks|cidrBlock|CidrIp|CidrIpv6)$/.test(key) &&
|
|
254
262
|
isIngress) {
|
|
255
263
|
check("NET001", array(child).some((cidr) => cidr === "0.0.0.0/0" || cidr === "::/0"), [...prefix, ...at]);
|
|
256
264
|
}
|
package/dist/server.js
CHANGED
|
@@ -71,12 +71,13 @@ export function createServer(remote = false) {
|
|
|
71
71
|
"terraform",
|
|
72
72
|
"cloudformation",
|
|
73
73
|
"kubernetes",
|
|
74
|
+
"pulumi",
|
|
74
75
|
"application",
|
|
75
76
|
"auto",
|
|
76
77
|
])
|
|
77
78
|
.optional()
|
|
78
79
|
.default("auto")
|
|
79
|
-
.describe("Content format: terraform, cloudformation, kubernetes, application, auto"),
|
|
80
|
+
.describe("Content format: terraform, cloudformation, kubernetes, pulumi, application, auto"),
|
|
80
81
|
},
|
|
81
82
|
}, async ({ content, focus, format, minSeverity, maxFindings }) => {
|
|
82
83
|
const botHints = fetchPublicBotHints();
|
|
@@ -123,7 +124,7 @@ export function createServer(remote = false) {
|
|
|
123
124
|
description: (name === "scan_iac_stub"
|
|
124
125
|
? "Compatibility alias for scan_iac; runs the full scanner. "
|
|
125
126
|
: "") +
|
|
126
|
-
"Parse Terraform HCL/JSON, CloudFormation YAML/JSON, or
|
|
127
|
+
"Parse Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes manifests, or Pulumi YAML. Check security, reliability and cost rules with resource locations and remediation. Pulumi TypeScript, Python, and Go programs are not parsed yet.",
|
|
127
128
|
annotations: {
|
|
128
129
|
readOnlyHint: true,
|
|
129
130
|
destructiveHint: false,
|
|
@@ -131,9 +132,9 @@ export function createServer(remote = false) {
|
|
|
131
132
|
},
|
|
132
133
|
_meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
|
|
133
134
|
inputSchema: {
|
|
134
|
-
content: contentSchema.describe("Terraform HCL/JSON, CloudFormation YAML/JSON,
|
|
135
|
+
content: contentSchema.describe("Terraform HCL/JSON, CloudFormation YAML/JSON, Kubernetes YAML/JSON, or Pulumi YAML"),
|
|
135
136
|
format: z
|
|
136
|
-
.enum(["terraform", "cloudformation", "kubernetes", "auto"])
|
|
137
|
+
.enum(["terraform", "cloudformation", "kubernetes", "pulumi", "auto"])
|
|
137
138
|
.default("auto"),
|
|
138
139
|
...filters,
|
|
139
140
|
},
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.6.
|
|
1
|
+
export declare const VERSION = "0.6.3";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.6.
|
|
1
|
+
export const VERSION = "0.6.3";
|