grepleaks 1.0.4 → 1.2.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 (2) hide show
  1. package/bin/grepleaks.js +111 -217
  2. package/package.json +1 -1
package/bin/grepleaks.js CHANGED
@@ -9,185 +9,86 @@ const readline = require('readline');
9
9
  const { exec } = require('child_process');
10
10
  const archiver = require('archiver');
11
11
 
12
- const VERSION = '1.0.4';
12
+ const VERSION = '1.2.0';
13
13
  const API_URL = 'https://grepleaks.com/api/v1';
14
14
  const WEB_URL = 'https://grepleaks.com';
15
15
  const CONFIG_DIR = path.join(os.homedir(), '.grepleaks');
16
16
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
17
17
  const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
18
18
 
19
- // Translations
20
- const translations = {
21
- en: {
22
- // General
23
- tagline: 'Security scanner for your code',
24
- version: 'grepleaks v',
25
-
26
- // Help
27
- helpUsage: 'USAGE:',
28
- helpCommands: 'COMMANDS:',
29
- helpOptions: 'OPTIONS:',
30
- helpExamples: 'EXAMPLES:',
31
- helpMoreInfo: 'MORE INFO:',
32
- helpLoginDesc: 'Login (email or GitHub)',
33
- helpLogoutDesc: 'Logout',
34
- helpScanDesc: 'Scan a directory',
35
- helpLangDesc: 'Set language (en/fr)',
36
- helpShowHelp: 'Show help',
37
- helpShowVersion: 'Show version',
38
-
39
- // Login
40
- loginTitle: 'Login to grepleaks',
41
- loginChooseMethod: 'Choose login method:',
42
- loginOptionEmail: 'Email / Password',
43
- loginOptionGitHub: 'GitHub',
44
- loginChoice: 'Your choice (1 or 2): ',
45
- loginBrowserTitle: 'Login via browser',
46
- loginGitHubTitle: 'Login with GitHub',
47
- loginOpeningBrowser: 'Opening browser...',
48
- loginTokenPrompt: 'After logging in on the website, copy your token and paste it here.',
49
- loginTokenInput: 'Token: ',
50
- loginTokenRequired: 'Token required',
51
- loginVerifying: 'Verifying token...',
52
- loginInvalidToken: 'Invalid token',
53
- loginEmail: 'Email: ',
54
- loginPassword: 'Password: ',
55
- loginEmailPasswordRequired: 'Email and password required',
56
- loginInProgress: 'Logging in...',
57
- loginFailed: 'Login failed',
58
- loginSuccess: 'Logged in as',
59
-
60
- // Logout
61
- logoutSuccess: 'Logged out',
62
-
63
- // Scan
64
- scanTitle: 'Security scan',
65
- scanMustLogin: 'You must be logged in to scan.',
66
- scanPathNotFound: 'Path not found:',
67
- scanMustBeDir: 'Path must be a directory:',
68
- scanProject: 'Project:',
69
- scanPath: 'Path:',
70
- scanPreparing: 'Preparing files...',
71
- scanArchiveCreated: 'Archive created',
72
- scanUploading: 'Uploading to server...',
73
- scanUploadFailed: 'Upload failed',
74
- scanStarted: 'Scan started',
75
- scanAnalyzing: 'Analyzing...',
76
- scanStatusFailed: 'Failed to check status',
77
- scanFailed: 'Scan failed',
78
- scanComplete: 'Scan complete!',
79
- scanResults: 'Results:',
80
- scanScore: 'Score:',
81
- scanVulnerabilities: 'Vulnerabilities:',
82
- scanReportSaved: 'Report saved:',
83
-
84
- // Report
85
- reportTitle: 'Security Report',
86
- reportDate: 'Date:',
87
- reportScore: 'Score:',
88
- reportVulns: 'Vulnerabilities:',
89
- reportNoVulns: 'No vulnerabilities detected',
90
- reportCongrats: 'Congratulations! Your code has no known vulnerabilities.',
91
- reportVulnsDetected: 'Vulnerabilities detected',
92
- reportFile: 'File:',
93
- reportLine: 'Line:',
94
- reportScanner: 'Scanner:',
95
- reportRecommendation: 'Recommendation:',
96
- reportGeneratedBy: 'Report generated by',
97
-
98
- // Language
99
- langSet: 'Language set to',
100
- langInvalid: 'Invalid language. Use: en, fr',
101
-
102
- // Errors
103
- errorUnknownCmd: 'Unknown command:',
104
- errorUseHelp: "Use 'grepleaks --help' for help."
105
- },
106
- fr: {
107
- // General
108
- tagline: 'Scanner de securite pour votre code',
109
- version: 'grepleaks v',
110
-
111
- // Help
112
- helpUsage: 'USAGE:',
113
- helpCommands: 'COMMANDES:',
114
- helpOptions: 'OPTIONS:',
115
- helpExamples: 'EXEMPLES:',
116
- helpMoreInfo: 'PLUS D\'INFO:',
117
- helpLoginDesc: 'Connexion (email ou GitHub)',
118
- helpLogoutDesc: 'Deconnexion',
119
- helpScanDesc: 'Scanner un dossier',
120
- helpLangDesc: 'Changer la langue (en/fr)',
121
- helpShowHelp: 'Afficher l\'aide',
122
- helpShowVersion: 'Afficher la version',
123
-
124
- // Login
125
- loginTitle: 'Connexion a grepleaks',
126
- loginChooseMethod: 'Choisissez votre methode de connexion:',
127
- loginOptionEmail: 'Email / Mot de passe',
128
- loginOptionGitHub: 'GitHub',
129
- loginChoice: 'Votre choix (1 ou 2): ',
130
- loginBrowserTitle: 'Connexion via navigateur',
131
- loginGitHubTitle: 'Connexion avec GitHub',
132
- loginOpeningBrowser: 'Ouverture du navigateur...',
133
- loginTokenPrompt: 'Apres connexion sur le site, copiez votre token et collez-le ici.',
134
- loginTokenInput: 'Token: ',
135
- loginTokenRequired: 'Token requis',
136
- loginVerifying: 'Verification du token...',
137
- loginInvalidToken: 'Token invalide',
138
- loginEmail: 'Email: ',
139
- loginPassword: 'Mot de passe: ',
140
- loginEmailPasswordRequired: 'Email et mot de passe requis',
141
- loginInProgress: 'Connexion en cours...',
142
- loginFailed: 'Echec de connexion',
143
- loginSuccess: 'Connecte en tant que',
144
-
145
- // Logout
146
- logoutSuccess: 'Deconnecte',
147
-
148
- // Scan
149
- scanTitle: 'Scan de securite',
150
- scanMustLogin: 'Vous devez etre connecte pour scanner.',
151
- scanPathNotFound: 'Chemin introuvable:',
152
- scanMustBeDir: 'Le chemin doit etre un dossier:',
153
- scanProject: 'Projet:',
154
- scanPath: 'Chemin:',
155
- scanPreparing: 'Preparation des fichiers...',
156
- scanArchiveCreated: 'Archive creee',
157
- scanUploading: 'Envoi au serveur...',
158
- scanUploadFailed: 'Echec de l\'upload',
159
- scanStarted: 'Scan demarre',
160
- scanAnalyzing: 'Analyse en cours...',
161
- scanStatusFailed: 'Echec de verification du statut',
162
- scanFailed: 'Le scan a echoue',
163
- scanComplete: 'Scan termine!',
164
- scanResults: 'Resultats:',
165
- scanScore: 'Score:',
166
- scanVulnerabilities: 'Vulnerabilites:',
167
- scanReportSaved: 'Rapport sauvegarde:',
168
-
169
- // Report
170
- reportTitle: 'Rapport de Securite',
171
- reportDate: 'Date:',
172
- reportScore: 'Score:',
173
- reportVulns: 'Vulnerabilites:',
174
- reportNoVulns: 'Aucune vulnerabilite detectee',
175
- reportCongrats: 'Felicitations! Votre code ne presente aucune vulnerabilite connue.',
176
- reportVulnsDetected: 'Vulnerabilites detectees',
177
- reportFile: 'Fichier:',
178
- reportLine: 'Ligne:',
179
- reportScanner: 'Scanner:',
180
- reportRecommendation: 'Recommandation:',
181
- reportGeneratedBy: 'Rapport genere par',
182
-
183
- // Language
184
- langSet: 'Langue definie:',
185
- langInvalid: 'Langue invalide. Utilisez: en, fr',
186
-
187
- // Errors
188
- errorUnknownCmd: 'Commande inconnue:',
189
- errorUseHelp: "Utilisez 'grepleaks --help' pour l'aide."
190
- }
19
+ // Text strings (English only)
20
+ const TEXT = {
21
+ // General
22
+ tagline: 'Security scanner for your code',
23
+ version: 'grepleaks v',
24
+
25
+ // Help
26
+ helpUsage: 'USAGE:',
27
+ helpCommands: 'COMMANDS:',
28
+ helpOptions: 'OPTIONS:',
29
+ helpExamples: 'EXAMPLES:',
30
+ helpMoreInfo: 'MORE INFO:',
31
+ helpLoginDesc: 'Login (email or GitHub)',
32
+ helpLogoutDesc: 'Logout',
33
+ helpScanDesc: 'Scan a directory',
34
+ helpShowHelp: 'Show help',
35
+ helpShowVersion: 'Show version',
36
+
37
+ // Login
38
+ loginTitle: 'Login to grepleaks',
39
+ loginChooseMethod: 'Choose login method:',
40
+ loginOptionEmail: 'Email / Password',
41
+ loginOptionGitHub: 'GitHub',
42
+ loginChoice: 'Your choice (1 or 2): ',
43
+ loginGitHubTitle: 'Login with GitHub',
44
+ loginOpeningBrowser: 'Opening browser...',
45
+ loginTokenPrompt: 'After logging in on the website, copy your token and paste it here.',
46
+ loginTokenInput: 'Token: ',
47
+ loginTokenRequired: 'Token required',
48
+ loginVerifying: 'Verifying token...',
49
+ loginInvalidToken: 'Invalid token',
50
+ loginEmail: 'Email: ',
51
+ loginPassword: 'Password: ',
52
+ loginEmailPasswordRequired: 'Email and password required',
53
+ loginInProgress: 'Logging in...',
54
+ loginFailed: 'Login failed',
55
+ loginSuccess: 'Logged in as',
56
+
57
+ // Logout
58
+ logoutSuccess: 'Logged out',
59
+
60
+ // Scan
61
+ scanTitle: 'Security scan',
62
+ scanMustLogin: 'You must be logged in to scan.',
63
+ scanPathNotFound: 'Path not found:',
64
+ scanMustBeDir: 'Path must be a directory:',
65
+ scanProject: 'Project:',
66
+ scanPath: 'Path:',
67
+ scanPreparing: 'Preparing files...',
68
+ scanArchiveCreated: 'Archive created',
69
+ scanUploading: 'Uploading to server...',
70
+ scanUploadFailed: 'Upload failed',
71
+ scanStarted: 'Scan started',
72
+ scanAnalyzing: 'Analyzing...',
73
+ scanStatusFailed: 'Failed to check status',
74
+ scanFailed: 'Scan failed',
75
+ scanComplete: 'Scan complete!',
76
+ scanResults: 'Results:',
77
+ scanScore: 'Score:',
78
+ scanVulnerabilities: 'Vulnerabilities:',
79
+ scanReportSaved: 'Report saved:',
80
+
81
+ // Report
82
+ reportTitle: 'Security Report',
83
+ reportNoVulns: 'No vulnerabilities detected',
84
+ reportCongrats: 'Congratulations! Your code has no known vulnerabilities.',
85
+ reportVulnsDetected: 'Vulnerabilities detected',
86
+ reportRecommendation: 'Recommendation',
87
+ reportGeneratedBy: 'Report generated by',
88
+
89
+ // Errors
90
+ errorUnknownCmd: 'Unknown command:',
91
+ errorUseHelp: "Use 'grepleaks --help' for help."
191
92
  };
192
93
 
193
94
  // Files/folders to skip when zipping
@@ -215,16 +116,9 @@ const c = {
215
116
  gray: '\x1b[90m'
216
117
  };
217
118
 
218
- // Get current language
219
- function getLang() {
220
- const config = loadConfig();
221
- return config.lang || 'en';
222
- }
223
-
224
- // Get translation
119
+ // Get text string
225
120
  function t(key) {
226
- const lang = getLang();
227
- return translations[lang]?.[key] || translations['en'][key] || key;
121
+ return TEXT[key] || key;
228
122
  }
229
123
 
230
124
  // Helpers
@@ -388,7 +282,7 @@ function request(method, url, data = null, headers = {}) {
388
282
  }
389
283
 
390
284
  // Upload file with multipart
391
- function uploadFile(url, filePath, token) {
285
+ function uploadFile(url, filePath, token, projectName) {
392
286
  return new Promise((resolve, reject) => {
393
287
  const boundary = '----grepleaks' + Date.now();
394
288
  const fileName = path.basename(filePath);
@@ -422,6 +316,19 @@ function uploadFile(url, filePath, token) {
422
316
 
423
317
  req.on('error', reject);
424
318
 
319
+ // Add project_name field
320
+ req.write(`--${boundary}\r\n`);
321
+ req.write(`Content-Disposition: form-data; name="project_name"\r\n\r\n`);
322
+ req.write(projectName || 'Unknown');
323
+ req.write('\r\n');
324
+
325
+ // Add source field
326
+ req.write(`--${boundary}\r\n`);
327
+ req.write(`Content-Disposition: form-data; name="source"\r\n\r\n`);
328
+ req.write('cli');
329
+ req.write('\r\n');
330
+
331
+ // Add file
425
332
  req.write(`--${boundary}\r\n`);
426
333
  req.write(`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`);
427
334
  req.write('Content-Type: application/zip\r\n\r\n');
@@ -491,9 +398,8 @@ function generateMarkdownReport(result) {
491
398
  const score = result.security_score || 0;
492
399
  const grade = result.grade_level || 'N/A';
493
400
  const projectName = result.project_name || 'Unknown Project';
494
- const lang = getLang();
495
401
 
496
- const dateStr = new Date().toLocaleDateString(lang === 'fr' ? 'fr-FR' : 'en-US', {
402
+ const dateStr = new Date().toLocaleDateString('en-US', {
497
403
  year: 'numeric',
498
404
  month: 'long',
499
405
  day: 'numeric'
@@ -512,11 +418,11 @@ function generateMarkdownReport(result) {
512
418
 
513
419
  // Score card
514
420
  const scoreEmoji = score >= 90 ? '🛡️' : score >= 75 ? '✅' : score >= 50 ? '⚠️' : '🚨';
515
- md += `## ${scoreEmoji} ${lang === 'fr' ? 'Score de Sécurité' : 'Security Score'}: ${score}/100 (${grade})\n\n`;
421
+ md += `## ${scoreEmoji} Security Score: ${score}/100 (${grade})\n\n`;
516
422
 
517
423
  // Summary table
518
424
  if (vulns.length > 0) {
519
- md += `| ${lang === 'fr' ? 'Sévérité' : 'Severity'} | ${lang === 'fr' ? 'Nombre' : 'Count'} |\n`;
425
+ md += `| Severity | Count |\n`;
520
426
  md += `|----------|-------|\n`;
521
427
  if (counts.critical > 0) md += `| 🔴 Critical | ${counts.critical} |\n`;
522
428
  if (counts.high > 0) md += `| 🟠 High | ${counts.high} |\n`;
@@ -546,9 +452,7 @@ function generateMarkdownReport(result) {
546
452
  severity === 'HIGH' ? '🟠' :
547
453
  severity === 'MEDIUM' ? '🟡' : '🟢';
548
454
 
549
- const severityLabel = lang === 'fr'
550
- ? { CRITICAL: 'Critique', HIGH: 'Élevée', MEDIUM: 'Moyenne', LOW: 'Faible' }[severity]
551
- : severity.charAt(0) + severity.slice(1).toLowerCase();
455
+ const severityLabel = severity.charAt(0) + severity.slice(1).toLowerCase();
552
456
 
553
457
  md += `### ${emoji} ${severityLabel}\n\n`;
554
458
 
@@ -568,7 +472,7 @@ function generateMarkdownReport(result) {
568
472
 
569
473
  // Location
570
474
  if (v.location) {
571
- md += `- **${lang === 'fr' ? 'Fichier' : 'File'}:** \`${v.location}\`\n`;
475
+ md += `- **File:** \`${v.location}\`\n`;
572
476
  }
573
477
 
574
478
  // CVE
@@ -582,7 +486,7 @@ function generateMarkdownReport(result) {
582
486
  if (v.current_version) md += ` v${v.current_version}`;
583
487
  md += `\n`;
584
488
  if (v.fixed_version) {
585
- md += `- **${lang === 'fr' ? 'Correction' : 'Fix'}:** ${lang === 'fr' ? 'Mettre à jour vers' : 'Upgrade to'} v${v.fixed_version}\n`;
489
+ md += `- **Fix:** Upgrade to v${v.fixed_version}\n`;
586
490
  }
587
491
  }
588
492
 
@@ -590,7 +494,7 @@ function generateMarkdownReport(result) {
590
494
 
591
495
  // Code snippet
592
496
  if (v.code_snippet) {
593
- md += `**${lang === 'fr' ? 'Code concerné' : 'Affected code'}:**\n\`\`\`\n${v.code_snippet}\n\`\`\`\n\n`;
497
+ md += `**Affected code:**\n\`\`\`\n${v.code_snippet}\n\`\`\`\n\n`;
594
498
  }
595
499
 
596
500
  // Recommendation
@@ -601,7 +505,7 @@ function generateMarkdownReport(result) {
601
505
 
602
506
  // Reference
603
507
  if (v.reference_url) {
604
- md += `[${lang === 'fr' ? 'Plus d\'informations' : 'Learn more'}](${v.reference_url})\n\n`;
508
+ md += `[Learn more](${v.reference_url})\n\n`;
605
509
  }
606
510
 
607
511
  md += `---\n\n`;
@@ -697,21 +601,6 @@ async function cmdLogout() {
697
601
  success(t('logoutSuccess'));
698
602
  }
699
603
 
700
- async function cmdLang(args) {
701
- const lang = args[1];
702
-
703
- if (!lang) {
704
- log(`Current language: ${getLang()}`);
705
- return;
706
- }
707
-
708
- if (!['en', 'fr'].includes(lang)) {
709
- error(t('langInvalid'));
710
- }
711
-
712
- saveConfig({ lang });
713
- success(`${t('langSet')} ${lang}`);
714
- }
715
604
 
716
605
  async function cmdScan(args) {
717
606
  const creds = loadCredentials();
@@ -749,7 +638,8 @@ async function cmdScan(args) {
749
638
  const uploadRes = await uploadFile(
750
639
  `${API_URL}/scan/async`,
751
640
  tempZip,
752
- creds.access_token
641
+ creds.access_token,
642
+ projectName
753
643
  );
754
644
 
755
645
  if (uploadRes.status !== 202) {
@@ -818,11 +708,20 @@ async function cmdScan(args) {
818
708
  if (low) log(` ${c.gray}● Low: ${low}${c.reset}`);
819
709
  }
820
710
 
821
- const report = generateMarkdownReport({ ...scanResult, project_name: projectName });
711
+ // Use AI-generated report from backend if available, otherwise generate locally
712
+ let report;
713
+ if (scanResult.report_markdown && scanResult.report_markdown.trim().length > 0) {
714
+ report = scanResult.report_markdown;
715
+ log(`\n${c.cyan}ℹ${c.reset} Using AI-generated report from server`);
716
+ } else {
717
+ report = generateMarkdownReport({ ...scanResult, project_name: projectName });
718
+ log(`\n${c.cyan}ℹ${c.reset} Generated report locally (AI report not available)`);
719
+ }
720
+
822
721
  const reportPath = path.join(absolutePath, 'grepleaks-report.md');
823
722
  fs.writeFileSync(reportPath, report);
824
723
 
825
- log(`\n${c.green}✓${c.reset} ${t('scanReportSaved')} ${c.cyan}${reportPath}${c.reset}\n`);
724
+ log(`${c.green}✓${c.reset} ${t('scanReportSaved')} ${c.cyan}${reportPath}${c.reset}\n`);
826
725
 
827
726
  } finally {
828
727
  if (fs.existsSync(tempZip)) {
@@ -842,7 +741,6 @@ ${c.bold}${t('helpCommands')}${c.reset}
842
741
  login ${t('helpLoginDesc')}
843
742
  logout ${t('helpLogoutDesc')}
844
743
  scan <path> ${t('helpScanDesc')}
845
- lang <en|fr> ${t('helpLangDesc')}
846
744
 
847
745
  ${c.bold}${t('helpOptions')}${c.reset}
848
746
  -h, --help ${t('helpShowHelp')}
@@ -852,7 +750,6 @@ ${c.bold}${t('helpExamples')}${c.reset}
852
750
  grepleaks login
853
751
  grepleaks scan .
854
752
  grepleaks scan ./my-project
855
- grepleaks lang fr
856
753
 
857
754
  ${c.bold}${t('helpMoreInfo')}${c.reset}
858
755
  ${c.cyan}https://grepleaks.com${c.reset}
@@ -883,9 +780,6 @@ async function main() {
883
780
  case 'logout':
884
781
  await cmdLogout();
885
782
  break;
886
- case 'lang':
887
- await cmdLang(args);
888
- break;
889
783
  case 'scan':
890
784
  await cmdScan(args);
891
785
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepleaks",
3
- "version": "1.0.4",
3
+ "version": "1.2.0",
4
4
  "description": "Security scanner for your code - detect vulnerabilities, secrets, and misconfigurations",
5
5
  "main": "bin/grepleaks.js",
6
6
  "bin": {