sensitive-guard-cli 1.0.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/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # sensitive-guard
2
+
3
+ Prevent API keys, passwords, and secrets from being committed to git.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ # Run setup wizard in any git repo
9
+ npx sensitive-guard
10
+ ```
11
+
12
+ The wizard will:
13
+ 1. Show all 14 built-in detection rules
14
+ 2. Ask if you want to add custom terms (project names, usernames…)
15
+ 3. Install a `pre-commit` hook that runs on every `git commit`
16
+
17
+ ## Commands
18
+
19
+ | Command | Description |
20
+ |---|---|
21
+ | `npx sensitive-guard` | Interactive setup wizard |
22
+ | `npx sensitive-guard add <term>` | Add a custom sensitive term |
23
+ | `npx sensitive-guard list` | Show all rules and custom terms |
24
+ | `npx sensitive-guard status` | Check if hook is installed |
25
+ | `npx sensitive-guard remove` | Uninstall the hook |
26
+
27
+ ## What it blocks
28
+
29
+ | Rule | Pattern |
30
+ |---|---|
31
+ | Private Key | `-----BEGIN RSA PRIVATE KEY-----` |
32
+ | AWS Access Key | `AKIA...` (16-char) |
33
+ | AWS Secret Key | `aws_secret_key = "..."` |
34
+ | Anthropic Key | `sk-ant-...` |
35
+ | OpenAI Key | `sk-...` (32+ chars) |
36
+ | Google API Key | `AIza...` |
37
+ | GitHub Token | `ghp_...`, `gho_...`, `ghs_...` |
38
+ | Slack Token | `xoxb-...`, `xoxa-...` |
39
+ | JWT Token | `eyJ....eyJ....` |
40
+ | Generic API Key | `api_key = "..."` |
41
+ | Generic Token | `access_token = "..."` |
42
+ | Password | `password = "..."` |
43
+ | Secret | `client_secret = "..."` |
44
+ | Connection String | `mongodb://user:pass@host` |
45
+
46
+ ## Custom terms
47
+
48
+ Add project-specific terms to `.sensitive-terms` at your repo root:
49
+
50
+ ```
51
+ # .sensitive-terms
52
+ my-internal-project
53
+ baka3k
54
+ internal-hostname.corp
55
+ ```
56
+
57
+ This file is automatically added to `.gitignore` — it will never be committed.
58
+
59
+ ## Bypass
60
+
61
+ For cases where the detection fires incorrectly:
62
+
63
+ ```bash
64
+ git commit --no-verify
65
+ ```
66
+
67
+ ## Requirements
68
+
69
+ - Node.js ≥ 14
70
+ - A git repository with `.git/hooks/` directory
package/bin/cli.js ADDED
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const readline = require('readline');
7
+
8
+ const PATTERNS = require('../lib/patterns');
9
+ const HOOK_TEMPLATE = fs.readFileSync(path.join(__dirname, '../lib/hook.sh'), 'utf8');
10
+ const PKG = require('../package.json');
11
+
12
+ // ── ANSI helpers ─────────────────────────────────────────────────────────────
13
+
14
+ const A = {
15
+ reset: '\x1b[0m',
16
+ bold: '\x1b[1m',
17
+ dim: '\x1b[2m',
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ cyan: '\x1b[36m',
23
+ };
24
+
25
+ const bold = s => `${A.bold}${s}${A.reset}`;
26
+ const dim = s => `${A.dim}${s}${A.reset}`;
27
+ const red = s => `${A.red}${s}${A.reset}`;
28
+ const green = s => `${A.green}${s}${A.reset}`;
29
+ const yellow = s => `${A.yellow}${s}${A.reset}`;
30
+ const cyan = s => `${A.cyan}${s}${A.reset}`;
31
+
32
+ // ── git helpers ───────────────────────────────────────────────────────────────
33
+
34
+ function findGitRoot(start) {
35
+ let dir = start || process.cwd();
36
+ while (true) {
37
+ if (fs.existsSync(path.join(dir, '.git'))) return dir;
38
+ const parent = path.dirname(dir);
39
+ if (parent === dir) return null;
40
+ dir = parent;
41
+ }
42
+ }
43
+
44
+ function isHookInstalled(gitRoot) {
45
+ const p = path.join(gitRoot, '.git', 'hooks', 'pre-commit');
46
+ if (!fs.existsSync(p)) return false;
47
+ return fs.readFileSync(p, 'utf8').includes('sensitive-guard');
48
+ }
49
+
50
+ // ── display helpers ───────────────────────────────────────────────────────────
51
+
52
+ function printHeader() {
53
+ const v = `v${PKG.version}`;
54
+ const title = ` sensitive-guard ${v} `;
55
+ const border = '='.repeat(title.length);
56
+ console.log('');
57
+ console.log(bold(cyan(` ${border}`)));
58
+ console.log(bold(cyan(` ${title}`)));
59
+ console.log(bold(cyan(` ${border}`)));
60
+ console.log(dim(' Prevent sensitive data from being committed to git'));
61
+ console.log('');
62
+ }
63
+
64
+ function printPatterns() {
65
+ console.log(bold(` Built-in detection rules (${PATTERNS.length}):`));
66
+ console.log('');
67
+
68
+ const W = Math.max(...PATTERNS.map(p => p.label.length));
69
+
70
+ for (const p of PATTERNS) {
71
+ const badge = p.type === 'icase'
72
+ ? dim('[i]') // case-insensitive
73
+ : dim('[E]'); // exact / format
74
+
75
+ console.log(
76
+ ` ${green('+')} ${bold(p.label.padEnd(W))} ${badge} ${dim(p.description)}`
77
+ );
78
+ console.log(
79
+ ` ${''.padEnd(W + 4)} ${dim('e.g. ' + p.example)}`
80
+ );
81
+ }
82
+ console.log('');
83
+ console.log(dim(' [E] = exact format match [i] = case-insensitive keyword assignment'));
84
+ console.log('');
85
+ }
86
+
87
+ // ── readline prompt ───────────────────────────────────────────────────────────
88
+
89
+ function ask(rl, question) {
90
+ return new Promise(resolve => rl.question(question, a => resolve(a.trim())));
91
+ }
92
+
93
+ // ── file helpers ──────────────────────────────────────────────────────────────
94
+
95
+ function readCustomTerms(gitRoot) {
96
+ const p = path.join(gitRoot, '.sensitive-terms');
97
+ if (!fs.existsSync(p)) return [];
98
+ return fs.readFileSync(p, 'utf8')
99
+ .split('\n')
100
+ .map(l => l.trim())
101
+ .filter(l => l && !l.startsWith('#'));
102
+ }
103
+
104
+ function appendTerms(gitRoot, newTerms) {
105
+ if (!newTerms.length) return 0;
106
+ const p = path.join(gitRoot, '.sensitive-terms');
107
+ const existing = readCustomTerms(gitRoot);
108
+ const toAdd = newTerms.filter(t => !existing.includes(t));
109
+ if (!toAdd.length) return 0;
110
+
111
+ const header = fs.existsSync(p) ? '' : [
112
+ '# .sensitive-terms — managed by sensitive-guard',
113
+ '# Each line is a term blocked at commit time (case-insensitive, fixed string).',
114
+ '# Lines starting with # are comments. Blank lines are ignored.',
115
+ '#',
116
+ '# Add project-specific terms below:',
117
+ '# - internal project names',
118
+ '# - usernames / handles',
119
+ '# - internal hostnames or IP ranges',
120
+ '# - any other string that must never appear in commits',
121
+ '',
122
+ ].join('\n');
123
+
124
+ fs.appendFileSync(p, header + toAdd.join('\n') + '\n');
125
+ return toAdd.length;
126
+ }
127
+
128
+ function ensureGitignore(gitRoot) {
129
+ const p = path.join(gitRoot, '.gitignore');
130
+ const entry = '.sensitive-terms';
131
+ if (fs.existsSync(p) && fs.readFileSync(p, 'utf8').includes(entry)) return;
132
+ fs.appendFileSync(p, `\n# sensitive-guard\n${entry}\n`);
133
+ console.log(` ${green('+')} ${entry} added to .gitignore`);
134
+ }
135
+
136
+ function installHook(gitRoot) {
137
+ const hooksDir = path.join(gitRoot, '.git', 'hooks');
138
+ const hookPath = path.join(hooksDir, 'pre-commit');
139
+
140
+ if (fs.existsSync(hookPath)) {
141
+ const content = fs.readFileSync(hookPath, 'utf8');
142
+ if (!content.includes('sensitive-guard')) {
143
+ const backup = `${hookPath}.bak.${Date.now()}`;
144
+ fs.copyFileSync(hookPath, backup);
145
+ console.log(` ${yellow('!')} Existing hook backed up to ${dim(path.basename(backup))}`);
146
+ }
147
+ }
148
+
149
+ fs.writeFileSync(hookPath, HOOK_TEMPLATE, { mode: 0o755 });
150
+ console.log(` ${green('+')} Hook installed → ${dim('.git/hooks/pre-commit')}`);
151
+ }
152
+
153
+ // ── commands ──────────────────────────────────────────────────────────────────
154
+
155
+ async function cmdInit() {
156
+ printHeader();
157
+
158
+ const gitRoot = findGitRoot();
159
+ if (!gitRoot) {
160
+ console.log(red(' Error: not inside a git repository.'));
161
+ console.log(dim(' Run this command from inside a git project.'));
162
+ process.exit(1);
163
+ }
164
+ console.log(` Repository: ${dim(gitRoot)}`);
165
+ console.log('');
166
+
167
+ // Already installed?
168
+ if (isHookInstalled(gitRoot)) {
169
+ console.log(` ${yellow('!')} sensitive-guard hook is already installed.`);
170
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
171
+ const ans = await ask(rl, ` ${bold('Reinstall / reconfigure? (y/N)')} `);
172
+ rl.close();
173
+ console.log('');
174
+ if (ans.toLowerCase() !== 'y') {
175
+ console.log(dim(' Aborted. Run `sensitive-guard list` to see active config.'));
176
+ console.log('');
177
+ return;
178
+ }
179
+ }
180
+
181
+ // Show all patterns
182
+ printPatterns();
183
+
184
+ // Collect custom terms
185
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
186
+ const wantCustom = await ask(rl, ` ${bold('Add custom sensitive terms? (y/N)')} `);
187
+ console.log('');
188
+
189
+ let newTerms = [];
190
+ if (wantCustom.toLowerCase() === 'y') {
191
+ console.log(bold(' Enter terms to block (project names, usernames, hostnames…)'));
192
+ console.log(dim(' One per line. Empty line when done.'));
193
+ console.log('');
194
+
195
+ let i = 1;
196
+ while (true) {
197
+ const t = await ask(rl, ` ${yellow('Term ' + i)} `);
198
+ if (!t) break;
199
+ newTerms.push(t);
200
+ console.log(` ${green('+')} Queued: ${bold(t)}`);
201
+ i++;
202
+ }
203
+ console.log('');
204
+ }
205
+ rl.close();
206
+
207
+ // Install
208
+ console.log(bold(' Installing…'));
209
+ installHook(gitRoot);
210
+
211
+ const added = appendTerms(gitRoot, newTerms);
212
+ if (added) console.log(` ${green('+')} ${added} term(s) written to .sensitive-terms`);
213
+
214
+ ensureGitignore(gitRoot);
215
+
216
+ console.log('');
217
+ console.log(bold(green(' sensitive-guard is now active!')));
218
+ console.log('');
219
+ console.log(` ${bold('What happens next:')}`);
220
+ console.log(` • Every ${yellow('git commit')} will be scanned automatically`);
221
+ console.log(` • Edit ${yellow('.sensitive-terms')} to add/remove custom terms`);
222
+ console.log(` • Run ${yellow('npx sensitive-guard list')} to review all rules`);
223
+ console.log(` • Run ${yellow('npx sensitive-guard add <term>')} to add a term`);
224
+ console.log(` • Run ${yellow('npx sensitive-guard remove')} to uninstall`);
225
+ console.log(` • ${dim('Emergency bypass:')} ${yellow('git commit --no-verify')}`);
226
+ console.log('');
227
+ }
228
+
229
+ function cmdList() {
230
+ printHeader();
231
+
232
+ const gitRoot = findGitRoot();
233
+ const installed = gitRoot && isHookInstalled(gitRoot);
234
+
235
+ console.log(` Status : ${installed ? green('active') : red('not installed')}`);
236
+ if (gitRoot) console.log(` Repository : ${dim(gitRoot)}`);
237
+ console.log('');
238
+
239
+ printPatterns();
240
+
241
+ if (gitRoot) {
242
+ const terms = readCustomTerms(gitRoot);
243
+ if (terms.length) {
244
+ console.log(bold(` Custom terms in .sensitive-terms (${terms.length}):`));
245
+ console.log('');
246
+ for (const t of terms) console.log(` ${yellow('•')} ${t}`);
247
+ console.log('');
248
+ console.log(dim(` Edit ${path.join(gitRoot, '.sensitive-terms')} to change these.`));
249
+ } else {
250
+ console.log(dim(' No custom terms configured.'));
251
+ console.log(dim(' Run `sensitive-guard add <term>` or re-run `sensitive-guard init`.'));
252
+ }
253
+ console.log('');
254
+ }
255
+ }
256
+
257
+ function cmdAdd(term) {
258
+ if (!term) {
259
+ console.log(red(' Usage: sensitive-guard add <term>'));
260
+ process.exit(1);
261
+ }
262
+ const gitRoot = findGitRoot();
263
+ if (!gitRoot) { console.log(red(' Error: not inside a git repository.')); process.exit(1); }
264
+
265
+ const added = appendTerms(gitRoot, [term]);
266
+ ensureGitignore(gitRoot);
267
+
268
+ if (added) {
269
+ console.log(` ${green('+')} "${bold(term)}" added to .sensitive-terms`);
270
+ } else {
271
+ console.log(` ${yellow('!')} "${term}" is already in .sensitive-terms`);
272
+ }
273
+ }
274
+
275
+ function cmdStatus() {
276
+ const gitRoot = findGitRoot();
277
+ if (!gitRoot) { console.log(red(' Error: not inside a git repository.')); process.exit(1); }
278
+
279
+ const installed = isHookInstalled(gitRoot);
280
+ const terms = readCustomTerms(gitRoot);
281
+
282
+ console.log('');
283
+ console.log(` Repository : ${dim(gitRoot)}`);
284
+ console.log(` Hook : ${installed ? green('installed') : red('not installed')}`);
285
+ console.log(` Custom terms : ${terms.length ? yellow(terms.length + ' term(s)') : dim('none')}`);
286
+ console.log('');
287
+
288
+ if (!installed) {
289
+ console.log(dim(' Run `npx sensitive-guard init` to install.'));
290
+ console.log('');
291
+ }
292
+ }
293
+
294
+ function cmdRemove() {
295
+ const gitRoot = findGitRoot();
296
+ if (!gitRoot) { console.log(red(' Error: not inside a git repository.')); process.exit(1); }
297
+
298
+ const hookPath = path.join(gitRoot, '.git', 'hooks', 'pre-commit');
299
+ if (!fs.existsSync(hookPath) || !isHookInstalled(gitRoot)) {
300
+ console.log(yellow(' Hook is not installed.'));
301
+ return;
302
+ }
303
+ fs.unlinkSync(hookPath);
304
+ console.log(` ${green('+')} Hook removed.`);
305
+ console.log(dim(' .sensitive-terms file is preserved.'));
306
+ }
307
+
308
+ function cmdHelp() {
309
+ printHeader();
310
+ console.log(` ${bold('Usage:')} sensitive-guard [command]`);
311
+ console.log('');
312
+ console.log(` ${bold('Commands:')}`);
313
+ console.log(` ${yellow('init')} Interactive setup wizard (default)`);
314
+ console.log(` ${yellow('add')} <term> Add a custom sensitive term`);
315
+ console.log(` ${yellow('list')} Show all built-in rules and custom terms`);
316
+ console.log(` ${yellow('status')} Check if hook is installed`);
317
+ console.log(` ${yellow('remove')} Uninstall the hook`);
318
+ console.log(` ${yellow('help')} Show this help`);
319
+ console.log('');
320
+ console.log(` ${bold('Examples:')}`);
321
+ console.log(` npx sensitive-guard`);
322
+ console.log(` npx sensitive-guard add my-project-name`);
323
+ console.log(` npx sensitive-guard add hieplq1`);
324
+ console.log(` npx sensitive-guard list`);
325
+ console.log('');
326
+ }
327
+
328
+ // ── entry point ───────────────────────────────────────────────────────────────
329
+
330
+ const [,, cmd, ...args] = process.argv;
331
+
332
+ switch (cmd) {
333
+ case undefined:
334
+ case 'init':
335
+ cmdInit().catch(e => { console.error(red(' Error: ' + e.message)); process.exit(1); });
336
+ break;
337
+ case 'add':
338
+ cmdAdd(args[0]);
339
+ break;
340
+ case 'list':
341
+ cmdList();
342
+ break;
343
+ case 'status':
344
+ cmdStatus();
345
+ break;
346
+ case 'remove':
347
+ case 'uninstall':
348
+ cmdRemove();
349
+ break;
350
+ case 'help':
351
+ case '--help':
352
+ case '-h':
353
+ cmdHelp();
354
+ break;
355
+ default:
356
+ console.log(red(` Unknown command: "${cmd}"`));
357
+ cmdHelp();
358
+ process.exit(1);
359
+ }
package/lib/hook.sh ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # sensitive-guard pre-commit hook
3
+ # Managed by sensitive-guard — https://npm.im/sensitive-guard
4
+ # Do not edit this file manually; run: npx sensitive-guard init
5
+
6
+ RED='\033[0;31m'
7
+ YELLOW='\033[1;33m'
8
+ GREEN='\033[0;32m'
9
+ NC='\033[0m'
10
+
11
+ REPO_ROOT=$(git rev-parse --show-toplevel)
12
+ SENSITIVE_TERMS_FILE="$REPO_ROOT/.sensitive-terms"
13
+ FOUND=0
14
+
15
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=d)
16
+ [ -z "$STAGED_FILES" ] && exit 0
17
+
18
+ echo ""
19
+
20
+ # ── helpers ──────────────────────────────────────────────────────────────────
21
+
22
+ _check() {
23
+ local flag="$1" label="$2" pattern="$3" file="$4"
24
+ local matches
25
+ matches=$(git diff --cached -U0 "$file" 2>/dev/null \
26
+ | grep "^+" | grep -v "^+++" \
27
+ | grep "$flag" "$pattern" 2>/dev/null)
28
+ [ -z "$matches" ] && return
29
+ echo -e "${RED}[BLOCKED] ${label} in: ${YELLOW}${file}${NC}"
30
+ echo "$matches" | head -3 | sed 's/^/ /'
31
+ echo ""
32
+ FOUND=1
33
+ }
34
+
35
+ check_exact() { _check "-E" "$1" "$2" "$3"; }
36
+ check_icase() { _check "-iE" "$1" "$2" "$3"; }
37
+
38
+ # ── built-in patterns ────────────────────────────────────────────────────────
39
+
40
+ for file in $STAGED_FILES; do
41
+
42
+ # --- exact / format-based ---
43
+ check_exact "Private Key" "-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY" "$file"
44
+ check_exact "AWS Access Key" "AKIA[0-9A-Z]{16}" "$file"
45
+ check_exact "Anthropic Key" "sk-ant-[A-Za-z0-9_-]{20,}" "$file"
46
+ check_exact "OpenAI Key" "sk-[A-Za-z0-9]{32,}" "$file"
47
+ check_exact "Google API Key" "AIza[0-9A-Za-z_-]{35}" "$file"
48
+ check_exact "GitHub Token" "gh[pousr]_[A-Za-z0-9]{36,}" "$file"
49
+ check_exact "Slack Token" "xox[baprs]-[A-Za-z0-9-]{10,}" "$file"
50
+ check_exact "JWT Token" "eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+" "$file"
51
+
52
+ # --- case-insensitive assignment patterns ---
53
+ SEP="[[:space:]]*[=:][[:space:]]*['\"]?"
54
+ check_icase "AWS Secret Key" "(aws_secret_access_key|aws_secret_key)${SEP}[A-Za-z0-9/+=]{40}" "$file"
55
+ check_icase "API Key" "(api_key|apikey|api-key)${SEP}[A-Za-z0-9_-]{20,}" "$file"
56
+ check_icase "Token" "(access_token|auth_token|secret_token|bearer_token)${SEP}[A-Za-z0-9_.-]{20,}" "$file"
57
+ check_icase "Password" "(password|passwd|pwd)${SEP}.{8,}" "$file"
58
+ check_icase "Secret" "(client_secret|app_secret)${SEP}[A-Za-z0-9_-]{10,}" "$file"
59
+ check_icase "Connection String" "(mongodb|postgresql|mysql|redis)://[^@[:space:]]+:[^@[:space:]]+@" "$file"
60
+
61
+ done
62
+
63
+ # ── custom terms from .sensitive-terms ───────────────────────────────────────
64
+
65
+ if [ -f "$SENSITIVE_TERMS_FILE" ]; then
66
+ while IFS= read -r term || [ -n "$term" ]; do
67
+ [[ -z "$term" || "$term" == \#* ]] && continue
68
+ for file in $STAGED_FILES; do
69
+ matches=$(git diff --cached -U0 "$file" 2>/dev/null \
70
+ | grep "^+" | grep -v "^+++" \
71
+ | grep -iF "$term" 2>/dev/null)
72
+ [ -z "$matches" ] && continue
73
+ echo -e "${RED}[BLOCKED] Custom term '${YELLOW}${term}${RED}' in: ${YELLOW}${file}${NC}"
74
+ echo "$matches" | head -3 | sed 's/^/ /'
75
+ echo ""
76
+ FOUND=1
77
+ done
78
+ done < "$SENSITIVE_TERMS_FILE"
79
+ fi
80
+
81
+ # ── result ───────────────────────────────────────────────────────────────────
82
+
83
+ if [ "$FOUND" -eq 1 ]; then
84
+ echo -e "${RED}Commit blocked: sensitive information detected.${NC}"
85
+ echo -e " Edit ${YELLOW}.sensitive-terms${NC} to manage custom blocked terms."
86
+ echo -e " Run ${YELLOW}npx sensitive-guard list${NC} to see all active rules."
87
+ echo -e " Use ${YELLOW}git commit --no-verify${NC} to bypass (use with care)."
88
+ echo ""
89
+ exit 1
90
+ fi
91
+
92
+ echo -e "${GREEN}[OK] No sensitive information detected.${NC}"
93
+ exit 0
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ // Display metadata for built-in patterns (mirrors the regexes in lib/hook.sh)
4
+ module.exports = [
5
+ {
6
+ label: 'Private Key',
7
+ description: 'SSH / TLS private keys',
8
+ example: '-----BEGIN RSA PRIVATE KEY-----',
9
+ type: 'exact',
10
+ },
11
+ {
12
+ label: 'AWS Access Key',
13
+ description: 'AWS IAM access key IDs',
14
+ example: 'AKIA4EXAMPLE...',
15
+ type: 'exact',
16
+ },
17
+ {
18
+ label: 'AWS Secret Key',
19
+ description: 'AWS secret access key assignments',
20
+ example: 'aws_secret_key = "wJalrXUt..."',
21
+ type: 'icase',
22
+ },
23
+ {
24
+ label: 'Anthropic Key',
25
+ description: 'Claude / Anthropic API keys',
26
+ example: 'sk-ant-api03-...',
27
+ type: 'exact',
28
+ },
29
+ {
30
+ label: 'OpenAI Key',
31
+ description: 'OpenAI API keys',
32
+ example: 'sk-proj-...',
33
+ type: 'exact',
34
+ },
35
+ {
36
+ label: 'Google API Key',
37
+ description: 'Google Cloud / Firebase API keys',
38
+ example: 'AIzaSyC...',
39
+ type: 'exact',
40
+ },
41
+ {
42
+ label: 'GitHub Token',
43
+ description: 'GitHub personal / OAuth / Actions tokens',
44
+ example: 'ghp_16C7e42F..., ghs_...',
45
+ type: 'exact',
46
+ },
47
+ {
48
+ label: 'Slack Token',
49
+ description: 'Slack Bot / App / User tokens',
50
+ example: 'xoxb-..., xoxa-...',
51
+ type: 'exact',
52
+ },
53
+ {
54
+ label: 'JWT Token',
55
+ description: 'JSON Web Tokens (3-part dot-separated)',
56
+ example: 'eyJhbGci....eyJzdWI....',
57
+ type: 'exact',
58
+ },
59
+ {
60
+ label: 'Generic API Key',
61
+ description: 'Any api_key / apikey / api-key assignment',
62
+ example: 'api_key = "abc123..."',
63
+ type: 'icase',
64
+ },
65
+ {
66
+ label: 'Generic Token',
67
+ description: 'access_token, auth_token, bearer_token assignments',
68
+ example: 'access_token = "xyz789..."',
69
+ type: 'icase',
70
+ },
71
+ {
72
+ label: 'Password',
73
+ description: 'password / passwd / pwd assignments',
74
+ example: 'password = "s3cr3t!"',
75
+ type: 'icase',
76
+ },
77
+ {
78
+ label: 'Secret',
79
+ description: 'client_secret / app_secret assignments',
80
+ example: 'client_secret = "abc..."',
81
+ type: 'icase',
82
+ },
83
+ {
84
+ label: 'Connection String',
85
+ description: 'Database URLs with embedded credentials',
86
+ example: 'mongodb://user:pass@host/db',
87
+ type: 'icase',
88
+ },
89
+ ];
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "sensitive-guard-cli",
3
+ "version": "1.0.0",
4
+ "description": "Prevent API keys, passwords, and secrets from being committed to git",
5
+ "bin": {
6
+ "sensitive-guard": "./bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "lib/"
11
+ ],
12
+ "engines": {
13
+ "node": ">=14"
14
+ },
15
+ "keywords": [
16
+ "git",
17
+ "security",
18
+ "secrets",
19
+ "pre-commit",
20
+ "hook",
21
+ "api-key",
22
+ "password"
23
+ ],
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/baka3k/sensitive-guard.git"
28
+ }
29
+ }