git-cli-scanner 1.2.1 → 1.3.1
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 +122 -2
- package/package.json +1 -1
- package/readme.md +17 -1
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
33
|
|
|
34
34
|
// src/utils/git.ts
|
|
35
|
+
var git_exports = {};
|
|
36
|
+
__export(git_exports, {
|
|
37
|
+
getHistoryDiffs: () => getHistoryDiffs,
|
|
38
|
+
getStagedDiff: () => getStagedDiff
|
|
39
|
+
});
|
|
35
40
|
async function getStagedDiff() {
|
|
36
41
|
try {
|
|
37
42
|
const { stdout: filesOutput } = await execAsync("git diff --cached --name-only");
|
|
@@ -50,6 +55,64 @@ async function getStagedDiff() {
|
|
|
50
55
|
return [];
|
|
51
56
|
}
|
|
52
57
|
}
|
|
58
|
+
async function getHistoryDiffs(options) {
|
|
59
|
+
try {
|
|
60
|
+
let cmd = 'git log -p --pretty=format:"---COMMIT:%H---"';
|
|
61
|
+
if (options.id) {
|
|
62
|
+
cmd += ` -1 ${options.id}`;
|
|
63
|
+
} else {
|
|
64
|
+
if (options.all) cmd += " --all";
|
|
65
|
+
if (options.since) cmd += ` --since="${options.since}"`;
|
|
66
|
+
if (!options.all && !options.since) {
|
|
67
|
+
cmd += " -1";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const { stdout } = await execAsync(cmd, { maxBuffer: 1024 * 1024 * 50 });
|
|
71
|
+
if (!stdout.trim()) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const commits = [];
|
|
75
|
+
let currentCommit = null;
|
|
76
|
+
let currentFile = null;
|
|
77
|
+
let currentDiff = "";
|
|
78
|
+
const lines = stdout.split("\n");
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.startsWith("---COMMIT:")) {
|
|
81
|
+
if (currentFile && currentCommit) {
|
|
82
|
+
currentCommit.diffs.push({ file: currentFile, content: currentDiff });
|
|
83
|
+
}
|
|
84
|
+
currentFile = null;
|
|
85
|
+
currentDiff = "";
|
|
86
|
+
const match = line.match(/---COMMIT:([a-f0-9]+)---/);
|
|
87
|
+
if (match) {
|
|
88
|
+
currentCommit = { commitHash: match[1], diffs: [] };
|
|
89
|
+
commits.push(currentCommit);
|
|
90
|
+
}
|
|
91
|
+
} else if (line.startsWith("diff --git a/")) {
|
|
92
|
+
if (currentFile && currentCommit) {
|
|
93
|
+
currentCommit.diffs.push({ file: currentFile, content: currentDiff });
|
|
94
|
+
}
|
|
95
|
+
const parts = line.substring(13).split(" b/");
|
|
96
|
+
if (parts.length > 0) {
|
|
97
|
+
currentFile = parts[0];
|
|
98
|
+
}
|
|
99
|
+
currentDiff = "";
|
|
100
|
+
} else if (currentFile) {
|
|
101
|
+
currentDiff += line + "\n";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (currentFile && currentCommit) {
|
|
105
|
+
currentCommit.diffs.push({ file: currentFile, content: currentDiff });
|
|
106
|
+
}
|
|
107
|
+
return commits;
|
|
108
|
+
} catch (error2) {
|
|
109
|
+
if (error2.message && error2.message.includes("unknown revision")) {
|
|
110
|
+
throw new Error(`Invalid commit hash: ${options.id}`);
|
|
111
|
+
}
|
|
112
|
+
console.warn("Could not retrieve git history diffs. Are you in a git repository?");
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
53
116
|
var import_child_process, import_util, execAsync;
|
|
54
117
|
var init_git = __esm({
|
|
55
118
|
"src/utils/git.ts"() {
|
|
@@ -477,7 +540,8 @@ var init_dummy = __esm({
|
|
|
477
540
|
var scanner_exports = {};
|
|
478
541
|
__export(scanner_exports, {
|
|
479
542
|
scanDiff: () => scanDiff,
|
|
480
|
-
scanDirectory: () => scanDirectory
|
|
543
|
+
scanDirectory: () => scanDirectory,
|
|
544
|
+
scanHistoryDiffs: () => scanHistoryDiffs
|
|
481
545
|
});
|
|
482
546
|
async function scanDiff() {
|
|
483
547
|
const diffs = await getStagedDiff();
|
|
@@ -509,6 +573,28 @@ async function scanDirectory(dirPath) {
|
|
|
509
573
|
}
|
|
510
574
|
return issues;
|
|
511
575
|
}
|
|
576
|
+
async function scanHistoryDiffs(options) {
|
|
577
|
+
const { getHistoryDiffs: getHistoryDiffs2 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
578
|
+
const commitDiffs = await getHistoryDiffs2(options);
|
|
579
|
+
const issues = [];
|
|
580
|
+
for (const commitDiff of commitDiffs) {
|
|
581
|
+
for (const diff of commitDiff.diffs) {
|
|
582
|
+
for (const scanner of allScanners) {
|
|
583
|
+
const scannerIssues = scanner.scan(diff);
|
|
584
|
+
for (const issue of scannerIssues) {
|
|
585
|
+
issue.commitHash = commitDiff.commitHash;
|
|
586
|
+
issues.push(issue);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
for (const issue of issues) {
|
|
592
|
+
if (isDummySecret(issue.match)) {
|
|
593
|
+
issue.severity = "dummy";
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return issues;
|
|
597
|
+
}
|
|
512
598
|
var init_scanner = __esm({
|
|
513
599
|
"src/scanner/index.ts"() {
|
|
514
600
|
"use strict";
|
|
@@ -573,6 +659,9 @@ function printIssues(issues, showSolution = false) {
|
|
|
573
659
|
console.error(`
|
|
574
660
|
${colorFn(indicator)} ${colorFn(import_picocolors.default.bold(label))} ${import_picocolors.default.dim("\xB7")} ${issue.type}`);
|
|
575
661
|
console.error(` ${import_picocolors.default.dim("File:")} ${issue.file}:${issue.line || "?"}`);
|
|
662
|
+
if (issue.commitHash) {
|
|
663
|
+
console.error(` ${import_picocolors.default.dim("Commit:")} ${import_picocolors.default.cyan(issue.commitHash)}`);
|
|
664
|
+
}
|
|
576
665
|
console.error(` ${import_picocolors.default.dim("Match:")} ${displayMatch}`);
|
|
577
666
|
if (issue.risk) {
|
|
578
667
|
console.error(` ${import_picocolors.default.dim("Risk:")} ${import_picocolors.default.yellow(issue.risk)}`);
|
|
@@ -812,7 +901,7 @@ var import_child_process2 = require("child_process");
|
|
|
812
901
|
var fs3 = __toESM(require("fs"));
|
|
813
902
|
var path3 = __toESM(require("path"));
|
|
814
903
|
var program = new import_commander.Command();
|
|
815
|
-
program.name("git-cli-scanner").description("Interactive Git hooks vulnerability scanner").version("1.
|
|
904
|
+
program.name("git-cli-scanner").description("Interactive Git hooks vulnerability scanner").version("1.3.1");
|
|
816
905
|
program.command("init").description("Install pre-commit hook for automatic scanning").action(() => {
|
|
817
906
|
info("Setting up pre-commit hook...");
|
|
818
907
|
try {
|
|
@@ -948,6 +1037,37 @@ program.command("scan-all [dir]").description("Scan an entire directory or codeb
|
|
|
948
1037
|
process.exit(1);
|
|
949
1038
|
}
|
|
950
1039
|
});
|
|
1040
|
+
program.command("scan-history").description("Scan Git commit history for exposed secrets (defaults to last commit)").option("--show-sol", "Show solutions for vulnerabilities").option("--since <date>", 'Scan commits since a specific date (e.g. "30 days ago")').option("--id <hash>", "Scan a specific commit hash").option("--all", "Scan all branches and history").action(async (options) => {
|
|
1041
|
+
info("Git CLI Scanner (History Mode) running...");
|
|
1042
|
+
try {
|
|
1043
|
+
const spinnerMsgs = [
|
|
1044
|
+
"Rewinding Git history...",
|
|
1045
|
+
"Extracting historical diffs...",
|
|
1046
|
+
"Scanning temporal anomalies...",
|
|
1047
|
+
"Looking for buried secrets...",
|
|
1048
|
+
"Thinking..."
|
|
1049
|
+
];
|
|
1050
|
+
const spinner = new Spinner(spinnerMsgs);
|
|
1051
|
+
spinner.start();
|
|
1052
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
1053
|
+
const issues = await scanHistoryDiffs({
|
|
1054
|
+
since: options.since,
|
|
1055
|
+
id: options.id,
|
|
1056
|
+
all: options.all
|
|
1057
|
+
});
|
|
1058
|
+
if (issues.length > 0) {
|
|
1059
|
+
const blockerIssues = issues.filter((i) => i.severity !== "dummy");
|
|
1060
|
+
spinner.fail(`Found ${issues.length} historical vulnerabilities! (${blockerIssues.length} blockers)`);
|
|
1061
|
+
const { printIssues: printIssues2 } = (init_logger(), __toCommonJS(logger_exports));
|
|
1062
|
+
printIssues2(issues, options.showSol);
|
|
1063
|
+
} else {
|
|
1064
|
+
spinner.stop("No vulnerabilities found in the scanned history!");
|
|
1065
|
+
}
|
|
1066
|
+
} catch (err) {
|
|
1067
|
+
error(`History scan failed: ${err.message}`);
|
|
1068
|
+
process.exit(1);
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
951
1071
|
program.command("explore").description("Launch an interactive Terminal UI to browse and scan files").action(async () => {
|
|
952
1072
|
try {
|
|
953
1073
|
const { runExplorer: runExplorer2 } = (init_explorer(), __toCommonJS(explorer_exports));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-cli-scanner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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": {
|
package/readme.md
CHANGED
|
@@ -39,7 +39,7 @@ After running `init`, every time you run `git commit`, the scanner will automati
|
|
|
39
39
|
| `scan` | Manually scan staged files |
|
|
40
40
|
| `scan --show-sol` | Scan staged files and show suggested fixes |
|
|
41
41
|
| `scan-all [dir]` | Scan an entire directory recursively |
|
|
42
|
-
| `scan-
|
|
42
|
+
| `scan-history` | Scan Git commit history for leaked secrets |
|
|
43
43
|
| `explore` | Launch interactive file explorer TUI |
|
|
44
44
|
|
|
45
45
|
### Enable automatic scanning
|
|
@@ -73,6 +73,22 @@ npx git-cli-scanner scan-all ./src
|
|
|
73
73
|
npx git-cli-scanner scan-all tests --show-sol
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### Scan Git history (Time Travel)
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Scan the very last commit (default)
|
|
80
|
+
npx git-cli-scanner scan-history
|
|
81
|
+
|
|
82
|
+
# Scan a specific commit by hash
|
|
83
|
+
npx git-cli-scanner scan-history --id <hash>
|
|
84
|
+
|
|
85
|
+
# Scan all commits in the last 30 days
|
|
86
|
+
npx git-cli-scanner scan-history --since="30 days ago"
|
|
87
|
+
|
|
88
|
+
# Scan the entire Git history across all branches!
|
|
89
|
+
npx git-cli-scanner scan-history --all
|
|
90
|
+
```
|
|
91
|
+
|
|
76
92
|
### Interactive file explorer
|
|
77
93
|
|
|
78
94
|
```bash
|