grepleaks 1.0.4 → 1.1.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.
- package/bin/grepleaks.js +95 -215
- 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
|
|
12
|
+
const VERSION = '1.1.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
|
-
//
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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
|
-
|
|
227
|
-
return translations[lang]?.[key] || translations['en'][key] || key;
|
|
121
|
+
return TEXT[key] || key;
|
|
228
122
|
}
|
|
229
123
|
|
|
230
124
|
// Helpers
|
|
@@ -491,9 +385,8 @@ function generateMarkdownReport(result) {
|
|
|
491
385
|
const score = result.security_score || 0;
|
|
492
386
|
const grade = result.grade_level || 'N/A';
|
|
493
387
|
const projectName = result.project_name || 'Unknown Project';
|
|
494
|
-
const lang = getLang();
|
|
495
388
|
|
|
496
|
-
const dateStr = new Date().toLocaleDateString(
|
|
389
|
+
const dateStr = new Date().toLocaleDateString('en-US', {
|
|
497
390
|
year: 'numeric',
|
|
498
391
|
month: 'long',
|
|
499
392
|
day: 'numeric'
|
|
@@ -512,11 +405,11 @@ function generateMarkdownReport(result) {
|
|
|
512
405
|
|
|
513
406
|
// Score card
|
|
514
407
|
const scoreEmoji = score >= 90 ? '🛡️' : score >= 75 ? '✅' : score >= 50 ? '⚠️' : '🚨';
|
|
515
|
-
md += `## ${scoreEmoji}
|
|
408
|
+
md += `## ${scoreEmoji} Security Score: ${score}/100 (${grade})\n\n`;
|
|
516
409
|
|
|
517
410
|
// Summary table
|
|
518
411
|
if (vulns.length > 0) {
|
|
519
|
-
md += `|
|
|
412
|
+
md += `| Severity | Count |\n`;
|
|
520
413
|
md += `|----------|-------|\n`;
|
|
521
414
|
if (counts.critical > 0) md += `| 🔴 Critical | ${counts.critical} |\n`;
|
|
522
415
|
if (counts.high > 0) md += `| 🟠 High | ${counts.high} |\n`;
|
|
@@ -546,9 +439,7 @@ function generateMarkdownReport(result) {
|
|
|
546
439
|
severity === 'HIGH' ? '🟠' :
|
|
547
440
|
severity === 'MEDIUM' ? '🟡' : '🟢';
|
|
548
441
|
|
|
549
|
-
const severityLabel =
|
|
550
|
-
? { CRITICAL: 'Critique', HIGH: 'Élevée', MEDIUM: 'Moyenne', LOW: 'Faible' }[severity]
|
|
551
|
-
: severity.charAt(0) + severity.slice(1).toLowerCase();
|
|
442
|
+
const severityLabel = severity.charAt(0) + severity.slice(1).toLowerCase();
|
|
552
443
|
|
|
553
444
|
md += `### ${emoji} ${severityLabel}\n\n`;
|
|
554
445
|
|
|
@@ -568,7 +459,7 @@ function generateMarkdownReport(result) {
|
|
|
568
459
|
|
|
569
460
|
// Location
|
|
570
461
|
if (v.location) {
|
|
571
|
-
md += `-
|
|
462
|
+
md += `- **File:** \`${v.location}\`\n`;
|
|
572
463
|
}
|
|
573
464
|
|
|
574
465
|
// CVE
|
|
@@ -582,7 +473,7 @@ function generateMarkdownReport(result) {
|
|
|
582
473
|
if (v.current_version) md += ` v${v.current_version}`;
|
|
583
474
|
md += `\n`;
|
|
584
475
|
if (v.fixed_version) {
|
|
585
|
-
md += `-
|
|
476
|
+
md += `- **Fix:** Upgrade to v${v.fixed_version}\n`;
|
|
586
477
|
}
|
|
587
478
|
}
|
|
588
479
|
|
|
@@ -590,7 +481,7 @@ function generateMarkdownReport(result) {
|
|
|
590
481
|
|
|
591
482
|
// Code snippet
|
|
592
483
|
if (v.code_snippet) {
|
|
593
|
-
md +=
|
|
484
|
+
md += `**Affected code:**\n\`\`\`\n${v.code_snippet}\n\`\`\`\n\n`;
|
|
594
485
|
}
|
|
595
486
|
|
|
596
487
|
// Recommendation
|
|
@@ -601,7 +492,7 @@ function generateMarkdownReport(result) {
|
|
|
601
492
|
|
|
602
493
|
// Reference
|
|
603
494
|
if (v.reference_url) {
|
|
604
|
-
md += `[
|
|
495
|
+
md += `[Learn more](${v.reference_url})\n\n`;
|
|
605
496
|
}
|
|
606
497
|
|
|
607
498
|
md += `---\n\n`;
|
|
@@ -697,21 +588,6 @@ async function cmdLogout() {
|
|
|
697
588
|
success(t('logoutSuccess'));
|
|
698
589
|
}
|
|
699
590
|
|
|
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
591
|
|
|
716
592
|
async function cmdScan(args) {
|
|
717
593
|
const creds = loadCredentials();
|
|
@@ -818,11 +694,20 @@ async function cmdScan(args) {
|
|
|
818
694
|
if (low) log(` ${c.gray}● Low: ${low}${c.reset}`);
|
|
819
695
|
}
|
|
820
696
|
|
|
821
|
-
|
|
697
|
+
// Use AI-generated report from backend if available, otherwise generate locally
|
|
698
|
+
let report;
|
|
699
|
+
if (scanResult.report_markdown && scanResult.report_markdown.trim().length > 0) {
|
|
700
|
+
report = scanResult.report_markdown;
|
|
701
|
+
log(`\n${c.cyan}ℹ${c.reset} Using AI-generated report from server`);
|
|
702
|
+
} else {
|
|
703
|
+
report = generateMarkdownReport({ ...scanResult, project_name: projectName });
|
|
704
|
+
log(`\n${c.cyan}ℹ${c.reset} Generated report locally (AI report not available)`);
|
|
705
|
+
}
|
|
706
|
+
|
|
822
707
|
const reportPath = path.join(absolutePath, 'grepleaks-report.md');
|
|
823
708
|
fs.writeFileSync(reportPath, report);
|
|
824
709
|
|
|
825
|
-
log(
|
|
710
|
+
log(`${c.green}✓${c.reset} ${t('scanReportSaved')} ${c.cyan}${reportPath}${c.reset}\n`);
|
|
826
711
|
|
|
827
712
|
} finally {
|
|
828
713
|
if (fs.existsSync(tempZip)) {
|
|
@@ -842,7 +727,6 @@ ${c.bold}${t('helpCommands')}${c.reset}
|
|
|
842
727
|
login ${t('helpLoginDesc')}
|
|
843
728
|
logout ${t('helpLogoutDesc')}
|
|
844
729
|
scan <path> ${t('helpScanDesc')}
|
|
845
|
-
lang <en|fr> ${t('helpLangDesc')}
|
|
846
730
|
|
|
847
731
|
${c.bold}${t('helpOptions')}${c.reset}
|
|
848
732
|
-h, --help ${t('helpShowHelp')}
|
|
@@ -852,7 +736,6 @@ ${c.bold}${t('helpExamples')}${c.reset}
|
|
|
852
736
|
grepleaks login
|
|
853
737
|
grepleaks scan .
|
|
854
738
|
grepleaks scan ./my-project
|
|
855
|
-
grepleaks lang fr
|
|
856
739
|
|
|
857
740
|
${c.bold}${t('helpMoreInfo')}${c.reset}
|
|
858
741
|
${c.cyan}https://grepleaks.com${c.reset}
|
|
@@ -883,9 +766,6 @@ async function main() {
|
|
|
883
766
|
case 'logout':
|
|
884
767
|
await cmdLogout();
|
|
885
768
|
break;
|
|
886
|
-
case 'lang':
|
|
887
|
-
await cmdLang(args);
|
|
888
|
-
break;
|
|
889
769
|
case 'scan':
|
|
890
770
|
await cmdScan(args);
|
|
891
771
|
break;
|