grepleaks 1.3.4 → 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 +85 -10
  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];
@@ -207,13 +277,13 @@ function generateReport(result) {
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
  });
@@ -382,9 +452,14 @@ ${colors.bold}========================================${colors.reset}
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`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepleaks",
3
- "version": "1.3.4",
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": {