vaspera 2.10.1 → 2.11.0

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.
Files changed (37) hide show
  1. package/dist/action/pr-comment.test.js +8 -0
  2. package/dist/action/pr-comment.test.js.map +1 -1
  3. package/dist/action/sarif-upload.test.js +8 -0
  4. package/dist/action/sarif-upload.test.js.map +1 -1
  5. package/dist/scanners/cache.d.ts.map +1 -1
  6. package/dist/scanners/cache.js +8 -0
  7. package/dist/scanners/cache.js.map +1 -1
  8. package/dist/scanners/dast.d.ts +40 -0
  9. package/dist/scanners/dast.d.ts.map +1 -0
  10. package/dist/scanners/dast.js +228 -0
  11. package/dist/scanners/dast.js.map +1 -0
  12. package/dist/scanners/deploy/types.d.ts +6 -6
  13. package/dist/scanners/index.d.ts +4 -4
  14. package/dist/scanners/index.d.ts.map +1 -1
  15. package/dist/scanners/index.js +133 -15
  16. package/dist/scanners/index.js.map +1 -1
  17. package/dist/scanners/index.test.js +6 -6
  18. package/dist/scanners/index.test.js.map +1 -1
  19. package/dist/scanners/openapi.d.ts +20 -0
  20. package/dist/scanners/openapi.d.ts.map +1 -0
  21. package/dist/scanners/openapi.js +226 -0
  22. package/dist/scanners/openapi.js.map +1 -0
  23. package/dist/scanners/runtime/types.d.ts +4 -4
  24. package/dist/scanners/rust.d.ts +22 -0
  25. package/dist/scanners/rust.d.ts.map +1 -0
  26. package/dist/scanners/rust.js +239 -0
  27. package/dist/scanners/rust.js.map +1 -0
  28. package/dist/scanners/scale/types.d.ts +16 -16
  29. package/dist/scanners/terraform.d.ts +23 -0
  30. package/dist/scanners/terraform.d.ts.map +1 -0
  31. package/dist/scanners/terraform.js +207 -0
  32. package/dist/scanners/terraform.js.map +1 -0
  33. package/dist/scanners/types.d.ts +1 -1
  34. package/dist/scanners/types.d.ts.map +1 -1
  35. package/dist/scanners/types.js +8 -0
  36. package/dist/scanners/types.js.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,226 @@
