secretsweep 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/README.md +31 -20
- data/lib/secretsweep/cli.rb +15 -14
- data/lib/secretsweep/file_scanner.rb +15 -12
- data/lib/secretsweep/git_history_scanner.rb +34 -22
- data/lib/secretsweep/ignore_list.rb +9 -7
- data/lib/secretsweep/pattern_registry.rb +2 -2
- data/lib/secretsweep/scanner.rb +1 -3
- data/lib/secretsweep/version.rb +1 -1
- data/sig/secretsweep/cli.rbs +15 -0
- data/sig/secretsweep/entropy.rbs +9 -0
- data/sig/secretsweep/file_scanner.rbs +22 -0
- data/sig/secretsweep/finding.rbs +17 -0
- data/sig/secretsweep/git_history_scanner.rbs +16 -0
- data/sig/secretsweep/ignore_list.rbs +13 -0
- data/sig/secretsweep/pattern_registry.rbs +10 -0
- data/sig/secretsweep/reporter.rbs +9 -0
- data/sig/secretsweep/scanner.rbs +13 -0
- data/sig/secretsweep.rbs +6 -0
- metadata +38 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c6eacc4e23dcefebe23dac26989b385da1309472b664a5ee740ec168a935042
|
|
4
|
+
data.tar.gz: 250156cd89f9de57d6a426afe0838bbae9edecae1f9783c3627836448e959c5f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b1970d66615a8ad00bacb071ecf6627733b5b24c2ce9552343f7c0c889317835f74a16a53ed22085a75be1472fdfe25fb32f389c25c61e91e2bfb1c9f6361e5e
|
|
7
|
+
data.tar.gz: dba748c56fb40b5afb8739d4e994631a85fac2e1ad8b497a123041289537f885c19d4c74e76007918115775cf079c283679a9f9b07c427a5544e8f7d6f4937a4
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.2] - 2026-09-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- GitHub Actions CI workflow (`.github/workflows/ci.yml`) running RSpec, Rubocop, and RBS signature validation on every push and pull request, across Ruby 3.0–3.3.
|
|
13
|
+
- `metadata` in the gemspec (`homepage_uri`, `source_code_uri`, `changelog_uri`, `bug_tracker_uri`) for better discoverability on RubyGems.org.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Gemspec development dependencies corrected: `rspec` and `rbs` replace the stale `minitest` reference left over from the pre-migration scaffold.
|
|
18
|
+
- `sig/**/*.rbs` and `CHANGELOG.md` are now actually included in the packaged gem — previous versions omitted the type signatures entirely despite them existing in the repo.
|
|
19
|
+
|
|
20
|
+
## [0.1.1] - 2026-09-07
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Corrected the homepage/source code URL in `secretsweep.gemspec` to point to the actual GitHub repository.
|
|
25
|
+
|
|
26
|
+
## [0.1.0] - 2026-09-05
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Core secret-detection engine combining two independent methods:
|
|
31
|
+
- Pattern matching (`PatternRegistry`) for known formats: AWS keys, GitHub/Slack/Stripe tokens, PEM private key headers, Google API keys, JWTs, and a generic key-assignment heuristic.
|
|
32
|
+
- Shannon entropy analysis (`Entropy`) to catch high-entropy strings that don't match any known format.
|
|
33
|
+
- `FileScanner` — scans a directory tree for secrets in the current working tree, skipping binary files and common noise directories (`node_modules`, `.git`, `vendor`, etc.).
|
|
34
|
+
- `GitHistoryScanner` — scans full git history across all branches (`git log -p --all`) to catch secrets that were committed and later removed but are still readable in history.
|
|
35
|
+
- `IgnoreList` / `.secretsweepignore` support with two levels of granularity: path globs (skip a directory entirely) and per-finding fingerprints (allowlist one confirmed false positive without silencing the whole rule).
|
|
36
|
+
- Stable, content-based fingerprinting (`Finding#fingerprint`) so ignore-list entries survive unrelated edits to the same file.
|
|
37
|
+
- CLI (`bin/secretsweep`) with `--history`, `--format [text|json]`, and `--ignore-file` flags, and meaningful exit codes (0 clean / 1 secrets found / 2 error).
|
|
38
|
+
- Text and JSON report output via `Reporter`.
|
|
39
|
+
- Full RSpec test suite (37 examples), including an end-to-end test that creates a real throwaway git repo, commits a secret, removes it in a later commit, and verifies history scanning still finds it.
|
|
40
|
+
- RBS type signatures for the full public API.
|
|
41
|
+
- Zero runtime dependencies — pure Ruby standard library (`optparse`, `json`, `open3`, `digest`, `pathname`, `find`).
|
|
42
|
+
|
|
43
|
+
### Known limitations
|
|
44
|
+
|
|
45
|
+
- The same secret can be reported twice (once per detection method) if it satisfies both pattern matching and entropy analysis. This is intentional — see README for reasoning.
|
|
46
|
+
- Entropy thresholds are heuristic, not yet validated against a large real-world sample.
|
data/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**A dependency-free Ruby CLI that scans files and full git history for leaked secrets — API keys, tokens, private keys — using known-format patterns plus entropy analysis for everything else.**
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+

