grepleaks 1.3.3 → 1.3.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.
Files changed (2) hide show
  1. package/bin/grepleaks.js +96 -21
  2. package/package.json +1 -1
package/bin/grepleaks.js CHANGED
@@ -136,8 +136,20 @@ async function createZip() {
136
136
  });
137
137
  }
138
138
 
139
- // Send scan request to API
139
+ // Send scan request to API (async endpoint with AI analysis)
140
140
  async function sendScan(zipPath, apiKey) {
141
+ // Step 1: Submit the scan job
142
+ const jobId = await submitScanJob(zipPath, apiKey);
143
+
144
+ // Step 2: Poll for completion
145
+ logInfo('Processing with AI analysis...');
146
+ const result = await pollForCompletion(jobId, apiKey);
147
+
148
+ return result;
149
+ }
150
+
151
+ // Submit scan job to async endpoint
152
+ function submitScanJob(zipPath, apiKey) {
141
153
  return new Promise((resolve, reject) => {
142
154
  const boundary = '----FormBoundary' + Math.random().toString(36).slice(2);
143
155
  const fileContent = fs.readFileSync(zipPath);
@@ -153,7 +165,7 @@ async function sendScan(zipPath, apiKey) {
153
165
  const bodyEndBuf = Buffer.from(bodyEnd);
154
166
  const body = Buffer.concat([bodyStart, fileContent, bodyEndBuf]);
155
167
 
156
- const url = new URL(`${API_URL}/api/v1/scan`);
168
+ const url = new URL(`${API_URL}/api/v1/scan/async`);
157
169
  const options = {
158
170
  hostname: url.hostname,
159
171
  port: url.port || 80,
@@ -171,7 +183,14 @@ async function sendScan(zipPath, apiKey) {
171
183
  res.on('data', (chunk) => data += chunk);
172
184
  res.on('end', () => {
173
185
  try {
174
- resolve(JSON.parse(data));
186
+ const response = JSON.parse(data);
187
+ if (response.error) {
188
+ reject(new Error(response.error));
189
+ } else if (response.job_id) {
190
+ resolve(response.job_id);
191
+ } else {
192
+ reject(new Error('No job_id in response'));
193
+ }
175
194
  } catch (e) {
176
195
  reject(new Error(`Invalid response: ${data}`));
177
196
  }
@@ -184,6 +203,57 @@ async function sendScan(zipPath, apiKey) {
184
203
  });
185
204
  }
186
205
 
206
+ // Poll for job completion
207
+ function pollForCompletion(jobId, apiKey, maxAttempts = 60) {
208
+ return new Promise((resolve, reject) => {
209
+ let attempts = 0;
210
+
211
+ const checkStatus = () => {
212
+ attempts++;
213
+
214
+ const url = new URL(`${API_URL}/api/v1/scan/${jobId}/status`);
215
+ const options = {
216
+ hostname: url.hostname,
217
+ port: url.port || 80,
218
+ path: url.pathname,
219
+ method: 'GET',
220
+ headers: {
221
+ 'X-API-Key': apiKey,
222
+ },
223
+ };
224
+
225
+ const req = http.request(options, (res) => {
226
+ let data = '';
227
+ res.on('data', (chunk) => data += chunk);
228
+ res.on('end', () => {
229
+ try {
230
+ const response = JSON.parse(data);
231
+
232
+ if (response.status === 'completed') {
233
+ resolve(response.result || response);
234
+ } else if (response.status === 'failed') {
235
+ reject(new Error(response.error || 'Scan failed'));
236
+ } else if (attempts >= maxAttempts) {
237
+ reject(new Error('Scan timed out'));
238
+ } else {
239
+ // Still processing, wait and retry
240
+ process.stdout.write('.');
241
+ setTimeout(checkStatus, 2000);
242
+ }
243
+ } catch (e) {
244
+ reject(new Error(`Invalid response: ${data}`));
245
+ }
246
+ });
247
+ });
248
+
249
+ req.on('error', (e) => reject(e));
250
+ req.end();
251
+ };
252
+
253
+ checkStatus();
254
+ });
255
+ }
256
+
187
257
  // Generate markdown report
188
258
  function generateReport(result) {
189
259
  const date = new Date().toISOString().split('T')[0];
@@ -193,27 +263,27 @@ function generateReport(result) {
193
263
 
194
264
  **Date:** ${date} ${time}
195
265
  **Score:** ${result.security_score || 0}/100
196
- **Grade:** ${result.grade || 'N/A'}
266
+ **Grade:** ${result.grade_level || result.grade || 'N/A'}
197
267
  **Total Findings:** ${result.total_findings || 0}
198
268
 
199
269
  ## Summary
200
270
 
201
271
  | Severity | Count |
202
272
  |----------|-------|
203
- | Critical | ${result.critical_count || 0} |
204
- | High | ${result.high_count || 0} |
205
- | Medium | ${result.medium_count || 0} |
206
- | Low | ${result.low_count || 0} |
273
+ | Critical | ${result.critical_findings || result.critical_count || 0} |
274
+ | High | ${result.high_findings || result.high_count || 0} |
275
+ | Medium | ${result.medium_findings || result.medium_count || 0} |
276
+ | Low | ${result.low_findings || result.low_count || 0} |
207
277
 
208
278
  `;
209
279
 
210
- if (result.findings && result.findings.length > 0) {
280
+ const findings = result.vulnerabilities || result.findings || [];
281
+ if (findings.length > 0) {
211
282
  report += `## Findings\n\n`;
212
- result.findings.forEach((finding, i) => {
213
- report += `### ${i + 1}. ${finding.title || finding.rule_id || 'Finding'}\n\n`;
283
+ findings.forEach((finding, i) => {
284
+ report += `### ${i + 1}. ${finding.rule_id || finding.title || 'Finding'}\n\n`;
214
285
  report += `- **Severity:** ${finding.severity || 'Unknown'}\n`;
215
- report += `- **File:** ${finding.file || 'N/A'}\n`;
216
- if (finding.line) report += `- **Line:** ${finding.line}\n`;
286
+ report += `- **Location:** ${finding.location || finding.file || 'N/A'}\n`;
217
287
  if (finding.description) report += `- **Description:** ${finding.description}\n`;
218
288
  report += '\n';
219
289
  });
@@ -371,27 +441,32 @@ ${colors.bold} SECURITY SCAN RESULTS${colors.reset}
371
441
  ${colors.bold}========================================${colors.reset}
372
442
 
373
443
  Score: ${colors.bold}${result.security_score || 0}/100${colors.reset}
374
- Grade: ${colors.bold}${result.grade || 'N/A'}${colors.reset}
444
+ Grade: ${colors.bold}${result.grade_level || result.grade || 'N/A'}${colors.reset}
375
445
 
376
446
  Findings:
377
- Critical: ${colors.red}${result.critical_count || 0}${colors.reset}
378
- High: ${colors.yellow}${result.high_count || 0}${colors.reset}
379
- Medium: ${colors.blue}${result.medium_count || 0}${colors.reset}
380
- Low: ${result.low_count || 0}
447
+ Critical: ${colors.red}${result.critical_findings || result.critical_count || 0}${colors.reset}
448
+ High: ${colors.yellow}${result.high_findings || result.high_count || 0}${colors.reset}
449
+ Medium: ${colors.blue}${result.medium_findings || result.medium_count || 0}${colors.reset}
450
+ Low: ${result.low_findings || result.low_count || 0}
381
451
 
382
452
  ${colors.bold}========================================${colors.reset}
383
453
  `);
384
454
 
385
- // Create report
455
+ // Create report - use AI-generated report from API if available
386
456
  if (config.createReport !== false) {
387
- const report = generateReport(result);
457
+ let report;
458
+ if (result.report_markdown) {
459
+ report = result.report_markdown;
460
+ } else {
461
+ report = generateReport(result);
462
+ }
388
463
  const reportPath = path.join(process.cwd(), 'SECURITY_REPORT.md');
389
464
  fs.writeFileSync(reportPath, report);
390
465
  logSuccess(`Report saved to SECURITY_REPORT.md`);
391
466
  }
392
467
 
393
468
  // Exit with error if critical findings
394
- if ((result.critical_count || 0) > 0) {
469
+ if ((result.critical_findings || result.critical_count || 0) > 0) {
395
470
  logError('Critical vulnerabilities found!');
396
471
  process.exit(1);
397
472
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepleaks",
3
- "version": "1.3.3",
3
+ "version": "1.3.6",
4
4
  "description": "Security scanner for your code - detect vulnerabilities, secrets, and misconfigurations",
5
5
  "main": "bin/grepleaks.js",
6
6
  "bin": {