api-key-guard 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/.env.example +7 -0
- package/README.md +61 -0
- package/TESTING.md +111 -0
- package/USAGE.md +126 -0
- package/bin/cli.js +62 -0
- package/config/patterns.json +49 -0
- package/package.json +31 -0
- package/src/gitHooks.js +61 -0
- package/src/index.js +9 -0
- package/src/readmeGenerator.js +221 -0
- package/src/scanner.js +143 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# API Key Guard
|
|
2
|
+
|
|
3
|
+
A comprehensive CLI tool for detecting, preventing, and managing API key leaks in your codebase with AI-powered README generation.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install the package
|
|
9
|
+
npm install -g api-key-guard
|
|
10
|
+
|
|
11
|
+
# Scan for API key leaks
|
|
12
|
+
api-key-guard scan
|
|
13
|
+
|
|
14
|
+
# Setup git hooks
|
|
15
|
+
api-key-guard setup-hooks
|
|
16
|
+
|
|
17
|
+
# Generate AI-powered README (requires GEMINI_API_KEY)
|
|
18
|
+
export GEMINI_API_KEY=your_gemini_api_key_here
|
|
19
|
+
api-key-guard readme
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- š **API Key Detection**: Scan your codebase for potential API key leaks
|
|
25
|
+
- š **Git Hooks Integration**: Automatically check for leaks before commits
|
|
26
|
+
- š¤ **AI-Powered README**: Generate professional README files using Google's Gemini API
|
|
27
|
+
- āļø **Configurable**: Customize scanning patterns and ignore rules
|
|
28
|
+
- š”ļø **Security First**: Built with security best practices in mind
|
|
29
|
+
|
|
30
|
+
## CLI Commands
|
|
31
|
+
|
|
32
|
+
### `api-key-guard scan`
|
|
33
|
+
Scan files for potential API key leaks.
|
|
34
|
+
|
|
35
|
+
Options:
|
|
36
|
+
- `-p, --path <path>`: Path to scan (default: current directory)
|
|
37
|
+
- `-v, --verbose`: Show detailed output
|
|
38
|
+
|
|
39
|
+
### `api-key-guard setup-hooks`
|
|
40
|
+
Setup git hooks for automatic API key detection before commits.
|
|
41
|
+
|
|
42
|
+
### `api-key-guard readme`
|
|
43
|
+
Generate an AI-powered README.md file using Gemini API.
|
|
44
|
+
|
|
45
|
+
Options:
|
|
46
|
+
- `-f, --force`: Overwrite existing README.md without confirmation
|
|
47
|
+
- `-o, --output <file>`: Output file path (default: README.md)
|
|
48
|
+
|
|
49
|
+
## Environment Variables
|
|
50
|
+
|
|
51
|
+
- `GEMINI_API_KEY`: Required for README generation. Get your API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install api-key-guard
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/TESTING.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Testing the API Key Guard CLI
|
|
2
|
+
|
|
3
|
+
## Demo Instructions
|
|
4
|
+
|
|
5
|
+
This guide helps you test the API Key Guard CLI tool with the AI-powered README generator.
|
|
6
|
+
|
|
7
|
+
### Step 1: Install Dependencies
|
|
8
|
+
```bash
|
|
9
|
+
cd api-key-guard
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Step 2: Test Basic Commands
|
|
14
|
+
|
|
15
|
+
#### Test the help system:
|
|
16
|
+
```bash
|
|
17
|
+
node bin/cli.js --help
|
|
18
|
+
node bin/cli.js readme --help
|
|
19
|
+
node bin/cli.js scan --help
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
#### Test the scanner:
|
|
23
|
+
```bash
|
|
24
|
+
node bin/cli.js scan
|
|
25
|
+
node bin/cli.js scan --verbose
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Step 3: Test API Key Detection
|
|
29
|
+
Create a test file with fake API keys:
|
|
30
|
+
```javascript
|
|
31
|
+
// test-keys.js
|
|
32
|
+
const config = {
|
|
33
|
+
api_key: "sk-1234567890abcdef1234567890abcdef12345678",
|
|
34
|
+
aws_key: "AKIA1234567890ABCDEF",
|
|
35
|
+
github_token: "ghp_abcdefghijklmnopqrstuvwxyz1234567890"
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then scan:
|
|
40
|
+
```bash
|
|
41
|
+
node bin/cli.js scan --verbose
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Step 4: Test README Generator
|
|
45
|
+
|
|
46
|
+
#### Without API key (should show error):
|
|
47
|
+
```bash
|
|
48
|
+
node bin/cli.js readme --output test-readme.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### With API key (requires real Gemini API key):
|
|
52
|
+
1. Get API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
|
|
53
|
+
2. Set environment variable:
|
|
54
|
+
```bash
|
|
55
|
+
# Windows PowerShell
|
|
56
|
+
$env:GEMINI_API_KEY = "your_actual_api_key"
|
|
57
|
+
|
|
58
|
+
# Then run
|
|
59
|
+
node bin/cli.js readme --force --output ai-generated-readme.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 5: Test Git Hooks
|
|
63
|
+
```bash
|
|
64
|
+
# Initialize git (if not already done)
|
|
65
|
+
git init
|
|
66
|
+
|
|
67
|
+
# Install hooks
|
|
68
|
+
node bin/cli.js setup-hooks
|
|
69
|
+
|
|
70
|
+
# Test the hook by trying to commit a file with API keys
|
|
71
|
+
echo 'const key = "AKIA1234567890ABCDEF";' > bad-file.js
|
|
72
|
+
git add bad-file.js
|
|
73
|
+
git commit -m "test commit" # Should be blocked
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Expected Outputs
|
|
77
|
+
|
|
78
|
+
### Successful README Generation
|
|
79
|
+
When you have a valid API key, you should see:
|
|
80
|
+
```
|
|
81
|
+
š¤ Generating README with AI...
|
|
82
|
+
š Analyzing project structure...
|
|
83
|
+
š Generating README with Gemini AI...
|
|
84
|
+
š¾ Writing README.md...
|
|
85
|
+
ā
Successfully generated README.md!
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### API Key Detection
|
|
89
|
+
When scanning files with API keys:
|
|
90
|
+
```
|
|
91
|
+
š Scanning for API key leaks...
|
|
92
|
+
šØ Found X potential API key leak(s):
|
|
93
|
+
š filename.js:line_number
|
|
94
|
+
Pattern: detected_pattern
|
|
95
|
+
ā ļø Please review these findings and secure any exposed API keys!
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Error Handling
|
|
99
|
+
Without API key:
|
|
100
|
+
```
|
|
101
|
+
Error generating README: GEMINI_API_KEY environment variable is required. Please set your Gemini API key.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Cleanup After Testing
|
|
105
|
+
```bash
|
|
106
|
+
# Remove test files
|
|
107
|
+
rm test-keys.js bad-file.js ai-generated-readme.md
|
|
108
|
+
|
|
109
|
+
# Remove git hooks if needed
|
|
110
|
+
rm .git/hooks/pre-commit
|
|
111
|
+
```
|
package/USAGE.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# API Key Guard - Usage Guide
|
|
2
|
+
|
|
3
|
+
## Quick Setup
|
|
4
|
+
|
|
5
|
+
1. **Install the package:**
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g api-key-guard
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
2. **Get a Gemini API key:**
|
|
11
|
+
- Visit [Google AI Studio](https://makersuite.google.com/app/apikey)
|
|
12
|
+
- Create a new API key
|
|
13
|
+
- Copy the API key
|
|
14
|
+
|
|
15
|
+
3. **Set up environment variable:**
|
|
16
|
+
```bash
|
|
17
|
+
# Windows (PowerShell)
|
|
18
|
+
$env:GEMINI_API_KEY = "your_gemini_api_key_here"
|
|
19
|
+
|
|
20
|
+
# Windows (Command Prompt)
|
|
21
|
+
set GEMINI_API_KEY=your_gemini_api_key_here
|
|
22
|
+
|
|
23
|
+
# macOS/Linux
|
|
24
|
+
export GEMINI_API_KEY=your_gemini_api_key_here
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Using the AI-Powered README Generator
|
|
28
|
+
|
|
29
|
+
### Basic Usage
|
|
30
|
+
```bash
|
|
31
|
+
# Generate README.md with AI
|
|
32
|
+
api-key-guard readme
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With Options
|
|
36
|
+
```bash
|
|
37
|
+
# Force overwrite existing README.md
|
|
38
|
+
api-key-guard readme --force
|
|
39
|
+
|
|
40
|
+
# Output to a different file
|
|
41
|
+
api-key-guard readme --output DOCUMENTATION.md
|
|
42
|
+
|
|
43
|
+
# Combine options
|
|
44
|
+
api-key-guard readme --force --output NEW_README.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### What the AI Generates
|
|
48
|
+
The README generator creates a comprehensive README.md that includes:
|
|
49
|
+
|
|
50
|
+
- **Project title and description**
|
|
51
|
+
- **Problem statement** explaining API key leak issues
|
|
52
|
+
- **Features list** showcasing all capabilities
|
|
53
|
+
- **Installation instructions** for npm
|
|
54
|
+
- **CLI usage examples** for all commands
|
|
55
|
+
- **Git hooks setup guide**
|
|
56
|
+
- **Configuration options**
|
|
57
|
+
- **Security best practices**
|
|
58
|
+
- **Contributing guidelines**
|
|
59
|
+
- **License information**
|
|
60
|
+
|
|
61
|
+
## Other Commands
|
|
62
|
+
|
|
63
|
+
### Scan for API Key Leaks
|
|
64
|
+
```bash
|
|
65
|
+
# Scan current directory
|
|
66
|
+
api-key-guard scan
|
|
67
|
+
|
|
68
|
+
# Scan specific path
|
|
69
|
+
api-key-guard scan --path ./src
|
|
70
|
+
|
|
71
|
+
# Verbose output
|
|
72
|
+
api-key-guard scan --verbose
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Setup Git Hooks
|
|
76
|
+
```bash
|
|
77
|
+
# Install pre-commit hook
|
|
78
|
+
api-key-guard setup-hooks
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Error Handling
|
|
82
|
+
|
|
83
|
+
The tool provides clear error messages for common issues:
|
|
84
|
+
|
|
85
|
+
- **Missing API key**: Clear instructions on how to set GEMINI_API_KEY
|
|
86
|
+
- **Network issues**: Helpful network troubleshooting information
|
|
87
|
+
- **Invalid API responses**: Detailed API error messages
|
|
88
|
+
- **File permissions**: Clear permission error messages
|
|
89
|
+
|
|
90
|
+
## Security Features
|
|
91
|
+
|
|
92
|
+
- **No API key storage**: API keys are read from environment variables only
|
|
93
|
+
- **Pattern-based detection**: Uses regex patterns to detect various API key formats
|
|
94
|
+
- **Configurable ignore patterns**: Respects .gitignore-style patterns
|
|
95
|
+
- **Git hook integration**: Prevents commits with detected API keys
|
|
96
|
+
|
|
97
|
+
## Supported API Key Formats
|
|
98
|
+
|
|
99
|
+
The scanner detects these common API key patterns:
|
|
100
|
+
- Generic API keys (`api_key`, `secret_key`, `access_token`)
|
|
101
|
+
- AWS Access Keys (`AKIA...`)
|
|
102
|
+
- GitHub Personal Access Tokens (`ghp_...`)
|
|
103
|
+
- Google API Keys (`AIza...`)
|
|
104
|
+
- Bearer tokens
|
|
105
|
+
- Custom patterns (configurable)
|
|
106
|
+
|
|
107
|
+
## Configuration
|
|
108
|
+
|
|
109
|
+
Create a `api-key-guard.config.json` file to customize:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"ignorePatterns": [
|
|
114
|
+
"node_modules",
|
|
115
|
+
".git",
|
|
116
|
+
"dist",
|
|
117
|
+
"*.log"
|
|
118
|
+
],
|
|
119
|
+
"customPatterns": [
|
|
120
|
+
{
|
|
121
|
+
"name": "Custom API Key",
|
|
122
|
+
"pattern": "custom_[0-9a-f]{32}"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { generateReadme } = require('../src/readmeGenerator');
|
|
6
|
+
const { scanForApiKeys } = require('../src/scanner');
|
|
7
|
+
const { setupGitHooks } = require('../src/gitHooks');
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('api-key-guard')
|
|
13
|
+
.description('CLI tool to detect API key leaks and manage security')
|
|
14
|
+
.version('1.0.0');
|
|
15
|
+
|
|
16
|
+
// Scan command
|
|
17
|
+
program
|
|
18
|
+
.command('scan')
|
|
19
|
+
.description('Scan files for potential API key leaks')
|
|
20
|
+
.option('-p, --path <path>', 'Path to scan', '.')
|
|
21
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
console.log(chalk.blue('š Scanning for API key leaks...'));
|
|
25
|
+
await scanForApiKeys(options.path, options.verbose);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(chalk.red('Error during scan:', error.message));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Setup git hooks command
|
|
33
|
+
program
|
|
34
|
+
.command('setup-hooks')
|
|
35
|
+
.description('Setup git hooks for automatic API key detection')
|
|
36
|
+
.action(async () => {
|
|
37
|
+
try {
|
|
38
|
+
console.log(chalk.blue('āļø Setting up git hooks...'));
|
|
39
|
+
await setupGitHooks();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error(chalk.red('Error setting up hooks:', error.message));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// README generator command
|
|
47
|
+
program
|
|
48
|
+
.command('readme')
|
|
49
|
+
.description('Generate an AI-powered README.md file using Gemini API')
|
|
50
|
+
.option('-f, --force', 'Overwrite existing README.md without confirmation')
|
|
51
|
+
.option('-o, --output <file>', 'Output file path', 'README.md')
|
|
52
|
+
.action(async (options) => {
|
|
53
|
+
try {
|
|
54
|
+
console.log(chalk.blue('š¤ Generating README with AI...'));
|
|
55
|
+
await generateReadme(options);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(chalk.red('Error generating README:', error.message));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
program.parse();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiKeyPatterns": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Generic API Key",
|
|
5
|
+
"pattern": "(?i)api[_-]?key[\\s]*[=:][\\s]*['\"]?([a-z0-9]{20,})",
|
|
6
|
+
"description": "Generic API key pattern"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Generic Secret Key",
|
|
10
|
+
"pattern": "(?i)secret[_-]?key[\\s]*[=:][\\s]*['\"]?([a-z0-9]{20,})",
|
|
11
|
+
"description": "Generic secret key pattern"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Access Token",
|
|
15
|
+
"pattern": "(?i)access[_-]?token[\\s]*[=:][\\s]*['\"]?([a-z0-9]{20,})",
|
|
16
|
+
"description": "Generic access token pattern"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "AWS Access Key ID",
|
|
20
|
+
"pattern": "AKIA[0-9A-Z]{16}",
|
|
21
|
+
"description": "AWS Access Key ID"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "GitHub Token",
|
|
25
|
+
"pattern": "ghp_[0-9a-zA-Z]{36}",
|
|
26
|
+
"description": "GitHub personal access token"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Google API Key",
|
|
30
|
+
"pattern": "AIza[0-9A-Za-z\\-_]{35}",
|
|
31
|
+
"description": "Google API key"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"ignorePatterns": [
|
|
35
|
+
"node_modules",
|
|
36
|
+
".git",
|
|
37
|
+
"dist",
|
|
38
|
+
"build",
|
|
39
|
+
"*.log",
|
|
40
|
+
"*.min.js",
|
|
41
|
+
"package-lock.json",
|
|
42
|
+
".env.example"
|
|
43
|
+
],
|
|
44
|
+
"fileExtensions": [
|
|
45
|
+
".js", ".ts", ".jsx", ".tsx", ".json", ".md", ".txt", ".yml", ".yaml",
|
|
46
|
+
".xml", ".html", ".css", ".scss", ".sass", ".less", ".env", ".config",
|
|
47
|
+
".py", ".java", ".php", ".rb", ".go", ".rs", ".cpp", ".c", ".h"
|
|
48
|
+
]
|
|
49
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-key-guard",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A comprehensive tool to detect, prevent, and manage API key leaks in your codebase with AI-powered README generation",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"api-key-guard": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/cli.js",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"api-key",
|
|
15
|
+
"security",
|
|
16
|
+
"git-hooks",
|
|
17
|
+
"leak-detection",
|
|
18
|
+
"cli",
|
|
19
|
+
"readme-generator"
|
|
20
|
+
],
|
|
21
|
+
"author": "your-name",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"commander": "^11.0.0",
|
|
25
|
+
"chalk": "^4.1.2",
|
|
26
|
+
"axios": "^1.6.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/gitHooks.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Setup git hooks for automatic API key detection
|
|
7
|
+
*/
|
|
8
|
+
async function setupGitHooks() {
|
|
9
|
+
const gitHooksDir = path.join(process.cwd(), '.git', 'hooks');
|
|
10
|
+
const preCommitHook = path.join(gitHooksDir, 'pre-commit');
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
// Check if .git directory exists
|
|
14
|
+
await fs.access(path.join(process.cwd(), '.git'));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
throw new Error('No git repository found. Please initialize git first.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Ensure hooks directory exists
|
|
20
|
+
try {
|
|
21
|
+
await fs.mkdir(gitHooksDir, { recursive: true });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// Directory already exists
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hookContent = `#!/bin/sh
|
|
27
|
+
# api-key-guard pre-commit hook
|
|
28
|
+
# This hook scans staged files for potential API key leaks
|
|
29
|
+
|
|
30
|
+
echo "š Scanning staged files for API key leaks..."
|
|
31
|
+
|
|
32
|
+
# Run api-key-guard scan
|
|
33
|
+
npx api-key-guard scan --path .
|
|
34
|
+
|
|
35
|
+
# Check exit code
|
|
36
|
+
if [ $? -ne 0 ]; then
|
|
37
|
+
echo "šØ API key leaks detected! Commit aborted."
|
|
38
|
+
echo "Please review the findings and remove any exposed API keys before committing."
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo "ā
No API key leaks detected. Proceeding with commit."
|
|
43
|
+
exit 0
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
await fs.writeFile(preCommitHook, hookContent, 'utf8');
|
|
47
|
+
|
|
48
|
+
// Make the hook executable (Unix-like systems)
|
|
49
|
+
try {
|
|
50
|
+
await fs.chmod(preCommitHook, '755');
|
|
51
|
+
} catch (error) {
|
|
52
|
+
// Chmod might not work on Windows, but that's okay
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(chalk.green('ā
Git pre-commit hook installed successfully!'));
|
|
56
|
+
console.log(chalk.blue('š The hook will automatically scan for API key leaks before each commit.'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
setupGitHooks
|
|
61
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate README.md using Google's Gemini API
|
|
9
|
+
*/
|
|
10
|
+
async function generateReadme(options = {}) {
|
|
11
|
+
// Check for API key
|
|
12
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error('GEMINI_API_KEY environment variable is required. Please set your Gemini API key.');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const outputFile = options.output || 'README.md';
|
|
18
|
+
const force = options.force || false;
|
|
19
|
+
|
|
20
|
+
// Check if README.md already exists
|
|
21
|
+
if (!force) {
|
|
22
|
+
try {
|
|
23
|
+
await fs.access(outputFile);
|
|
24
|
+
const shouldOverwrite = await promptConfirmation(
|
|
25
|
+
`${outputFile} already exists. Do you want to overwrite it? (y/N): `
|
|
26
|
+
);
|
|
27
|
+
if (!shouldOverwrite) {
|
|
28
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// File doesn't exist, proceed
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(chalk.blue('š Analyzing project structure...'));
|
|
37
|
+
const projectContext = await analyzeProject();
|
|
38
|
+
|
|
39
|
+
console.log(chalk.blue('š Generating README with Gemini AI...'));
|
|
40
|
+
const readmeContent = await generateWithGemini(apiKey, projectContext);
|
|
41
|
+
|
|
42
|
+
console.log(chalk.blue('š¾ Writing README.md...'));
|
|
43
|
+
await fs.writeFile(outputFile, readmeContent, 'utf8');
|
|
44
|
+
|
|
45
|
+
console.log(chalk.green(`ā
Successfully generated ${outputFile}!`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Analyze the current project to gather context
|
|
50
|
+
*/
|
|
51
|
+
async function analyzeProject() {
|
|
52
|
+
const context = {
|
|
53
|
+
projectName: 'api-key-guard',
|
|
54
|
+
hasPackageJson: false,
|
|
55
|
+
hasSrcDirectory: false,
|
|
56
|
+
hasGitDirectory: false,
|
|
57
|
+
dependencies: [],
|
|
58
|
+
scripts: {}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// Check for package.json
|
|
63
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
64
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
65
|
+
context.hasPackageJson = true;
|
|
66
|
+
context.projectName = packageJson.name || context.projectName;
|
|
67
|
+
context.dependencies = Object.keys(packageJson.dependencies || {});
|
|
68
|
+
context.scripts = packageJson.scripts || {};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// No package.json found
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Check for src directory
|
|
75
|
+
await fs.access(path.join(process.cwd(), 'src'));
|
|
76
|
+
context.hasSrcDirectory = true;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// No src directory
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Check for git directory
|
|
83
|
+
await fs.access(path.join(process.cwd(), '.git'));
|
|
84
|
+
context.hasGitDirectory = true;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// No git directory
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return context;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generate README content using Gemini API
|
|
94
|
+
*/
|
|
95
|
+
async function generateWithGemini(apiKey, projectContext) {
|
|
96
|
+
const prompt = createGeminiPrompt(projectContext);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const response = await axios.post(
|
|
100
|
+
`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`,
|
|
101
|
+
{
|
|
102
|
+
contents: [{
|
|
103
|
+
parts: [{
|
|
104
|
+
text: prompt
|
|
105
|
+
}]
|
|
106
|
+
}],
|
|
107
|
+
generationConfig: {
|
|
108
|
+
temperature: 0.7,
|
|
109
|
+
topK: 40,
|
|
110
|
+
topP: 0.95,
|
|
111
|
+
maxOutputTokens: 2048,
|
|
112
|
+
},
|
|
113
|
+
safetySettings: [
|
|
114
|
+
{
|
|
115
|
+
category: "HARM_CATEGORY_HARASSMENT",
|
|
116
|
+
threshold: "BLOCK_MEDIUM_AND_ABOVE"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
category: "HARM_CATEGORY_HATE_SPEECH",
|
|
120
|
+
threshold: "BLOCK_MEDIUM_AND_ABOVE"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
124
|
+
threshold: "BLOCK_MEDIUM_AND_ABOVE"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
category: "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
128
|
+
threshold: "BLOCK_MEDIUM_AND_ABOVE"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (response.data.candidates && response.data.candidates[0].content.parts[0].text) {
|
|
140
|
+
return response.data.candidates[0].content.parts[0].text;
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error('Invalid response from Gemini API');
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (error.response) {
|
|
146
|
+
throw new Error(`Gemini API error: ${error.response.status} - ${error.response.data?.error?.message || 'Unknown error'}`);
|
|
147
|
+
} else if (error.request) {
|
|
148
|
+
throw new Error('Network error: Unable to reach Gemini API');
|
|
149
|
+
} else {
|
|
150
|
+
throw new Error(`Error generating content: ${error.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a detailed prompt for Gemini to generate README
|
|
157
|
+
*/
|
|
158
|
+
function createGeminiPrompt(context) {
|
|
159
|
+
return `Generate a professional README.md file for the "${context.projectName}" Node.js package. This is a comprehensive CLI tool for detecting, preventing, and managing API key leaks in codebases.
|
|
160
|
+
|
|
161
|
+
Context about the project:
|
|
162
|
+
- Project name: ${context.projectName}
|
|
163
|
+
- Has package.json: ${context.hasPackageJson}
|
|
164
|
+
- Has src directory: ${context.hasSrcDirectory}
|
|
165
|
+
- Has git repository: ${context.hasGitDirectory}
|
|
166
|
+
- Dependencies: ${context.dependencies.join(', ')}
|
|
167
|
+
|
|
168
|
+
Please generate a comprehensive README.md with the following sections:
|
|
169
|
+
|
|
170
|
+
1. **Project Title & Description**: Clear, concise description of what api-key-guard does
|
|
171
|
+
2. **Problem Statement**: Explain the critical issue of API key leaks in code repositories
|
|
172
|
+
3. **Features List**: Comprehensive list of features including:
|
|
173
|
+
- API key detection and scanning
|
|
174
|
+
- Git hooks integration
|
|
175
|
+
- CLI commands
|
|
176
|
+
- AI-powered README generation
|
|
177
|
+
- Multiple file format support
|
|
178
|
+
- Configurable ignore patterns
|
|
179
|
+
4. **Installation**: Step-by-step installation instructions using npm
|
|
180
|
+
5. **CLI Usage Examples**: Detailed examples of all CLI commands:
|
|
181
|
+
- \`api-key-guard scan\` with various options
|
|
182
|
+
- \`api-key-guard setup-hooks\` for git integration
|
|
183
|
+
- \`api-key-guard readme\` for README generation
|
|
184
|
+
6. **Git Hooks Usage**: How to set up and use pre-commit hooks
|
|
185
|
+
7. **Configuration**: Details about configuration files and ignore patterns
|
|
186
|
+
8. **Security Best Practices**: Important security notes and recommendations
|
|
187
|
+
9. **Future Roadmap**: Planned features and improvements
|
|
188
|
+
10. **Contributing**: Guidelines for contributors
|
|
189
|
+
11. **License**: MIT license information
|
|
190
|
+
|
|
191
|
+
Requirements:
|
|
192
|
+
- Use clear, developer-friendly language
|
|
193
|
+
- Include practical code examples
|
|
194
|
+
- Use proper markdown formatting with badges, code blocks, and tables
|
|
195
|
+
- Make it visually appealing with emojis and good structure
|
|
196
|
+
- Include security warnings and best practices
|
|
197
|
+
- Keep it professional but engaging
|
|
198
|
+
|
|
199
|
+
Generate ONLY the README.md content in markdown format, no additional text or explanations.`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Prompt user for confirmation
|
|
204
|
+
*/
|
|
205
|
+
function promptConfirmation(question) {
|
|
206
|
+
return new Promise((resolve) => {
|
|
207
|
+
const rl = readline.createInterface({
|
|
208
|
+
input: process.stdin,
|
|
209
|
+
output: process.stdout
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
rl.question(question, (answer) => {
|
|
213
|
+
rl.close();
|
|
214
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = {
|
|
220
|
+
generateReadme
|
|
221
|
+
};
|
package/src/scanner.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scan directory for potential API key leaks
|
|
7
|
+
*/
|
|
8
|
+
async function scanForApiKeys(scanPath = '.', verbose = false) {
|
|
9
|
+
const apiKeyPatterns = [
|
|
10
|
+
/api[_-]?key[\s]*[=:][\s]*['"]?([a-z0-9]{20,})/gi,
|
|
11
|
+
/secret[_-]?key[\s]*[=:][\s]*['"]?([a-z0-9]{20,})/gi,
|
|
12
|
+
/access[_-]?token[\s]*[=:][\s]*['"]?([a-z0-9]{20,})/gi,
|
|
13
|
+
/auth[_-]?token[\s]*[=:][\s]*['"]?([a-z0-9]{20,})/gi,
|
|
14
|
+
/bearer[\s]*[=:][\s]*['"]?([a-z0-9]{20,})/gi,
|
|
15
|
+
// AWS patterns
|
|
16
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
17
|
+
// GitHub tokens
|
|
18
|
+
/ghp_[0-9a-zA-Z]{36}/g,
|
|
19
|
+
// Google API keys
|
|
20
|
+
/AIza[0-9A-Za-z\-_]{35}/g,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const ignorePatterns = [
|
|
24
|
+
'node_modules',
|
|
25
|
+
'.git',
|
|
26
|
+
'dist',
|
|
27
|
+
'build',
|
|
28
|
+
'*.log',
|
|
29
|
+
'*.min.js',
|
|
30
|
+
'package-lock.json'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
let findings = [];
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const files = await getFilesRecursively(scanPath, ignorePatterns);
|
|
37
|
+
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
const content = await fs.readFile(file, 'utf8');
|
|
40
|
+
|
|
41
|
+
for (const pattern of apiKeyPatterns) {
|
|
42
|
+
let match;
|
|
43
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
44
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
45
|
+
findings.push({
|
|
46
|
+
file: path.relative(process.cwd(), file),
|
|
47
|
+
line: lineNumber,
|
|
48
|
+
pattern: match[0],
|
|
49
|
+
type: 'potential-api-key'
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (findings.length > 0) {
|
|
56
|
+
console.log(chalk.red(`šØ Found ${findings.length} potential API key leak(s):`));
|
|
57
|
+
findings.forEach(finding => {
|
|
58
|
+
console.log(chalk.yellow(` š ${finding.file}:${finding.line}`));
|
|
59
|
+
if (verbose) {
|
|
60
|
+
console.log(chalk.gray(` Pattern: ${finding.pattern}`));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
console.log(chalk.red('\nā ļø Please review these findings and secure any exposed API keys!'));
|
|
64
|
+
} else {
|
|
65
|
+
console.log(chalk.green('ā
No potential API key leaks detected!'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return findings;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(`Scan failed: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get all files recursively, respecting ignore patterns
|
|
76
|
+
*/
|
|
77
|
+
async function getFilesRecursively(dir, ignorePatterns = []) {
|
|
78
|
+
let files = [];
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
82
|
+
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const fullPath = path.join(dir, entry.name);
|
|
85
|
+
|
|
86
|
+
// Check if should be ignored
|
|
87
|
+
if (shouldIgnore(entry.name, fullPath, ignorePatterns)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
files = files.concat(await getFilesRecursively(fullPath, ignorePatterns));
|
|
93
|
+
} else {
|
|
94
|
+
// Only scan text files
|
|
95
|
+
if (isTextFile(entry.name)) {
|
|
96
|
+
files.push(fullPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
// Skip directories we can't read
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return files;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if file/directory should be ignored
|
|
109
|
+
*/
|
|
110
|
+
function shouldIgnore(name, fullPath, ignorePatterns) {
|
|
111
|
+
for (const pattern of ignorePatterns) {
|
|
112
|
+
if (pattern.includes('*')) {
|
|
113
|
+
// Simple glob pattern matching
|
|
114
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
115
|
+
if (regex.test(name)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
if (name === pattern || fullPath.includes(pattern)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if file is likely a text file based on extension
|
|
129
|
+
*/
|
|
130
|
+
function isTextFile(filename) {
|
|
131
|
+
const textExtensions = [
|
|
132
|
+
'.js', '.ts', '.jsx', '.tsx', '.json', '.md', '.txt', '.yml', '.yaml',
|
|
133
|
+
'.xml', '.html', '.css', '.scss', '.sass', '.less', '.env', '.config',
|
|
134
|
+
'.py', '.java', '.php', '.rb', '.go', '.rs', '.cpp', '.c', '.h'
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const ext = path.extname(filename).toLowerCase();
|
|
138
|
+
return textExtensions.includes(ext) || !ext; // Include files without extensions
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = {
|
|
142
|
+
scanForApiKeys
|
|
143
|
+
};
|