1
+ /**
2
+ * OpenAPI Security Scanner
3
+ *
4
+ * Validates OpenAPI/Swagger specifications for security issues
5
+ * using Spectral with security-focused rules.
6
+ *
7
+ * @module scanners/openapi
8
+ */
9
+ import { exec } from "child_process";
10
+ import { promisify } from "util";
11
+ import { readFile, readdir } from "fs/promises";
12
+ import { join, extname } from "path";
13
+ const execAsync = promisify(exec);
14
+ const SECURITY_RULES = `
15
+ extends: ["spectral:oas"]
16
+ rules:
17
+ # Authentication
18
+ operation-security-defined:
19
+ description: Operations must have security defined
20
+ given: "$.paths[*][get,post,put,patch,delete]"
21
+ then:
22
+ field: security
23
+ function: truthy
24
+ severity: error
25
+
26
+ # HTTPS only
27
+ servers-https:
28
+ description: Server URLs should use HTTPS
29
+ given: "$.servers[*].url"
30
+ then:
31
+ function: pattern
32
+ functionOptions:
33
+ match: "^https://"
34
+ severity: error
35
+
36
+ # No credentials in URLs
37
+ no-credentials-in-url:
38
+ description: URLs should not contain credentials
39
+ given: "$.servers[*].url"
40
+ then:
41
+ function: pattern
42
+ functionOptions:
43
+ notMatch: "://[^/]*:[^/]*@"
44
+ severity: error
45
+
46
+ # Rate limiting headers
47
+ rate-limit-headers:
48
+ description: Responses should include rate limiting headers
49
+ given: "$.paths[*][*].responses[*].headers"
50
+ then:
51
+ function: schema
52
+ functionOptions:
53
+ schema:
54
+ anyOf:
55
+ - required: ["X-RateLimit-Limit"]
56
+ - required: ["X-Rate-Limit-Limit"]
57
+ - required: ["RateLimit-Limit"]
58
+ severity: warn
59
+
60
+ # Sensitive data in query params
61
+ no-sensitive-query-params:
62
+ description: Sensitive data should not be in query parameters
63
+ given: "$.paths[*][*].parameters[?(@.in=='query')]"
64
+ then:
65
+ field: name
66
+ function: pattern
67
+ functionOptions:
68
+ notMatch: "(?i)(password|secret|token|api_key|apikey|auth|credential)"
69
+ severity: error
70
+ `;
71
+ export async function checkSpectralAvailable() {
72
+ try {
73
+ const { stdout } = await execAsync("npx spectral --version", { timeout: 10000 });
74
+ return {
75
+ scanner: "spectral",
76
+ available: true,
77
+ version: stdout.trim(),
78
+ };
79
+ }
80
+ catch {
81
+ return {
82
+ scanner: "spectral",
83
+ available: false,
84
+ error: "Spectral not available. Install with: npm install -g @stoplight/spectral-cli",
85
+ };
86
+ }
87
+ }
88
+ function mapSeverity(severity) {
89
+ switch (severity) {
90
+ case 0:
91
+ return "high";
92
+ case 1:
93
+ return "medium";
94
+ case 2:
95
+ return "low";
96
+ default:
97
+ return "info";
98
+ }
99
+ }
100
+ export async function runSpectral(specPath, options) {
101
+ const startTime = Date.now();
102
+ try {
103
+ const availability = await checkSpectralAvailable();
104
+ if (!availability.available) {
105
+ return {
106
+ scanner: "spectral",
107
+ findings: [],
108
+ duration: Date.now() - startTime,
109
+ success: false,
110
+ error: availability.error,
111
+ };
112
+ }
113
+ let command = `npx spectral lint "${specPath}" -f json`;
114
+ if (options?.rulesetPath) {
115
+ command += ` -r "${options.rulesetPath}"`;
116
+ }
117
+ const { stdout, stderr } = await execAsync(command, {
118
+ timeout: options?.timeout || 60000,
119
+ maxBuffer: 10 * 1024 * 1024,
120
+ }).catch((error) => {
121
+ if (error.stdout) {
122
+ return { stdout: error.stdout, stderr: error.stderr || "" };
123
+ }
124
+ throw error;
125
+ });
126
+ const results = JSON.parse(stdout || "[]");
127
+ const findings = results.map((result) => ({
128
+ scanner: "spectral",
129
+ ruleId: `spectral:${result.code}`,
130
+ file: result.source,
131
+ line: result.range.start.line + 1,
132
+ column: result.range.start.character + 1,
133
+ endLine: result.range.end.line + 1,
134
+ message: result.message,
135
+ severity: mapSeverity(result.severity),
136
+ confidence: 100,
137
+ metadata: {
138
+ path: result.path.join("."),
139
+ },
140
+ }));
141
+ return {
142
+ scanner: "spectral",
143
+ findings,
144
+ duration: Date.now() - startTime,
145
+ success: true,
146
+ version: availability.version,
147
+ };
148
+ }
149
+ catch (error) {
150
+ return {
151
+ scanner: "spectral",
152
+ findings: [],
153
+ duration: Date.now() - startTime,
154
+ success: false,
155
+ error: error instanceof Error ? error.message : "Unknown error",
156
+ };
157
+ }
158
+ }
159
+ export async function findOpenAPISpecs(projectPath) {
160
+ const specs = [];
161
+ const extensions = [".yaml", ".yml", ".json"];
162
+ const patterns = ["openapi", "swagger", "api-spec", "api"];
163
+ async function searchDir(dir, depth = 0) {
164
+ if (depth > 3)
165
+ return;
166
+ try {
167
+ const entries = await readdir(dir, { withFileTypes: true });
168
+ for (const entry of entries) {
169
+ const fullPath = join(dir, entry.name);
170
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
171
+ await searchDir(fullPath, depth + 1);
172
+ }
173
+ else if (entry.isFile()) {
174
+ const ext = extname(entry.name).toLowerCase();
175
+ const nameWithoutExt = entry.name.slice(0, -ext.length).toLowerCase();
176
+ if (extensions.includes(ext) && patterns.some((p) => nameWithoutExt.includes(p))) {
177
+ try {
178
+ const content = await readFile(fullPath, "utf-8");
179
+ if (content.includes("openapi") || content.includes("swagger")) {
180
+ specs.push(fullPath);
181
+ }
182
+ }
183
+ catch {
184
+ // Skip files that can't be read
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ catch {
191
+ // Skip directories that can't be read
192
+ }
193
+ }
194
+ await searchDir(projectPath);
195
+ return specs;
196
+ }
197
+ export async function runOpenAPIScan(projectPath, options) {
198
+ const startTime = Date.now();
199
+ const specs = await findOpenAPISpecs(projectPath);
200
+ if (specs.length === 0) {
201
+ return {
202
+ scanner: "openapi",
203
+ findings: [],
204
+ duration: Date.now() - startTime,
205
+ success: true,
206
+ filesScanned: 0,
207
+ };
208
+ }
209
+ const allFindings = [];
210
+ for (const spec of specs) {
211
+ const result = await runSpectral(spec, options);
212
+ allFindings.push(...result.findings);
213
+ }
214
+ return {
215
+ scanner: "openapi",
216
+ findings: allFindings,
217
+ duration: Date.now() - startTime,
218
+ success: true,
219
+ filesScanned: specs.length,
220
+ };
221
+ }
222
+ export async function detectOpenAPI(projectPath) {
223
+ const specs = await findOpenAPISpecs(projectPath);
224
+ return specs.length > 0;
225
+ }
226
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/scanners/openapi.ts"],"names":[],"mappings":"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"}
@@ -90,9 +90,9 @@ export declare const GoldenPathFlowSchema: z.ZodObject<{
90
90
  description?: string | undefined;
91
91
  timeout?: number | undefined;
92
92
  url?: string | undefined;
93
+ headers?: Record<string, string> | undefined;
93
94
  body?: Record<string, unknown> | undefined;
94
95
  method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | undefined;
95
- headers?: Record<string, string> | undefined;
96
96
  screenshot?: boolean | undefined;
97
97
  selector?: string | undefined;
98
98
  }, {
@@ -101,9 +101,9 @@ export declare const GoldenPathFlowSchema: z.ZodObject<{
101
101
  description?: string | undefined;
102
102
  timeout?: number | undefined;
103
103
  url?: string | undefined;
104
+ headers?: Record<string, string> | undefined;
104
105
  body?: Record<string, unknown> | undefined;
105
106
  method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | undefined;
106
- headers?: Record<string, string> | undefined;
107
107
  screenshot?: boolean | undefined;
108
108
  selector?: string | undefined;
109
109
  }>, "many">;
@@ -116,9 +116,9 @@ export declare const GoldenPathFlowSchema: z.ZodObject<{
116
116
  description?: string | undefined;
117
117
  timeout?: number | undefined;
118
118
  url?: string | undefined;
119
+ headers?: Record<string, string> | undefined;
119
120
  body?: Record<string, unknown> | undefined;
120
121
  method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | undefined;
121
- headers?: Record<string, string> | undefined;
122
122
  screenshot?: boolean | undefined;
123
123
  selector?: string | undefined;
124
124
  }[];
@@ -133,9 +133,9 @@ export declare const GoldenPathFlowSchema: z.ZodObject<{
133
133
  description?: string | undefined;
134
134
  timeout?: number | undefined;
135
135
  url?: string | undefined;
136
+ headers?: Record<string, string> | undefined;
136
137
  body?: Record<string, unknown> | undefined;
137
138
  method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | undefined;
138
- headers?: Record<string, string> | undefined;
139
139
  screenshot?: boolean | undefined;
140
140
  selector?: string | undefined;
141
141
  }[];
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Rust Security Scanner
3
+ *
4
+ * Scans Rust projects for security vulnerabilities using
5
+ * cargo-audit and clippy.
6
+ *
7
+ * @module scanners/rust
8
+ */
9
+ import type { ScannerResult, ScannerAvailability } from "./types.js";
10
+ export declare function checkCargoAuditAvailable(): Promise<ScannerAvailability>;
11
+ export declare function checkClippyAvailable(): Promise<ScannerAvailability>;
12
+ export declare function runCargoAudit(projectPath: string, options?: {
13
+ timeout?: number;
14
+ }): Promise<ScannerResult>;
15
+ export declare function runClippy(projectPath: string, options?: {
16
+ timeout?: number;
17
+ }): Promise<ScannerResult>;
18
+ export declare function runRustScanners(projectPath: string, options?: {
19
+ timeout?: number;
20
+ }): Promise<ScannerResult>;
21
+ export declare function detectRust(projectPath: string): Promise<boolean>;
22
+ //# sourceMappingURL=rust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../src/scanners/rust.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAwB,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAmE3F,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAe7E;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAezE;AAgBD,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,aAAa,CAAC,CAqFxB;AAED,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,aAAa,CAAC,CAmFxB;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,aAAa,CAAC,CAkBxB;AAED,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOtE"}
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Rust Security Scanner
3
+ *
4
+ * Scans Rust projects for security vulnerabilities using
5
+ * cargo-audit and clippy.
6
+ *
7
+ * @module scanners/rust
8
+ */
9
+ import { exec } from "child_process";
10
+ import { promisify } from "util";
11
+ import { access } from "fs/promises";
12
+ import { join } from "path";
13
+ const execAsync = promisify(exec);
14
+ export async function checkCargoAuditAvailable() {
15
+ try {
16
+ const { stdout } = await execAsync("cargo audit --version", { timeout: 10000 });
17
+ return {
18
+ scanner: "cargo-audit",
19
+ available: true,
20
+ version: stdout.trim(),
21
+ };
22
+ }
23
+ catch {
24
+ return {
25
+ scanner: "cargo-audit",
26
+ available: false,
27
+ error: "cargo-audit not found. Install with: cargo install cargo-audit",
28
+ };
29
+ }
30
+ }
31
+ export async function checkClippyAvailable() {
32
+ try {
33
+ const { stdout } = await execAsync("cargo clippy --version", { timeout: 10000 });
34
+ return {
35
+ scanner: "clippy",
36
+ available: true,
37
+ version: stdout.trim(),
38
+ };
39
+ }
40
+ catch {
41
+ return {
42
+ scanner: "clippy",
43
+ available: false,
44
+ error: "clippy not found. Install with: rustup component add clippy",
45
+ };
46
+ }
47
+ }
48
+ function mapAuditSeverity(severity) {
49
+ switch (severity.toLowerCase()) {
50
+ case "critical":
51
+ return "critical";
52
+ case "high":
53
+ return "high";
54
+ case "medium":
55
+ case "moderate":
56
+ return "medium";
57
+ default:
58
+ return "low";
59
+ }
60
+ }
61
+ export async function runCargoAudit(projectPath, options) {
62
+ const startTime = Date.now();
63
+ try {
64
+ const availability = await checkCargoAuditAvailable();
65
+ if (!availability.available) {
66
+ return {
67
+ scanner: "cargo-audit",
68
+ findings: [],
69
+ duration: Date.now() - startTime,
70
+ success: false,
71
+ error: availability.error,
72
+ };
73
+ }
74
+ const { stdout } = await execAsync(`cd "${projectPath}" && cargo audit --json`, {
75
+ timeout: options?.timeout || 120000,
76
+ maxBuffer: 10 * 1024 * 1024,
77
+ }).catch((error) => {
78
+ if (error.stdout) {
79
+ return { stdout: error.stdout, stderr: error.stderr || "" };
80
+ }
81
+ throw error;
82
+ });
83
+ const output = JSON.parse(stdout);
84
+ const findings = [];
85
+ for (const vuln of output.vulnerabilities.list) {
86
+ findings.push({
87
+ scanner: "cargo-audit",
88
+ ruleId: `cargo-audit:${vuln.advisory.id}`,
89
+ file: "Cargo.lock",
90
+ line: 1,
91
+ message: `${vuln.advisory.title} in ${vuln.package.name}@${vuln.package.version}`,
92
+ severity: mapAuditSeverity(vuln.advisory.severity),
93
+ confidence: 100,
94
+ metadata: {
95
+ package: vuln.package.name,
96
+ version: vuln.package.version,
97
+ advisoryUrl: vuln.advisory.url,
98
+ patchedVersions: vuln.versions.patched,
99
+ cvss: vuln.advisory.cvss,
100
+ description: vuln.advisory.description,
101
+ },
102
+ });
103
+ }
104
+ // Add warnings for unmaintained packages
105
+ for (const warn of output.warnings.unmaintained || []) {
106
+ findings.push({
107
+ scanner: "cargo-audit",
108
+ ruleId: `cargo-audit:${warn.advisory.id}`,
109
+ file: "Cargo.lock",
110
+ line: 1,
111
+ message: `Unmaintained package: ${warn.package.name}@${warn.package.version} - ${warn.advisory.title}`,
112
+ severity: "low",
113
+ confidence: 100,
114
+ metadata: {
115
+ package: warn.package.name,
116
+ version: warn.package.version,
117
+ type: "unmaintained",
118
+ },
119
+ });
120
+ }
121
+ return {
122
+ scanner: "cargo-audit",
123
+ findings,
124
+ duration: Date.now() - startTime,
125
+ success: true,
126
+ version: availability.version,
127
+ };
128
+ }
129
+ catch (error) {
130
+ return {
131
+ scanner: "cargo-audit",
132
+ findings: [],
133
+ duration: Date.now() - startTime,
134
+ success: false,
135
+ error: error instanceof Error ? error.message : "Unknown error",
136
+ };
137
+ }
138
+ }
139
+ export async function runClippy(projectPath, options) {
140
+ const startTime = Date.now();
141
+ try {
142
+ const availability = await checkClippyAvailable();
143
+ if (!availability.available) {
144
+ return {
145
+ scanner: "clippy",
146
+ findings: [],
147
+ duration: Date.now() - startTime,
148
+ success: false,
149
+ error: availability.error,
150
+ };
151
+ }
152
+ const { stdout, stderr } = await execAsync(`cd "${projectPath}" && cargo clippy --message-format=json -- -W clippy::all -W clippy::pedantic -W clippy::nursery 2>&1`, {
153
+ timeout: options?.timeout || 300000,
154
+ maxBuffer: 50 * 1024 * 1024,
155
+ }).catch((error) => {
156
+ if (error.stdout) {
157
+ return { stdout: error.stdout, stderr: error.stderr || "" };
158
+ }
159
+ throw error;
160
+ });
161
+ const findings = [];
162
+ const lines = stdout.split("\n").filter((l) => l.trim());
163
+ for (const line of lines) {
164
+ try {
165
+ const msg = JSON.parse(line);
166
+ if (msg.reason === "compiler-message" && msg.message && msg.message.spans?.length > 0) {
167
+ const primarySpan = msg.message.spans.find((s) => s.is_primary) || msg.message.spans[0];
168
+ // Only include security-relevant lints
169
+ const code = msg.message.code?.code || "";
170
+ const isSecurityRelevant = code.includes("unsafe") ||
171
+ code.includes("panic") ||
172
+ code.includes("unwrap") ||
173
+ code.includes("expect") ||
174
+ code.includes("transmute") ||
175
+ msg.message.level === "error";
176
+ if (isSecurityRelevant) {
177
+ findings.push({
178
+ scanner: "clippy",
179
+ ruleId: `clippy:${code}`,
180
+ file: primarySpan.file_name.replace(projectPath + "/", ""),
181
+ line: primarySpan.line_start,
182
+ endLine: primarySpan.line_end,
183
+ column: primarySpan.column_start,
184
+ message: msg.message.message,
185
+ severity: msg.message.level === "error" ? "high" : "medium",
186
+ confidence: 100,
187
+ evidence: primarySpan.text?.[0]?.text,
188
+ });
189
+ }
190
+ }
191
+ }
192
+ catch {
193
+ // Skip non-JSON lines
194
+ }
195
+ }
196
+ return {
197
+ scanner: "clippy",
198
+ findings,
199
+ duration: Date.now() - startTime,
200
+ success: true,
201
+ version: availability.version,
202
+ };
203
+ }
204
+ catch (error) {
205
+ return {
206
+ scanner: "clippy",
207
+ findings: [],
208
+ duration: Date.now() - startTime,
209
+ success: false,
210
+ error: error instanceof Error ? error.message : "Unknown error",
211
+ };
212
+ }
213
+ }
214
+ export async function runRustScanners(projectPath, options) {
215
+ const startTime = Date.now();
216
+ const [auditResult, clippyResult] = await Promise.all([
217
+ runCargoAudit(projectPath, options),
218
+ runClippy(projectPath, options),
219
+ ]);
220
+ const findings = [...auditResult.findings, ...clippyResult.findings];
221
+ const success = auditResult.success || clippyResult.success;
222
+ return {
223
+ scanner: "rust",
224
+ findings,
225
+ duration: Date.now() - startTime,
226
+ success,
227
+ error: !success ? "No Rust scanners available" : undefined,
228
+ };
229
+ }
230
+ export async function detectRust(projectPath) {
231
+ try {
232
+ await access(join(projectPath, "Cargo.toml"));
233
+ return true;
234
+ }
235
+ catch {
236
+ return false;
237
+ }
238
+ }
239
+ //# sourceMappingURL=rust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust.js","sourceRoot":"","sources":["../../src/scanners/rust.ts"],"names":[],"mappings":"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"}