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.
- package/bin/grepleaks.js +111 -217
- 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.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
|
-
//
|
|
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
|
|
@@ -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(
|
|
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}
|
|
421
|
+
md += `## ${scoreEmoji} Security Score: ${score}/100 (${grade})\n\n`;
|
|
516
422
|
|
|
517
423
|
// Summary table
|
|
518
424
|
if (vulns.length > 0) {
|
|
519
|
-
md += `|
|
|
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 =
|
|
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 += `-
|
|
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 += `-
|
|
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 +=
|
|
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 += `[
|
|
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
|
-
|
|
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(
|
|
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;
|