Skip to content

Commit 6b49a43

Browse files
authored
Merge pull request #4 from epilande/secrets
feat: Add Automatic Secret Detection and Redaction
2 parents ee85fe3 + 0595fb8 commit 6b49a43

File tree

15 files changed

+808
-113
lines changed

15 files changed

+808
-113
lines changed

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ to your clipboard, ready for LLM processing.
2929
- 📋 **Clipboard Integration**: Copy content or output file directly to your clipboard
3030
- 🌲 **Directory Tree View**: Display a tree-style view of your project structure
3131
- 🧮 **Token Estimation**: Get estimated token count for LLM context windows
32+
- 🛡️ **Secret Detection & Redaction**: Uses [gitleaks](https://github.com/gitleaks/gitleaks) to identify potential secrets and prevent sharing sensitive information
3233

3334
## 📦 Installation
3435

@@ -98,6 +99,8 @@ grab [options] [directory]
9899
| `-t, --temp` | Use system temporary directory for output file |
99100
| `-g, --glob pattern` | Include/exclude files and directories (e.g., `--glob="*.{ts,tsx}" --glob="!*.spec.ts"`) |
100101
| `-f, --format format` | Output format (available: markdown, text, xml) |
102+
| `-S, --skip-redaction` | Skip automatic secret redaction (WARNING: This may expose sensitive information) |
103+
| |
101104
| `--theme` | Set the UI theme |
102105

103106
### 📖 Examples
@@ -169,12 +172,13 @@ grab [options] [directory]
169172

170173
### Selection & Output
171174

172-
| Action | Key | Description |
173-
| :------------------- | :--------------------------------- | :----------------------------------------------------------- |
174-
| Select/deselect item | <kbd>tab</kbd> or <kbd>space</kbd> | Toggle selection of the current file or directory |
175-
| Copy to clipboard | <kbd>y</kbd> | Copy the generated output to clipboard |
176-
| Generate output file | <kbd>g</kbd> | Generate the output file with selected content |
177-
| Cycle output formats | <kbd>F</kbd> | Cycle through available output formats (markdown, text, xml) |
175+
| Action | Key | Description |
176+
| :---------------------- | :--------------------------------- | :----------------------------------------------------------- |
177+
| Select/deselect item | <kbd>tab</kbd> or <kbd>space</kbd> | Toggle selection of the current file or directory |
178+
| Copy to clipboard | <kbd>y</kbd> | Copy the generated output to clipboard |
179+
| Generate output file | <kbd>g</kbd> | Generate the output file with selected content |
180+
| Cycle output formats | <kbd>F</kbd> | Cycle through available output formats (markdown, text, xml) |
181+
| Toggle Secret Redaction | <kbd>S</kbd> | Enable/disable automatic secret redaction (Default: On) |
178182

179183
### View Options
180184

@@ -185,6 +189,14 @@ grab [options] [directory]
185189
| Toggle help screen | <kbd>?</kbd> | Show or hide the help screen |
186190
| Quit | <kbd>q</kbd> | Exit the application |
187191

192+
## 🛡️ Secret Detection & Redaction
193+
194+
CodeGrab automatically scans the content of selected files for potential secrets using [gitleaks](https://github.com/gitleaks/gitleaks) with its default rules. This helps prevent accidental exposure of sensitive credentials like API keys, private tokens, and passwords.
195+
196+
- **Enabled by Default**: Secret scanning and redaction are active unless explicitly disabled.
197+
- **Redaction Format**: Detected secrets are replaced with `[REDACTED_RuleID]`, where `RuleID` indicates the type of secret found (e.g., `[REDACTED_generic-api-key]`).
198+
- **Skipping Redaction**: You can disable this feature using the `-S` / `--skip-redaction` flag when running the command, or by pressing `S` in the interactive TUI. Use this option with caution, as it may expose sensitive information in the output.
199+
188200
## 🎨 Themes
189201

190202
CodeGrab comes with several built-in themes:

cmd/grab/main.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func main() {
3939
var useTempFile bool
4040
var themeName string
4141
var formatName string
42+
var skipRedaction bool
4243

4344
flag.BoolVar(&showHelp, "help", false, "Display help information")
4445
flag.BoolVar(&showHelp, "h", false, "Display help information (shorthand)")
@@ -67,6 +68,9 @@ func main() {
6768
flag.StringVar(&formatName, "format", "markdown", formatUsage)
6869
flag.StringVar(&formatName, "f", "markdown", formatUsage+" (shorthand)")
6970

71+
flag.BoolVar(&skipRedaction, "skip-redaction", false, "Skip automatic secret redaction (WARNING: this may expose secrets)")
72+
flag.BoolVar(&skipRedaction, "S", false, "Skip automatic secret redaction (shorthand)")
73+
7074
flag.Parse()
7175

7276
if showHelp {
@@ -111,14 +115,15 @@ func main() {
111115
}
112116

113117
if nonInteractive {
114-
runNonInteractive(root, filterMgr, outputPath, useTempFile, formatName)
118+
runNonInteractive(root, filterMgr, outputPath, useTempFile, formatName, skipRedaction)
115119
} else {
116120
config := model.Config{
117-
RootPath: root,
118-
FilterMgr: filterMgr,
119-
OutputPath: outputPath,
120-
UseTempFile: useTempFile,
121-
Format: formatName,
121+
RootPath: root,
122+
FilterMgr: filterMgr,
123+
OutputPath: outputPath,
124+
UseTempFile: useTempFile,
125+
Format: formatName,
126+
SkipRedaction: skipRedaction,
122127
}
123128

124129
m := model.NewModel(config)
@@ -131,7 +136,7 @@ func main() {
131136
}
132137

133138
// runNonInteractive processes files and generates output without user interaction
134-
func runNonInteractive(rootPath string, filterMgr *filesystem.FilterManager, outputPath string, useTempFile bool, formatName string) {
139+
func runNonInteractive(rootPath string, filterMgr *filesystem.FilterManager, outputPath string, useTempFile bool, formatName string, skipRedaction bool) {
135140
gitIgnoreMgr, err := filesystem.NewGitIgnoreManager(rootPath)
136141
if err != nil {
137142
fmt.Fprintf(os.Stderr, "Error reading .gitignore: %v\n", err)
@@ -146,6 +151,7 @@ func runNonInteractive(rootPath string, filterMgr *filesystem.FilterManager, out
146151
gen := generator.NewGenerator(rootPath, gitIgnoreMgr, filterMgr, outputPath, useTempFile)
147152
format := formats.GetFormat(formatName)
148153
gen.SetFormat(format)
154+
gen.SetRedactionMode(!skipRedaction)
149155

150156
// Automatically select all non-directory files
151157
selectedFiles := make(map[string]bool)
@@ -157,10 +163,16 @@ func runNonInteractive(rootPath string, filterMgr *filesystem.FilterManager, out
157163

158164
gen.SelectedFiles = selectedFiles
159165

160-
outputFilePath, tokenCount, err := gen.Generate()
166+
outputFilePath, tokenCount, secretCount, err := gen.Generate()
161167
if err != nil {
162168
log.Fatalf("Error generating output: %v\n", err)
163169
}
164170

165-
fmt.Printf("✅ %s generated: %s (%d tokens)\n", formatName, outputFilePath, tokenCount)
171+
fmt.Printf("✅ Generated %s (%d tokens)\n", outputFilePath, tokenCount)
172+
173+
if secretCount > 0 && skipRedaction {
174+
fmt.Fprintf(os.Stderr, "⚠️ WARNING: %d secrets detected in the output and redaction was skipped!\n", secretCount)
175+
} else if secretCount > 0 && !skipRedaction {
176+
fmt.Fprintf(os.Stderr, "ℹ️ INFO: %d secrets detected and redacted in the output.\n", secretCount)
177+
}
166178
}

go.mod

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,61 @@ require (
88
github.com/charmbracelet/bubbletea v1.2.4
99
github.com/charmbracelet/lipgloss v1.0.0
1010
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
11+
github.com/zricethezav/gitleaks/v8 v8.24.2
1112
)
1213

1314
require (
15+
dario.cat/mergo v1.0.1 // indirect
16+
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
17+
github.com/Masterminds/goutils v1.1.1 // indirect
18+
github.com/Masterminds/semver/v3 v3.3.0 // indirect
19+
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
1420
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
1521
github.com/charmbracelet/x/ansi v0.4.5 // indirect
1622
github.com/charmbracelet/x/term v0.2.1 // indirect
1723
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
24+
github.com/fatih/semgroup v1.2.0 // indirect
25+
github.com/fsnotify/fsnotify v1.8.0 // indirect
26+
github.com/gitleaks/go-gitdiff v0.9.1 // indirect
27+
github.com/google/uuid v1.6.0 // indirect
28+
github.com/h2non/filetype v1.1.3 // indirect
29+
github.com/hashicorp/hcl v1.0.0 // indirect
30+
github.com/huandu/xstrings v1.5.0 // indirect
1831
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
32+
github.com/magiconair/properties v1.8.9 // indirect
33+
github.com/mattn/go-colorable v0.1.14 // indirect
1934
github.com/mattn/go-isatty v0.0.20 // indirect
2035
github.com/mattn/go-localereader v0.0.1 // indirect
2136
github.com/mattn/go-runewidth v0.0.16 // indirect
37+
github.com/mitchellh/copystructure v1.2.0 // indirect
38+
github.com/mitchellh/mapstructure v1.5.0 // indirect
39+
github.com/mitchellh/reflectwalk v1.0.2 // indirect
2240
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
2341
github.com/muesli/cancelreader v0.2.2 // indirect
2442
github.com/muesli/termenv v0.15.2 // indirect
43+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
2544
github.com/rivo/uniseg v0.4.7 // indirect
26-
golang.org/x/sync v0.9.0 // indirect
27-
golang.org/x/sys v0.27.0 // indirect
28-
golang.org/x/text v0.3.8 // indirect
45+
github.com/rogpeppe/go-internal v1.13.1 // indirect
46+
github.com/rs/zerolog v1.33.0 // indirect
47+
github.com/sagikazarmark/locafero v0.7.0 // indirect
48+
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
49+
github.com/shopspring/decimal v1.4.0 // indirect
50+
github.com/sourcegraph/conc v0.3.0 // indirect
51+
github.com/spf13/afero v1.12.0 // indirect
52+
github.com/spf13/cast v1.7.1 // indirect
53+
github.com/spf13/pflag v1.0.6 // indirect
54+
github.com/spf13/viper v1.19.0 // indirect
55+
github.com/subosito/gotenv v1.6.0 // indirect
56+
github.com/tetratelabs/wazero v1.9.0 // indirect
57+
github.com/wasilibs/go-re2 v1.9.0 // indirect
58+
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
59+
go.uber.org/multierr v1.11.0 // indirect
60+
golang.org/x/crypto v0.35.0 // indirect
61+
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
62+
golang.org/x/sync v0.11.0 // indirect
63+
golang.org/x/sys v0.30.0 // indirect
64+
golang.org/x/text v0.22.0 // indirect
65+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
66+
gopkg.in/ini.v1 v1.67.0 // indirect
67+
gopkg.in/yaml.v3 v3.0.1 // indirect
2968
)

0 commit comments

Comments
 (0)