git-cli-scanner 1.1.5 → 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/dist/cli.js +146 -56
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -69,32 +69,44 @@ var init_apiKeys = __esm({
|
|
|
69
69
|
{
|
|
70
70
|
id: "stripe-key",
|
|
71
71
|
description: "Stripe Secret/Restricted Key",
|
|
72
|
-
pattern: /(?:sk|rk)_(?:test|live)_[0-9a-zA-Z]{24}
|
|
72
|
+
pattern: /(?:sk|rk)_(?:test|live)_[0-9a-zA-Z]{24}/,
|
|
73
|
+
risk: "An attacker can issue unauthorized refunds, access customer payment data, or disrupt billing operations.",
|
|
74
|
+
solution: "Revoke in Stripe Dashboard (Developers > API keys). Use a secret manager (e.g. AWS Secrets Manager, Doppler) or .env files, and load via process.env."
|
|
73
75
|
},
|
|
74
76
|
{
|
|
75
77
|
id: "sendgrid-key",
|
|
76
78
|
description: "SendGrid API Key",
|
|
77
|
-
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}
|
|
79
|
+
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/,
|
|
80
|
+
risk: "Allows unauthorized sending of emails from your domains, leading to phishing campaigns and domain blacklisting.",
|
|
81
|
+
solution: "Revoke in Twilio SendGrid UI (Settings > API Keys). Store the new key securely using environment variables."
|
|
78
82
|
},
|
|
79
83
|
{
|
|
80
84
|
id: "mailgun-key",
|
|
81
85
|
description: "Mailgun API Key",
|
|
82
|
-
pattern: /key-[0-9a-zA-Z]{32}
|
|
86
|
+
pattern: /key-[0-9a-zA-Z]{32}/,
|
|
87
|
+
risk: "Allows an attacker to read incoming emails, send spam, and permanently ruin your email domain reputation.",
|
|
88
|
+
solution: "Rotate the key immediately in Mailgun Settings. Hardcoded Mailgun keys can lead to severe email spoofing and domain reputation loss."
|
|
83
89
|
},
|
|
84
90
|
{
|
|
85
91
|
id: "twilio-key",
|
|
86
92
|
description: "Twilio API Key",
|
|
87
|
-
pattern: /SK[0-9a-fA-F]{32}
|
|
93
|
+
pattern: /SK[0-9a-fA-F]{32}/,
|
|
94
|
+
risk: "Attackers can incur massive financial charges via SMS toll fraud or hijack phone numbers.",
|
|
95
|
+
solution: "Delete this key in the Twilio Console (Account > API keys & tokens). Never hardcode communication API keys."
|
|
88
96
|
},
|
|
89
97
|
{
|
|
90
98
|
id: "generic-api-key",
|
|
91
99
|
description: "Generic API Key / Secret / Password / Passphrase",
|
|
92
|
-
pattern: /(?:api[_\-]?key|secret|token|password|pwd|passphrase)[\s:=]+["'][a-zA-Z0-9\-_!@#$%^&*()=+]{8,}["']/i
|
|
100
|
+
pattern: /(?:api[_\-]?key|secret|token|password|pwd|passphrase)[\s:=]+["'][a-zA-Z0-9\-_!@#$%^&*()=+]{8,}["']/i,
|
|
101
|
+
risk: "Generic secrets often grant unrestricted access to databases, third-party APIs, or internal microservices.",
|
|
102
|
+
solution: "Extract this secret into a local .env file. Ensure .env is added to your .gitignore so it never enters source control."
|
|
93
103
|
},
|
|
94
104
|
{
|
|
95
105
|
id: "generic-bearer-token",
|
|
96
106
|
description: "Generic Bearer Token",
|
|
97
|
-
pattern: /bearer\s+[a-zA-Z0-9\-_.]{20,}/i
|
|
107
|
+
pattern: /bearer\s+[a-zA-Z0-9\-_.]{20,}/i,
|
|
108
|
+
risk: "Bearer tokens allow impersonation of users or service accounts to access protected REST APIs directly.",
|
|
109
|
+
solution: "Bearer tokens grant direct access to APIs. Remove it from code, revoke the session, and inject it securely at runtime."
|
|
98
110
|
}
|
|
99
111
|
];
|
|
100
112
|
apiKeyScanner = {
|
|
@@ -114,7 +126,8 @@ var init_apiKeys = __esm({
|
|
|
114
126
|
line: lineNumber,
|
|
115
127
|
match: cleanLine.trim().substring(0, 50) + "...",
|
|
116
128
|
severity: "high",
|
|
117
|
-
solution:
|
|
129
|
+
solution: rule.solution,
|
|
130
|
+
risk: rule.risk
|
|
118
131
|
});
|
|
119
132
|
}
|
|
120
133
|
}
|
|
@@ -136,17 +149,23 @@ var init_cloudProviders = __esm({
|
|
|
136
149
|
{
|
|
137
150
|
id: "aws-access-key",
|
|
138
151
|
description: "AWS Access Key ID",
|
|
139
|
-
pattern: /(?:A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
|
|
152
|
+
pattern: /(?:A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}/,
|
|
153
|
+
risk: "An attacker can use this to spin up thousands of crypto-mining EC2 instances, costing you massive amounts of money.",
|
|
154
|
+
solution: "Deactivate the key in AWS IAM Console immediately. Switch to using short-lived credentials via AWS STS or IAM Roles whenever possible."
|
|
140
155
|
},
|
|
141
156
|
{
|
|
142
157
|
id: "aws-secret-key",
|
|
143
158
|
description: "AWS Secret Access Key (heuristics)",
|
|
144
|
-
pattern: /aws_?(?:secret)?_?(?:access)?_?key[\s:=]+["'][a-zA-Z0-9\/+]{40}["']/i
|
|
159
|
+
pattern: /aws_?(?:secret)?_?(?:access)?_?key[\s:=]+["'][a-zA-Z0-9\/+]{40}["']/i,
|
|
160
|
+
risk: "Compromised AWS Secret Keys grant direct access to your entire cloud infrastructure, databases, and storage buckets.",
|
|
161
|
+
solution: "This secret grants access to your AWS infrastructure. Remove it from the codebase and inject it using a secure CI/CD secrets manager or HashiCorp Vault."
|
|
145
162
|
},
|
|
146
163
|
{
|
|
147
164
|
id: "gcp-api-key",
|
|
148
165
|
description: "Google Cloud API Key",
|
|
149
|
-
pattern: /AIza[0-9A-Za-z\-_]{35}
|
|
166
|
+
pattern: /AIza[0-9A-Za-z\-_]{35}/,
|
|
167
|
+
risk: "Attackers can bypass quotas to abuse GCP services like Maps API or Vertex AI, causing high billing charges.",
|
|
168
|
+
solution: "Restrict the key in Google Cloud Console (APIs & Services > Credentials) to specific IP addresses/apps, or revoke it and use GCP Service Accounts."
|
|
150
169
|
}
|
|
151
170
|
];
|
|
152
171
|
cloudProviderScanner = {
|
|
@@ -166,7 +185,8 @@ var init_cloudProviders = __esm({
|
|
|
166
185
|
line: lineNumber,
|
|
167
186
|
match: cleanLine.trim().substring(0, 50) + "...",
|
|
168
187
|
severity: "high",
|
|
169
|
-
solution:
|
|
188
|
+
solution: rule.solution,
|
|
189
|
+
risk: rule.risk
|
|
170
190
|
});
|
|
171
191
|
}
|
|
172
192
|
}
|
|
@@ -188,22 +208,30 @@ var init_collaboration = __esm({
|
|
|
188
208
|
{
|
|
189
209
|
id: "github-pat",
|
|
190
210
|
description: "GitHub Personal Access Token",
|
|
191
|
-
pattern: /ghp_[a-zA-Z0-9]{36}
|
|
211
|
+
pattern: /ghp_[a-zA-Z0-9]{36}/,
|
|
212
|
+
risk: "Attackers can read, modify, or delete your entire private source code and infrastructure configurations.",
|
|
213
|
+
solution: "Revoke this token immediately in your GitHub Developer Settings. For automated workflows, use a fine-grained PAT or GitHub Apps."
|
|
192
214
|
},
|
|
193
215
|
{
|
|
194
216
|
id: "github-oauth",
|
|
195
217
|
description: "GitHub OAuth Access Token",
|
|
196
|
-
pattern: /gho_[a-zA-Z0-9]{36}
|
|
218
|
+
pattern: /gho_[a-zA-Z0-9]{36}/,
|
|
219
|
+
risk: "Grants third-party level access to GitHub accounts, leading to source code theft or supply chain attacks.",
|
|
220
|
+
solution: "Revoke the OAuth token via GitHub Settings > Authorized OAuth Apps. Never hardcode OAuth tokens."
|
|
197
221
|
},
|
|
198
222
|
{
|
|
199
223
|
id: "slack-token",
|
|
200
224
|
description: "Slack Token",
|
|
201
|
-
pattern: /xox[pboar]-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{32}
|
|
225
|
+
pattern: /xox[pboar]-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{10,13}-[a-zA-Z0-9]{32}/,
|
|
226
|
+
risk: "An attacker can read private messages, extract company secrets, and socially engineer employees using internal channels.",
|
|
227
|
+
solution: "Regenerate the Slack App Token in your Slack API Dashboard. If pushed, an attacker can access your internal team communications."
|
|
202
228
|
},
|
|
203
229
|
{
|
|
204
230
|
id: "slack-webhook",
|
|
205
231
|
description: "Slack Webhook",
|
|
206
|
-
pattern: /https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9_]{8,10}\/B[a-zA-Z0-9_]{8,10}\/[a-zA-Z0-9_]{24}
|
|
232
|
+
pattern: /https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9_]{8,10}\/B[a-zA-Z0-9_]{8,10}\/[a-zA-Z0-9_]{24}/,
|
|
233
|
+
risk: "Allows unauthenticated actors to spam or spoof messages into your internal team channels.",
|
|
234
|
+
solution: "Delete the webhook in Slack and create a new one. Load the webhook URL via environment variables in production."
|
|
207
235
|
}
|
|
208
236
|
];
|
|
209
237
|
collaborationScanner = {
|
|
@@ -223,7 +251,8 @@ var init_collaboration = __esm({
|
|
|
223
251
|
line: lineNumber,
|
|
224
252
|
match: cleanLine.trim().substring(0, 50) + "...",
|
|
225
253
|
severity: "high",
|
|
226
|
-
solution:
|
|
254
|
+
solution: rule.solution,
|
|
255
|
+
risk: rule.risk
|
|
227
256
|
});
|
|
228
257
|
}
|
|
229
258
|
}
|
|
@@ -245,17 +274,23 @@ var init_privateKeys = __esm({
|
|
|
245
274
|
{
|
|
246
275
|
id: "rsa-private-key",
|
|
247
276
|
description: "RSA Private Key",
|
|
248
|
-
pattern: /-----BEGIN RSA PRIVATE KEY
|
|
277
|
+
pattern: /-----BEGIN RSA PRIVATE KEY-----/,
|
|
278
|
+
risk: "Allows attackers to decrypt intercepted traffic, forge digital signatures, or authenticate to your infrastructure.",
|
|
279
|
+
solution: "RSA keys should never enter source control. Rotate this key on all associated servers/services and use a secure keystore like AWS KMS or HashiCorp Vault."
|
|
249
280
|
},
|
|
250
281
|
{
|
|
251
282
|
id: "openssh-private-key",
|
|
252
283
|
description: "OpenSSH Private Key",
|
|
253
|
-
pattern: /-----BEGIN OPENSSH PRIVATE KEY
|
|
284
|
+
pattern: /-----BEGIN OPENSSH PRIVATE KEY-----/,
|
|
285
|
+
risk: "Attackers can use this to SSH directly into your production servers, clone private repos, or pivot into your internal network.",
|
|
286
|
+
solution: "This SSH key could grant direct server access. Generate a new SSH keypair (`ssh-keygen`), replace the public key on your servers, and delete this leaked private key."
|
|
254
287
|
},
|
|
255
288
|
{
|
|
256
289
|
id: "pgp-private-key",
|
|
257
290
|
description: "PGP Private Key",
|
|
258
|
-
pattern: /-----BEGIN PGP PRIVATE KEY BLOCK
|
|
291
|
+
pattern: /-----BEGIN PGP PRIVATE KEY BLOCK-----/,
|
|
292
|
+
risk: "An attacker can impersonate you to sign malicious software releases or decrypt highly sensitive PGP-encrypted messages.",
|
|
293
|
+
solution: "Revoke this PGP key via your keyserver immediately, as it can be used to forge digital signatures or decrypt sensitive payloads."
|
|
259
294
|
}
|
|
260
295
|
];
|
|
261
296
|
privateKeyScanner = {
|
|
@@ -275,7 +310,8 @@ var init_privateKeys = __esm({
|
|
|
275
310
|
line: lineNumber,
|
|
276
311
|
match: cleanLine.trim().substring(0, 50) + "...",
|
|
277
312
|
severity: "high",
|
|
278
|
-
solution:
|
|
313
|
+
solution: rule.solution,
|
|
314
|
+
risk: rule.risk
|
|
279
315
|
});
|
|
280
316
|
}
|
|
281
317
|
}
|
|
@@ -330,7 +366,8 @@ var init_envFiles = __esm({
|
|
|
330
366
|
line: 0,
|
|
331
367
|
match: matchMsg,
|
|
332
368
|
severity,
|
|
333
|
-
solution: "
|
|
369
|
+
solution: `Run 'echo "${filename}" >> .gitignore' to safely ignore this file type moving forward. If it's meant to be a template, rename it to '${filename}.example'.`,
|
|
370
|
+
risk: "Contains highly sensitive environment variables, database passwords, or SSL certificates that grant instant system access if leaked."
|
|
334
371
|
});
|
|
335
372
|
}
|
|
336
373
|
return issues;
|
|
@@ -500,31 +537,55 @@ function error(message) {
|
|
|
500
537
|
console.error(import_picocolors.default.red("\u2716 error ") + message);
|
|
501
538
|
}
|
|
502
539
|
function printIssues(issues, showSolution = false) {
|
|
540
|
+
if (issues.length === 0) return;
|
|
503
541
|
console.error("\n" + import_picocolors.default.bold("Scan Results:"));
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
label = "IGNORED (DUMMY)";
|
|
514
|
-
colorFn = import_picocolors.default.dim;
|
|
515
|
-
}
|
|
516
|
-
let displayMatch = issue.match.replace(/\n/g, " ").trim();
|
|
517
|
-
if (displayMatch.length > 60) {
|
|
518
|
-
displayMatch = displayMatch.substring(0, 57) + "...";
|
|
542
|
+
const severityWeight = { high: 3, medium: 2, dummy: 1 };
|
|
543
|
+
const sortedIssues = [...issues].sort((a, b) => {
|
|
544
|
+
return (severityWeight[b.severity] || 0) - (severityWeight[a.severity] || 0);
|
|
545
|
+
});
|
|
546
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
547
|
+
for (const issue of sortedIssues) {
|
|
548
|
+
const key = `${issue.file}:::${issue.type}`;
|
|
549
|
+
if (!grouped.has(key)) {
|
|
550
|
+
grouped.set(key, []);
|
|
519
551
|
}
|
|
520
|
-
|
|
552
|
+
grouped.get(key).push(issue);
|
|
553
|
+
}
|
|
554
|
+
for (const [key, group] of grouped.entries()) {
|
|
555
|
+
const displayCount = Math.min(group.length, 3);
|
|
556
|
+
for (let i = 0; i < displayCount; i++) {
|
|
557
|
+
const issue = group[i];
|
|
558
|
+
let indicator = "\u25CF";
|
|
559
|
+
let label = "HIGH";
|
|
560
|
+
let colorFn = import_picocolors.default.red;
|
|
561
|
+
if (issue.severity === "medium") {
|
|
562
|
+
label = "MEDIUM";
|
|
563
|
+
colorFn = import_picocolors.default.yellow;
|
|
564
|
+
} else if (issue.severity === "dummy") {
|
|
565
|
+
indicator = "\u25CB";
|
|
566
|
+
label = "IGNORED (DUMMY)";
|
|
567
|
+
colorFn = import_picocolors.default.dim;
|
|
568
|
+
}
|
|
569
|
+
let displayMatch = issue.match.replace(/\n/g, " ").trim();
|
|
570
|
+
if (displayMatch.length > 60) {
|
|
571
|
+
displayMatch = displayMatch.substring(0, 57) + "...";
|
|
572
|
+
}
|
|
573
|
+
console.error(`
|
|
521
574
|
${colorFn(indicator)} ${colorFn(import_picocolors.default.bold(label))} ${import_picocolors.default.dim("\xB7")} ${issue.type}`);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
575
|
+
console.error(` ${import_picocolors.default.dim("File:")} ${issue.file}:${issue.line || "?"}`);
|
|
576
|
+
console.error(` ${import_picocolors.default.dim("Match:")} ${displayMatch}`);
|
|
577
|
+
if (issue.risk) {
|
|
578
|
+
console.error(` ${import_picocolors.default.dim("Risk:")} ${import_picocolors.default.yellow(issue.risk)}`);
|
|
579
|
+
}
|
|
580
|
+
if (showSolution && issue.solution) {
|
|
581
|
+
console.error(` ${import_picocolors.default.dim("Fix:")} ${import_picocolors.default.green(issue.solution)}`);
|
|
582
|
+
}
|
|
526
583
|
}
|
|
527
|
-
|
|
584
|
+
if (group.length > 3) {
|
|
585
|
+
console.error(`
|
|
586
|
+
${import_picocolors.default.cyan(`... and ${group.length - 3} more similar issues in ${group[0].file}`)}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
528
589
|
console.error();
|
|
529
590
|
}
|
|
530
591
|
var import_picocolors;
|
|
@@ -555,17 +616,40 @@ ${import_picocolors3.default.dim(" Scanning")} ${import_picocolors3.default.cya
|
|
|
555
616
|
console.log(`
|
|
556
617
|
${import_picocolors3.default.bold(`Found ${fileIssues.length} issue(s):`)}
|
|
557
618
|
`);
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
console.log(` ${indicator} ${severity} ${import_picocolors3.default.dim("\xB7")} ${issue.type}`);
|
|
562
|
-
console.log(` ${import_picocolors3.default.dim("Line:")} ${issue.line || "?"}`);
|
|
563
|
-
console.log(` ${import_picocolors3.default.dim("Match:")} ${issue.match.substring(0, 60)}`);
|
|
564
|
-
if (issue.solution) {
|
|
565
|
-
console.log(` ${import_picocolors3.default.dim("Fix:")} ${import_picocolors3.default.green(issue.solution)}`);
|
|
566
|
-
}
|
|
567
|
-
console.log();
|
|
619
|
+
const severityWeight = { high: 3, medium: 2, dummy: 1 };
|
|
620
|
+
const sortedIssues = [...fileIssues].sort((a, b) => {
|
|
621
|
+
return (severityWeight[b.severity] || 0) - (severityWeight[a.severity] || 0);
|
|
568
622
|
});
|
|
623
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
624
|
+
for (const issue of sortedIssues) {
|
|
625
|
+
if (!grouped.has(issue.type)) {
|
|
626
|
+
grouped.set(issue.type, []);
|
|
627
|
+
}
|
|
628
|
+
grouped.get(issue.type).push(issue);
|
|
629
|
+
}
|
|
630
|
+
for (const [type, group] of grouped.entries()) {
|
|
631
|
+
const displayCount = Math.min(group.length, 3);
|
|
632
|
+
for (let i = 0; i < displayCount; i++) {
|
|
633
|
+
const issue = group[i];
|
|
634
|
+
const severity = issue.severity === "high" ? import_picocolors3.default.red("HIGH") : issue.severity === "medium" ? import_picocolors3.default.yellow("MEDIUM") : import_picocolors3.default.dim("DUMMY");
|
|
635
|
+
const colorFn = issue.severity === "high" ? import_picocolors3.default.red : issue.severity === "medium" ? import_picocolors3.default.yellow : import_picocolors3.default.dim;
|
|
636
|
+
const indicator = issue.severity === "dummy" ? import_picocolors3.default.dim("\u25CB") : colorFn("\u25CF");
|
|
637
|
+
console.log(` ${indicator} ${severity} ${import_picocolors3.default.dim("\xB7")} ${issue.type}`);
|
|
638
|
+
console.log(` ${import_picocolors3.default.dim("Line:")} ${issue.line || "?"}`);
|
|
639
|
+
console.log(` ${import_picocolors3.default.dim("Match:")} ${issue.match.substring(0, 60)}`);
|
|
640
|
+
if (issue.risk) {
|
|
641
|
+
console.log(` ${import_picocolors3.default.dim("Risk:")} ${import_picocolors3.default.yellow(issue.risk)}`);
|
|
642
|
+
}
|
|
643
|
+
if (issue.solution) {
|
|
644
|
+
console.log(` ${import_picocolors3.default.dim("Fix:")} ${import_picocolors3.default.green(issue.solution)}`);
|
|
645
|
+
}
|
|
646
|
+
console.log();
|
|
647
|
+
}
|
|
648
|
+
if (group.length > 3) {
|
|
649
|
+
console.log(` ${import_picocolors3.default.cyan(`... and ${group.length - 3} more similar issues in this file.`)}
|
|
650
|
+
`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
569
653
|
}
|
|
570
654
|
}
|
|
571
655
|
function buildChoices(dirPath) {
|
|
@@ -727,7 +811,7 @@ var Spinner = class {
|
|
|
727
811
|
var fs3 = __toESM(require("fs"));
|
|
728
812
|
var path3 = __toESM(require("path"));
|
|
729
813
|
var program = new import_commander.Command();
|
|
730
|
-
program.name("git-cli-scanner").description("Interactive Git hooks vulnerability scanner").version("1.
|
|
814
|
+
program.name("git-cli-scanner").description("Interactive Git hooks vulnerability scanner").version("1.2.0");
|
|
731
815
|
program.command("init").description("Install pre-commit hook for automatic scanning").action(() => {
|
|
732
816
|
info("Setting up pre-commit hook...");
|
|
733
817
|
try {
|
|
@@ -779,16 +863,22 @@ program.command("disable").description("Remove the pre-commit hook and disable a
|
|
|
779
863
|
program.command("scan").description("Scan staged files for vulnerabilities").option("--show-sol", "Show solutions for vulnerabilities").option("--hook", "Internal flag used when running as a git hook").action(async (options) => {
|
|
780
864
|
info("Git CLI Scanner running...");
|
|
781
865
|
try {
|
|
782
|
-
const
|
|
866
|
+
const spinnerMsgs = options.hook ? [
|
|
783
867
|
"Scanning staged files for vulnerabilities...",
|
|
784
868
|
"Analyzing code patterns...",
|
|
785
869
|
"Checking for exposed API keys...",
|
|
870
|
+
"Thinking..."
|
|
871
|
+
] : [
|
|
872
|
+
"Scanning entire repository for vulnerabilities...",
|
|
873
|
+
"Analyzing code patterns...",
|
|
874
|
+
"Checking for exposed API keys...",
|
|
786
875
|
"Inspecting hidden files and directories...",
|
|
787
876
|
"Thinking..."
|
|
788
|
-
]
|
|
877
|
+
];
|
|
878
|
+
const spinner = new Spinner(spinnerMsgs);
|
|
789
879
|
spinner.start();
|
|
790
880
|
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
791
|
-
const issues = await scanDiff();
|
|
881
|
+
const issues = options.hook ? await scanDiff() : await scanDirectory(process.cwd());
|
|
792
882
|
if (issues.length > 0) {
|
|
793
883
|
const blockerIssues = issues.filter((i) => i.severity !== "dummy");
|
|
794
884
|
spinner.fail(`Found ${issues.length} potential vulnerabilities! (${blockerIssues.length} blockers)`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-cli-scanner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A powerful interactive CLI tool that scans your codebase for hardcoded secrets, API keys, passwords, and private keys before they reach your Git history.",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"scripts": {
|