|
|
5
7
|

|
|
6
8
|

|
|
7
|
-

|
|
8
9
|

|
|
9
10
|
|
|
10
11
|
## Why this exists
|
|
@@ -34,12 +35,20 @@ rather than "fixed."
|
|
|
34
35
|
|
|
35
36
|
## Installation
|
|
36
37
|
|
|
38
|
+
SecretSweep is published on RubyGems:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
gem install secretsweep
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or from source:
|
|
45
|
+
|
|
37
46
|
```bash
|
|
38
|
-
git clone https://github.com/
|
|
47
|
+
git clone https://github.com/Majd2404/SecretSweep.git
|
|
39
48
|
cd SecretSweep
|
|
40
49
|
bundle install
|
|
41
50
|
gem build secretsweep.gemspec
|
|
42
|
-
gem install ./secretsweep
|
|
51
|
+
gem install ./secretsweep-*.gem
|
|
43
52
|
```
|
|
44
53
|
|
|
45
54
|
## Usage
|
|
@@ -84,7 +93,7 @@ directory you're scanning. Two kinds of entries:
|
|
|
84
93
|
|
|
85
94
|
```
|
|
86
95
|
# Skip scanning these paths entirely
|
|
87
|
-
|
|
96
|
+
spec/fixtures/**
|
|
88
97
|
|
|
89
98
|
# Allowlist ONE specific confirmed false positive by its fingerprint
|
|
90
99
|
# (does NOT silence the whole rule — just that exact file+type+snippet)
|
|
@@ -120,12 +129,9 @@ a1b2c3d4e5f6
|
|
|
120
129
|
GitHub token is both a recognized format *and* high-entropy). This is
|
|
121
130
|
left as-is rather than deduplicated across types: two independent
|
|
122
131
|
detectors agreeing is a legitimate confidence signal, and collapsing
|
|
123
|
-
it would hide which detector(s) actually fired.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- **Entropy thresholds are heuristic**, not universally tuned — see
|
|
127
|
-
`docs/COMMIT_PLAN.md` Week 2 for the plan to validate them against a
|
|
128
|
-
larger real-world sample.
|
|
132
|
+
it would hide which detector(s) actually fired.
|
|
133
|
+
- **Entropy thresholds are heuristic**, not yet validated against a
|
|
134
|
+
large real-world sample.
|
|
129
135
|
- **History scanning reads the full diff of every commit on every
|
|
130
136
|
branch** (`git log -p --all`) — this is thorough but can be slow on
|
|
131
137
|
very large, long-lived repositories. No pagination/depth limit yet.
|
|
@@ -133,23 +139,28 @@ a1b2c3d4e5f6
|
|
|
133
139
|
## Testing
|
|
134
140
|
|
|
135
141
|
```bash
|
|
136
|
-
|
|
142
|
+
bundle exec rspec
|
|
137
143
|
```
|
|
138
144
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
37 RSpec examples, including a real end-to-end test
|
|
146
|
+
(`git_history_scanner_spec.rb`) that creates an actual throwaway git
|
|
147
|
+
repo, commits a secret, removes it in a later commit, and verifies
|
|
148
|
+
`--history` still catches it.
|
|
149
|
+
|
|
150
|
+
Type signatures are validated separately:
|
|
144
151
|
|
|
145
|
-
|
|
152
|
+
```bash
|
|
153
|
+
bundle exec rbs -I sig validate
|
|
154
|
+
```
|
|
146
155
|
|
|
147
|
-
|
|
156
|
+
Both run automatically on every push via GitHub Actions across Ruby
|
|
157
|
+
3.0–3.3 — see the CI badge above.
|
|
148
158
|
|
|
149
159
|
## Tech stack
|
|
150
160
|
|
|
151
|
-
Ruby 3.0+, standard library only.
|
|
161
|
+
Ruby 3.0+, standard library only. RSpec for testing, RBS for type
|
|
162
|
+
signatures, Rubocop for style, GitHub Actions for CI.
|
|
152
163
|
|
|
153
164
|
## License
|
|
154
165
|
|
|
155
|
-
MIT — see [LICENSE](LICENSE).
|
|
166
|
+
MIT — see [LICENSE](LICENSE).
|
data/lib/secretsweep/cli.rb
CHANGED
|
@@ -18,28 +18,33 @@ module SecretSweep
|
|
|
18
18
|
|
|
19
19
|
def run(argv)
|
|
20
20
|
options = parse(argv)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
scanner = Scanner.new(path, ignore_file: options[:ignore_file], include_history: options[:history])
|
|
24
|
-
findings = scanner.run
|
|
21
|
+
findings = scan(options)
|
|
25
22
|
|
|
26
23
|
puts Reporter.render(findings, format: options[:format])
|
|
27
|
-
|
|
28
24
|
findings.empty? ? EXIT_CLEAN : EXIT_SECRETS_FOUND
|
|
29
|
-
rescue GitHistoryScanner::NotAGitRepoError => e
|
|
30
|
-
warn "Error: #{e.message}"
|
|
31
|
-
EXIT_ERROR
|
|
32
|
-
rescue Errno::ENOENT => e
|
|
25
|
+
rescue GitHistoryScanner::NotAGitRepoError, Errno::ENOENT => e
|
|
33
26
|
warn "Error: #{e.message}"
|
|
34
27
|
EXIT_ERROR
|
|
35
28
|
end
|
|
36
29
|
|
|
37
30
|
private
|
|
38
31
|
|
|
32
|
+
def scan(options)
|
|
33
|
+
path = options[:path] || "."
|
|
34
|
+
Scanner.new(path, ignore_file: options[:ignore_file], include_history: options[:history]).run
|
|
35
|
+
end
|
|
36
|
+
|
|
39
37
|
def parse(argv)
|
|
40
38
|
options = { format: :text, history: false }
|
|
39
|
+
parser = build_option_parser(options)
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
remaining = parser.parse(argv)
|
|
42
|
+
options[:path] = remaining.first
|
|
43
|
+
options
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def build_option_parser(options)
|
|
47
|
+
OptionParser.new do |opts|
|
|
43
48
|
opts.banner = "Usage: secretsweep [path] [options]"
|
|
44
49
|
|
|
45
50
|
opts.on("--history", "Also scan full git history, not just the working tree") do
|
|
@@ -64,10 +69,6 @@ module SecretSweep
|
|
|
64
69
|
exit(0)
|
|
65
70
|
end
|
|
66
71
|
end
|
|
67
|
-
|
|
68
|
-
remaining = parser.parse(argv)
|
|
69
|
-
options[:path] = remaining.first
|
|
70
|
-
options
|
|
71
72
|
end
|
|
72
73
|
end
|
|
73
74
|
end
|
|
@@ -50,17 +50,7 @@ module SecretSweep
|
|
|
50
50
|
findings = []
|
|
51
51
|
|
|
52
52
|
File.foreach(path).with_index(1) do |line, line_number|
|
|
53
|
-
|
|
54
|
-
# unexpected control characters) should never discard matches
|
|
55
|
-
# already found earlier in the same file.
|
|
56
|
-
begin
|
|
57
|
-
PatternRegistry.scan_line(line).each do |match|
|
|
58
|
-
findings << Finding.new(relative_path, line_number, match[:type], match[:snippet], nil)
|
|
59
|
-
end
|
|
60
|
-
check_high_entropy_tokens(line, relative_path, line_number, findings)
|
|
61
|
-
rescue ArgumentError, Encoding::InvalidByteSequenceError
|
|
62
|
-
next
|
|
63
|
-
end
|
|
53
|
+
findings.concat(scan_line_safely(line, relative_path, line_number))
|
|
64
54
|
end
|
|
65
55
|
|
|
66
56
|
findings
|
|
@@ -70,13 +60,26 @@ module SecretSweep
|
|
|
70
60
|
findings
|
|
71
61
|
end
|
|
72
62
|
|
|
63
|
+
# Guards per-line, not per-file: one malformed line (bad encoding,
|
|
64
|
+
# unexpected control characters) should never discard matches already
|
|
65
|
+
# found earlier in the same file.
|
|
66
|
+
def scan_line_safely(line, relative_path, line_number)
|
|
67
|
+
findings = PatternRegistry.scan_line(line).map do |match|
|
|
68
|
+
Finding.new(relative_path, line_number, match[:type], match[:snippet], nil)
|
|
69
|
+
end
|
|
70
|
+
check_high_entropy_tokens(line, relative_path, line_number, findings)
|
|
71
|
+
findings
|
|
72
|
+
rescue ArgumentError, Encoding::InvalidByteSequenceError
|
|
73
|
+
[]
|
|
74
|
+
end
|
|
75
|
+
|
|
73
76
|
# Beyond known patterns, flag bare high-entropy tokens assigned to a
|
|
74
77
|
# variable — catches secrets in formats we don't have a specific regex
|
|
75
78
|
# for yet (a new provider's token format, an internal auth token).
|
|
76
79
|
def check_high_entropy_tokens(line, relative_path, line_number, findings)
|
|
77
80
|
return unless line.match?(/=|:/)
|
|
78
81
|
|
|
79
|
-
line.scan(
|
|
82
|
+
line.scan(%r{['"]([A-Za-z0-9/+_-]{20,})['"]}) do |match|
|
|
80
83
|
token = match[0]
|
|
81
84
|
next unless Entropy.high_entropy?(token)
|
|
82
85
|
|
|
@@ -10,6 +10,16 @@ module SecretSweep
|
|
|
10
10
|
class GitHistoryScanner
|
|
11
11
|
class NotAGitRepoError < StandardError; end
|
|
12
12
|
|
|
13
|
+
# Tracks the small bit of state needed while walking `git log -p`
|
|
14
|
+
# output line by line: which commit and file we're currently inside,
|
|
15
|
+
# plus the findings accumulated so far. Extracted from Scanner so
|
|
16
|
+
# #scan itself stays a short, readable dispatch loop.
|
|
17
|
+
State = Struct.new(:commit, :file, :findings) do
|
|
18
|
+
def initialize
|
|
19
|
+
super(nil, nil, [])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
13
23
|
def initialize(repo_path, ignore_list: IgnoreList.new([], []))
|
|
14
24
|
@repo_path = repo_path
|
|
15
25
|
@ignore_list = ignore_list
|
|
@@ -18,41 +28,43 @@ module SecretSweep
|
|
|
18
28
|
def scan
|
|
19
29
|
raise NotAGitRepoError, "#{@repo_path} is not a git repository" unless git_repo?
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
each_diff_line do |line|
|
|
26
|
-
if line.start_with?("commit ")
|
|
27
|
-
current_commit = line.split(" ", 2).last[0, 12]
|
|
28
|
-
elsif line.start_with?("+++ b/")
|
|
29
|
-
current_file = line.sub("+++ b/", "").strip
|
|
30
|
-
elsif line.start_with?("+") && !line.start_with?("+++")
|
|
31
|
-
content = line[1..]
|
|
32
|
-
next if current_file.nil? || @ignore_list.path_ignored?(current_file)
|
|
33
|
-
|
|
34
|
-
PatternRegistry.scan_line(content).each do |match|
|
|
35
|
-
findings << Finding.new(current_file, nil, match[:type], match[:snippet], current_commit)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
findings
|
|
31
|
+
state = State.new
|
|
32
|
+
each_diff_line { |line| process_line(line, state) }
|
|
33
|
+
state.findings
|
|
41
34
|
end
|
|
42
35
|
|
|
43
36
|
private
|
|
44
37
|
|
|
38
|
+
def process_line(line, state)
|
|
39
|
+
case line
|
|
40
|
+
when /\Acommit /
|
|
41
|
+
state.commit = line.split(" ", 2).last[0, 12]
|
|
42
|
+
when %r{\A\+\+\+ b/}
|
|
43
|
+
state.file = line.sub("+++ b/", "").strip
|
|
44
|
+
when /\A\+(?!\+\+)/
|
|
45
|
+
record_added_line(line[1..], state)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def record_added_line(content, state)
|
|
50
|
+
return if state.file.nil? || @ignore_list.path_ignored?(state.file)
|
|
51
|
+
|
|
52
|
+
PatternRegistry.scan_line(content).each do |match|
|
|
53
|
+
state.findings << Finding.new(state.file, nil, match[:type], match[:snippet], state.commit)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
45
57
|
def git_repo?
|
|
46
58
|
_out, status = Open3.capture2("git", "-C", @repo_path, "rev-parse", "--is-inside-work-tree")
|
|
47
59
|
status.success?
|
|
48
60
|
end
|
|
49
61
|
|
|
50
|
-
def each_diff_line
|
|
62
|
+
def each_diff_line(&block)
|
|
51
63
|
# -p: show patches (actual added/removed lines), --all: every branch,
|
|
52
64
|
# not just the currently checked-out one — a secret on a stale
|
|
53
65
|
# feature branch is still a leak.
|
|
54
66
|
Open3.popen2("git", "-C", @repo_path, "log", "-p", "--all", "--no-color") do |_in, out, _thread|
|
|
55
|
-
out.each_line
|
|
67
|
+
out.each_line(&block)
|
|
56
68
|
end
|
|
57
69
|
end
|
|
58
70
|
end
|
|
@@ -13,22 +13,24 @@ module SecretSweep
|
|
|
13
13
|
def self.load(path)
|
|
14
14
|
return new([], []) unless path && File.exist?(path)
|
|
15
15
|
|
|
16
|
+
globs, fingerprints = partition_lines(File.readlines(path))
|
|
17
|
+
new(globs, fingerprints)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.partition_lines(raw_lines)
|
|
16
21
|
globs = []
|
|
17
22
|
fingerprints = []
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
raw_lines.each do |raw_line|
|
|
20
25
|
line = raw_line.strip
|
|
21
26
|
next if line.empty? || line.start_with?("#")
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
fingerprints << line
|
|
25
|
-
else
|
|
26
|
-
globs << line
|
|
27
|
-
end
|
|
28
|
+
line.match?(/\A[0-9a-f]{12}\z/) ? fingerprints << line : globs << line
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
[globs, fingerprints]
|
|
31
32
|
end
|
|
33
|
+
private_class_method :partition_lines
|
|
32
34
|
|
|
33
35
|
def initialize(path_globs, fingerprints)
|
|
34
36
|
@path_globs = path_globs
|
|
@@ -8,7 +8,7 @@ module SecretSweep
|
|
|
8
8
|
module PatternRegistry
|
|
9
9
|
PATTERNS = {
|
|
10
10
|
"aws_access_key_id" => /\bAKIA[0-9A-Z]{16}\b/,
|
|
11
|
-
"aws_secret_access_key" =>
|
|
11
|
+
"aws_secret_access_key" => %r{\baws(.{0,20})?(secret|access)[_-]?key\b.{0,5}['"]([A-Za-z0-9/+=]{40})['"]}i,
|
|
12
12
|
"github_token" => /\bgh[pousr]_[A-Za-z0-9]{36,}\b/,
|
|
13
13
|
"slack_token" => /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
|
|
14
14
|
"stripe_live_key" => /\bsk_live_[A-Za-z0-9]{24,}\b/,
|
|
@@ -16,7 +16,7 @@ module SecretSweep
|
|
|
16
16
|
"generic_private_key" => /-----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/,
|
|
17
17
|
"google_api_key" => /\bAIza[0-9A-Za-z\-_]{35}\b/,
|
|
18
18
|
"generic_api_key_assignment" =>
|
|
19
|
-
|
|
19
|
+
%r{\b(api[_-]?key|secret|token|password|passwd|pwd)\b\s*[:=]\s*['"][A-Za-z0-9/+=_-]{16,}['"]}i,
|
|
20
20
|
"jwt" => /\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/
|
|
21
21
|
}.freeze
|
|
22
22
|
|
data/lib/secretsweep/scanner.rb
CHANGED
|
@@ -15,9 +15,7 @@ module SecretSweep
|
|
|
15
15
|
def run
|
|
16
16
|
findings = FileScanner.new(@path, ignore_list: @ignore_list).scan
|
|
17
17
|
|
|
18
|
-
if @include_history
|
|
19
|
-
findings.concat(GitHistoryScanner.new(@path, ignore_list: @ignore_list).scan)
|
|
20
|
-
end
|
|
18
|
+
findings.concat(GitHistoryScanner.new(@path, ignore_list: @ignore_list).scan) if @include_history
|
|
21
19
|
|
|
22
20
|
dedupe(@ignore_list.reject_ignored(findings))
|
|
23
21
|
end
|
data/lib/secretsweep/version.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class CLI
|
|
3
|
+
EXIT_CLEAN: Integer
|
|
4
|
+
EXIT_SECRETS_FOUND: Integer
|
|
5
|
+
EXIT_ERROR: Integer
|
|
6
|
+
|
|
7
|
+
def self.start: (Array[String] argv) -> Integer
|
|
8
|
+
|
|
9
|
+
def run: (Array[String] argv) -> Integer
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def parse: (Array[String] argv) -> Hash[Symbol, untyped]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class FileScanner
|
|
3
|
+
SKIP_DIRS: Array[String]
|
|
4
|
+
MAX_FILE_SIZE: Integer
|
|
5
|
+
|
|
6
|
+
def initialize: (String root, ?ignore_list: IgnoreList) -> void
|
|
7
|
+
|
|
8
|
+
def scan: () -> Array[Finding]
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def each_scannable_file: () { (String path) -> void } -> void
|
|
13
|
+
|
|
14
|
+
def scan_file: (String path, String relative_path) -> Array[Finding]
|
|
15
|
+
|
|
16
|
+
def check_high_entropy_tokens: (String line, String relative_path, Integer line_number, Array[Finding] findings) -> void
|
|
17
|
+
|
|
18
|
+
def relative: (String path) -> String
|
|
19
|
+
|
|
20
|
+
def binary?: (String path) -> bool
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class Finding
|
|
3
|
+
attr_reader source: String
|
|
4
|
+
attr_reader line_number: Integer?
|
|
5
|
+
attr_reader type: String
|
|
6
|
+
attr_reader snippet: String
|
|
7
|
+
attr_reader commit: String?
|
|
8
|
+
|
|
9
|
+
def initialize: (String source, Integer? line_number, String type, String snippet, String? commit) -> void
|
|
10
|
+
|
|
11
|
+
def fingerprint: () -> String
|
|
12
|
+
|
|
13
|
+
def redacted_snippet: () -> String
|
|
14
|
+
|
|
15
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class GitHistoryScanner
|
|
3
|
+
class NotAGitRepoError < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def initialize: (String repo_path, ?ignore_list: IgnoreList) -> void
|
|
7
|
+
|
|
8
|
+
def scan: () -> Array[Finding]
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def git_repo?: () -> bool
|
|
13
|
+
|
|
14
|
+
def each_diff_line: () { (String line) -> void } -> void
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class IgnoreList
|
|
3
|
+
def self.load: (String? path) -> IgnoreList
|
|
4
|
+
|
|
5
|
+
def initialize: (Array[String] path_globs, Array[String] fingerprints) -> void
|
|
6
|
+
|
|
7
|
+
def path_ignored?: (String path) -> bool
|
|
8
|
+
|
|
9
|
+
def finding_ignored?: (Finding finding) -> bool
|
|
10
|
+
|
|
11
|
+
def reject_ignored: (Array[Finding] findings) -> Array[Finding]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module SecretSweep
|
|
2
|
+
class Scanner
|
|
3
|
+
def initialize: (String path, ?ignore_file: String?, ?include_history: bool) -> void
|
|
4
|
+
|
|
5
|
+
def run: () -> Array[Finding]
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def default_ignore_path: () -> String?
|
|
10
|
+
|
|
11
|
+
def dedupe: (Array[Finding] findings) -> Array[Finding]
|
|
12
|
+
end
|
|
13
|
+
end
|
data/sig/secretsweep.rbs
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secretsweep
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Majd
|
|
@@ -10,33 +10,47 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: rake
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '13.0'
|
|
19
19
|
type: :development
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '13.0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: rbs
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '3.0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '3.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.13'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.13'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: rubocop
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -61,6 +75,7 @@ executables:
|
|
|
61
75
|
extensions: []
|
|
62
76
|
extra_rdoc_files: []
|
|
63
77
|
files:
|
|
78
|
+
- CHANGELOG.md
|
|
64
79
|
- LICENSE
|
|
65
80
|
- README.md
|
|
66
81
|
- bin/secretsweep
|
|
@@ -75,10 +90,24 @@ files:
|
|
|
75
90
|
- lib/secretsweep/reporter.rb
|
|
76
91
|
- lib/secretsweep/scanner.rb
|
|
77
92
|
- lib/secretsweep/version.rb
|
|
78
|
-
|
|
93
|
+
- sig/secretsweep.rbs
|
|
94
|
+
- sig/secretsweep/cli.rbs
|
|
95
|
+
- sig/secretsweep/entropy.rbs
|
|
96
|
+
- sig/secretsweep/file_scanner.rbs
|
|
97
|
+
- sig/secretsweep/finding.rbs
|
|
98
|
+
- sig/secretsweep/git_history_scanner.rbs
|
|
99
|
+
- sig/secretsweep/ignore_list.rbs
|
|
100
|
+
- sig/secretsweep/pattern_registry.rbs
|
|
101
|
+
- sig/secretsweep/reporter.rbs
|
|
102
|
+
- sig/secretsweep/scanner.rbs
|
|
103
|
+
homepage: https://github.com/Majd2404/SecretSweep
|
|
79
104
|
licenses:
|
|
80
105
|
- MIT
|
|
81
|
-
metadata:
|
|
106
|
+
metadata:
|
|
107
|
+
homepage_uri: https://github.com/Majd2404/SecretSweep
|
|
108
|
+
source_code_uri: https://github.com/Majd2404/SecretSweep
|
|
109
|
+
changelog_uri: https://github.com/Majd2404/SecretSweep/blob/main/CHANGELOG.md
|
|
110
|
+
bug_tracker_uri: https://github.com/Majd2404/SecretSweep/issues
|
|
82
111
|
rdoc_options: []
|
|
83
112
|
require_paths:
|
|
84
113
|
- lib
|