@skaleagents/swarm 0.6.0 → 0.6.2

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/review.js DELETED
@@ -1,191 +0,0 @@
1
- import { getApiUrl } from "./config.js";
2
- function shouldInclude(focus, category) {
3
- return focus === "general" || focus === category;
4
- }
5
- export function architectureFindings(content, focus) {
6
- const focusValue = focus === "security" ||
7
- focus === "reliability" ||
8
- focus === "cost" ||
9
- focus === "general"
10
- ? focus
11
- : "general";
12
- const findings = [
13
- {
14
- severity: "info",
15
- title: "Structured review completed",
16
- detail: `Checked ${content.length} characters with the ${focusValue} ruleset. Findings are pattern-based and should be validated against the running system.`,
17
- },
18
- ];
19
- if (shouldInclude(focusValue, "security") &&
20
- /0\.0\.0\.0(?:\/0)?/.test(content)) {
21
- findings.push({
22
- severity: "high",
23
- title: "Broad network exposure",
24
- detail: "Detected a possible wide-open CIDR or bind address. Restrict ingress to approved sources and keep public listeners behind the intended edge control.",
25
- });
26
- }
27
- if (shouldInclude(focusValue, "security") &&
28
- /AKIA[0-9A-Z]{16}/.test(content)) {
29
- findings.push({
30
- severity: "critical",
31
- title: "Possible AWS access key",
32
- detail: "A value matches an AWS access-key pattern. Remove it from source, rotate the credential, and check repository history.",
33
- });
34
- }
35
- if (shouldInclude(focusValue, "security") &&
36
- /-----BEGIN (?:RSA |EC |OPENSSH |DSA |)PRIVATE KEY-----/.test(content)) {
37
- findings.push({
38
- severity: "critical",
39
- title: "Private key material in source",
40
- detail: "Detected a private-key block. Revoke or rotate the key, remove it from source and history, and load replacement credentials through a secret manager.",
41
- });
42
- }
43
- if (shouldInclude(focusValue, "security") &&
44
- [
45
- ...content.matchAll(/(?:password|passwd|secret|api[_-]?key|auth[_-]?token)["']?\s*[:=]\s*["']([^"'\n]+)["']/gi),
46
- ].some((match) => literalCredential(match[1]))) {
47
- findings.push({
48
- severity: "high",
49
- title: "Hardcoded credential-like value",
50
- detail: "A credential-like assignment contains a literal value. Move it to a secret manager or runtime environment and rotate the exposed value.",
51
- });
52
- }
53
- if (shouldInclude(focusValue, "security") &&
54
- /(?:Action|actions?)\s*[:=][^\n]*["']?\*["']?/i.test(content)) {
55
- findings.push({
56
- severity: "high",
57
- title: "Wildcard permission detected",
58
- detail: "An IAM or policy action uses a wildcard. Replace it with the smallest permission set required by the workload.",
59
- });
60
- }
61
- if (shouldInclude(focusValue, "security") &&
62
- /(?:privileged\s*:\s*true|hostNetwork\s*:\s*true|allowPrivilegeEscalation\s*:\s*true)/i.test(content)) {
63
- findings.push({
64
- severity: "critical",
65
- title: "Elevated container privileges",
66
- detail: "The content enables a privileged container setting. Remove it unless the workload requires it, then isolate and monitor that workload.",
67
- });
68
- }
69
- if (shouldInclude(focusValue, "security") &&
70
- [...content.matchAll(/\bhttp:\/\/[^\s"'<>]+/gi)].some((match) => externalHttp(match[0]))) {
71
- findings.push({
72
- severity: "medium",
73
- title: "Unencrypted HTTP endpoint",
74
- detail: "Detected an HTTP URL outside localhost. Use HTTPS for service and dependency traffic, and verify certificate validation is enabled.",
75
- });
76
- }
77
- if ((shouldInclude(focusValue, "security") ||
78
- shouldInclude(focusValue, "reliability")) &&
79
- /(?:^|[\s:=])(?:[\w./-]+:)?latest(?:[\s"']|$)/im.test(content)) {
80
- findings.push({
81
- severity: "medium",
82
- title: "Unpinned container image",
83
- detail: "An image uses the mutable latest tag. Pin a version or digest so deployments are repeatable and rollback targets remain known.",
84
- });
85
- }
86
- if (shouldInclude(focusValue, "security") &&
87
- /(?:public-read|publicRead|allUsers)/i.test(content)) {
88
- findings.push({
89
- severity: "high",
90
- title: "Public data access pattern",
91
- detail: "Detected a public access setting. Confirm the resource is intended to be public and restrict access when it is not.",
92
- });
93
- }
94
- if (shouldInclude(focusValue, "reliability") &&
95
- /(?:debug|app_debug)\s*[:=]\s*["']?(?:true|1|yes)["']?/i.test(content)) {
96
- findings.push({
97
- severity: "medium",
98
- title: "Debug mode enabled",
99
- detail: "The content enables debug mode. Disable it in production to avoid noisy behavior and accidental disclosure of internal details.",
100
- });
101
- }
102
- if (shouldInclude(focusValue, "cost") &&
103
- /(?:instance_type|machine_type|vm_size)\s*[:=]/i.test(content)) {
104
- findings.push({
105
- severity: "info",
106
- title: "Compute sizing needs review",
107
- detail: "Detected an explicit compute size. Compare the selected size with observed utilization and set a review point for scale-up and scale-down decisions.",
108
- });
109
- }
110
- return findings.map((finding, index) => {
111
- if (index === 0)
112
- return finding;
113
- const matchers = {
114
- "Broad network exposure": /0\.0\.0\.0(?:\/0)?/,
115
- "Possible AWS access key": /AKIA[0-9A-Z]{16}/,
116
- "Private key material in source": /-----BEGIN (?:RSA |EC |OPENSSH |DSA |)PRIVATE KEY-----/,
117
- "Hardcoded credential-like value": /(?:password|passwd|secret|api[_-]?key|auth[_-]?token)["']?\s*[:=]\s*["']([^"'\n]+)["']/gi,
118
- "Wildcard permission detected": /(?:Action|actions?)\s*[:=][^\n]*["']?\*["']?/i,
119
- "Elevated container privileges": /(?:privileged|hostNetwork|allowPrivilegeEscalation)\s*:\s*true/i,
120
- "Unencrypted HTTP endpoint": /\bhttp:\/\/[^\s"'<>]+/gi,
121
- "Unpinned container image": /\blatest\b/,
122
- "Public data access pattern": /(?:public-read|publicRead|allUsers)/i,
123
- "Debug mode enabled": /(?:debug|app_debug)\s*[:=]\s*["']?(?:true|1|yes)/i,
124
- "Compute sizing needs review": /(?:instance_type|machine_type|vm_size)\s*[:=]/i,
125
- };
126
- const pattern = matchers[finding.title];
127
- const matches = pattern
128
- ? [
129
- ...content.matchAll(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g")),
130
- ]
131
- : [];
132
- const match = matches.find((m) => finding.title === "Hardcoded credential-like value"
133
- ? literalCredential(m[1])
134
- : finding.title === "Unencrypted HTTP endpoint"
135
- ? externalHttp(m[0])
136
- : true);
137
- const offset = match?.index ?? 0;
138
- const before = content.slice(0, offset);
139
- const ruleIndex = Object.keys(matchers).indexOf(finding.title) + 1;
140
- return {
141
- ...finding,
142
- ruleId: `APP${String(ruleIndex).padStart(3, "0")}`,
143
- category: (finding.title === "Compute sizing needs review"
144
- ? "cost"
145
- : ["Debug mode enabled", "Unpinned container image"].includes(finding.title)
146
- ? "reliability"
147
- : "security"),
148
- remediation: finding.detail,
149
- location: {
150
- line: before.split("\n").length,
151
- column: offset - before.lastIndexOf("\n"),
152
- path: "source",
153
- },
154
- };
155
- });
156
- }
157
- export function literalCredential(value) {
158
- return (value.trim().length > 0 &&
159
- !/^(?:\$\{|\{\{|<[^>]+>$|process\.env\.|var\.|secrets?\.)/.test(value));
160
- }
161
- export function externalHttp(value) {
162
- try {
163
- const url = new URL(value);
164
- return (url.protocol === "http:" &&
165
- !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname));
166
- }
167
- catch {
168
- return false;
169
- }
170
- }
171
- export async function fetchPublicBotHints(token) {
172
- try {
173
- const res = await fetch(`${getApiUrl()}/api/bots`, {
174
- signal: AbortSignal.timeout(10_000),
175
- headers: {
176
- Accept: "application/json",
177
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
178
- },
179
- });
180
- if (!res.ok)
181
- return [];
182
- const data = (await res.json());
183
- return data.bots
184
- .filter((b) => b.status === "published" && b.visibility === "public")
185
- .map((b) => b.name)
186
- .slice(0, 5);
187
- }
188
- catch {
189
- return [];
190
- }
191
- }
package/dist/server.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function createServer(remote?: boolean): McpServer;
package/dist/server.js DELETED
@@ -1,157 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { getApiAccessToken, requireApiAuth, unauthorizedContent, } from "./auth.js";
4
- import { architectureFindings, fetchPublicBotHints } from "./review.js";
5
- import { looksLikeIac, ScanInputError } from "./iac/parse.js";
6
- import { filterFindings, scanIac, summarize } from "./iac/scan.js";
7
- import { registerConsultation } from "./application/tools.js";
8
- import { VERSION } from "./version.js";
9
- const contentSchema = z
10
- .string()
11
- .min(1)
12
- .max(500_000)
13
- .refine((value) => value.trim().length > 0, "Content must not be whitespace");
14
- const filters = {
15
- focus: z
16
- .enum(["security", "reliability", "cost", "general"])
17
- .default("general"),
18
- minSeverity: z
19
- .enum(["info", "low", "medium", "high", "critical"])
20
- .default("info")
21
- .describe("Lowest finding severity to return"),
22
- maxFindings: z
23
- .number()
24
- .int()
25
- .min(1)
26
- .max(500)
27
- .default(100)
28
- .describe("Maximum findings returned; totals include all matching findings"),
29
- };
30
- function result(output) {
31
- return {
32
- content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
33
- structuredContent: output,
34
- };
35
- }
36
- function inputError(error) {
37
- if (!(error instanceof ScanInputError))
38
- throw error;
39
- return {
40
- isError: true,
41
- content: [
42
- {
43
- type: "text",
44
- text: JSON.stringify({
45
- error: "invalid_input",
46
- message: error.message,
47
- }),
48
- },
49
- ],
50
- };
51
- }
52
- export function createServer(remote = false) {
53
- const server = new McpServer({ name: "skaleagents-swarm", version: VERSION }, {
54
- instructions: "For a whole application architecture consultation, start with plan_architecture_review, read relevant files with the client's workspace tools, then call review_application_architecture. Ask its follow-up questions and carry answers forward in context. Use review_architecture for individual source snippets and scan_iac for infrastructure. Ground conclusions in the returned evidence and coverage limits.",
55
- });
56
- registerConsultation(server, remote);
57
- server.registerTool("review_architecture", {
58
- title: "Review architecture",
59
- description: "Review individual source snippets or parsed infrastructure for security, reliability, and cost risks. For a whole application or Next.js architecture consultation, use plan_architecture_review and review_application_architecture instead.",
60
- annotations: {
61
- readOnlyHint: true,
62
- destructiveHint: false,
63
- openWorldHint: false,
64
- },
65
- _meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
66
- inputSchema: {
67
- content: contentSchema.describe("Application source or IaC text to review"),
68
- ...filters,
69
- format: z
70
- .enum([
71
- "terraform",
72
- "cloudformation",
73
- "kubernetes",
74
- "application",
75
- "auto",
76
- ])
77
- .optional()
78
- .default("auto")
79
- .describe("Content format: terraform, cloudformation, kubernetes, application, auto"),
80
- },
81
- }, async ({ content, focus, format, minSeverity, maxFindings }) => {
82
- let token;
83
- if (!remote) {
84
- const auth = await requireApiAuth();
85
- if (!auth.ok)
86
- return unauthorizedContent(auth);
87
- token = (await getApiAccessToken()) ?? undefined;
88
- if (!token)
89
- return unauthorizedContent({ ok: false, reason: "oauth_failed" });
90
- }
91
- try {
92
- const options = { focus, minSeverity, maxFindings };
93
- if (format !== "application" &&
94
- (format !== "auto" || looksLikeIac(content))) {
95
- return result({
96
- ...scanIac(content, { ...options, format }),
97
- botHints: await fetchPublicBotHints(token),
98
- });
99
- }
100
- const findings = filterFindings(architectureFindings(content, focus).slice(1), options);
101
- return result({
102
- status: "completed",
103
- engineVersion: VERSION,
104
- format: "application",
105
- focus,
106
- summary: `Application review completed; ${findings.length} findings match the selected filters.`,
107
- findings: findings.slice(0, maxFindings),
108
- totalFindings: findings.length,
109
- totals: summarize(findings),
110
- truncated: findings.length > maxFindings,
111
- limitations: [
112
- "Application checks are text patterns, not a language-aware or runtime analysis. Findings do not establish that code is safe.",
113
- ],
114
- botHints: await fetchPublicBotHints(token),
115
- });
116
- }
117
- catch (error) {
118
- return inputError(error);
119
- }
120
- });
121
- for (const name of ["scan_iac", "scan_iac_stub"])
122
- server.registerTool(name, {
123
- title: name === "scan_iac"
124
- ? "Scan infrastructure"
125
- : "Scan infrastructure (compatibility alias)",
126
- description: (name === "scan_iac_stub"
127
- ? "Compatibility alias for scan_iac; runs the full scanner. "
128
- : "") +
129
- "Parse Terraform HCL/JSON, CloudFormation YAML/JSON, or Kubernetes manifests. Check security, reliability and cost rules with resource locations and remediation.",
130
- annotations: {
131
- readOnlyHint: true,
132
- destructiveHint: false,
133
- openWorldHint: false,
134
- },
135
- _meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
136
- inputSchema: {
137
- content: contentSchema.describe("Terraform HCL/JSON, CloudFormation YAML/JSON, or Kubernetes YAML/JSON"),
138
- format: z
139
- .enum(["terraform", "cloudformation", "kubernetes", "auto"])
140
- .default("auto"),
141
- ...filters,
142
- },
143
- }, async ({ content, format, focus, minSeverity, maxFindings }) => {
144
- if (!remote) {
145
- const auth = await requireApiAuth();
146
- if (!auth.ok)
147
- return unauthorizedContent(auth);
148
- }
149
- try {
150
- return result(scanIac(content, { format, focus, minSeverity, maxFindings }));
151
- }
152
- catch (error) {
153
- return inputError(error);
154
- }
155
- });
156
- return server;
157
- }
package/dist/version.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const VERSION = "0.6.0";
package/dist/version.js DELETED
@@ -1 +0,0 @@
1
- export const VERSION = "0.6.0";