sanctify 0.2.5 → 0.3.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +22 -2
- data/lib/sanctify/cli.rb +1 -0
- data/lib/sanctify/matcher.rb +15 -0
- data/lib/sanctify/matcher_list.rb +45 -7
- data/lib/sanctify/repo.rb +6 -5
- data/lib/sanctify/scanner.rb +11 -24
- data/lib/sanctify/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb2d4bc5fa6962ffe6d54ff7881fe1aa876d4fb1
|
4
|
+
data.tar.gz: 5ac1b2ac6cb425f7c72cf55318fad1fee2a87f03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 012dcb7a0de9b152fa3e94804926d664ebe206f513615b7cfda835952256fa8c6bb2cc227bb6d80e1916df68b5eabd38b3c06c7b4af4b0a73ad0ce96ac751dc1
|
7
|
+
data.tar.gz: 9794b0c234c5bd50a5bee592498fa54998d45d116ad5e534c4f22e49fb0924a470ce02b47699f2b061c355cbb44c5139361ade5435bec8de14cb2411265b7507
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -48,14 +48,15 @@ repos:
|
|
48
48
|
|
49
49
|
## Configuration
|
50
50
|
|
51
|
-
Sanctify supports
|
51
|
+
Sanctify supports 3 top-level objects in the config: `ignored_paths`,`custom_matchers`, and `disabled_matchers`. Currently sanctify supports a number of default matchers, but you are free to add more to your config file under custom_matchers. If there is a file that you know has secrets or is a false positive, you can add a list of Ruby-style regexes to ignore certain files. Currently the `id` field is optional and is used to select matchers from the default list you want to disable. However, we recommend adding an `id` so that in future features you will be able to explicitly reference your custom matcher as well.
|
52
52
|
|
53
53
|
Here's an example config file:
|
54
54
|
|
55
55
|
```yaml
|
56
56
|
---
|
57
57
|
custom_matchers:
|
58
|
-
-
|
58
|
+
- id: test_description
|
59
|
+
description: "Test Description"
|
59
60
|
regex: "secret.*"
|
60
61
|
|
61
62
|
ignored_paths:
|
@@ -80,8 +81,27 @@ The list of current default matchers are located in `lib/sanctify/matcher_list.
|
|
80
81
|
]
|
81
82
|
```
|
82
83
|
|
84
|
+
If you'd like to disable certain matchers from the default list, you can do so by adding the `id` of the matcher you'd like to disable to a list in the config called `disabled_matchers`. For example, if you wanted to disable all default matchers and only use your custom matchers, you can add the following to the config:
|
85
|
+
|
86
|
+
```yaml
|
87
|
+
disabled_matchers:
|
88
|
+
- aws_access_key_id
|
89
|
+
- aws_secret_key
|
90
|
+
- ssh_rsa_private_key
|
91
|
+
- x509_certificate
|
92
|
+
- redis_url_with_password
|
93
|
+
- url_basic_auth
|
94
|
+
- google_access_token
|
95
|
+
- google_api
|
96
|
+
- slack_api
|
97
|
+
- slack_bot
|
98
|
+
- gem_fury_v1
|
99
|
+
- gem_fury_v2
|
100
|
+
```
|
101
|
+
|
83
102
|
If you see any problem with a default matcher list or would like to add another to the default list, please feel free to make a pull request.
|
84
103
|
|
104
|
+
|
85
105
|
## Troubleshooting
|
86
106
|
|
87
107
|
- If you are facing an issue with integration with a Rail project, where you are using rbenv, and get the following error:
|
data/lib/sanctify/cli.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sanctify
|
2
|
+
class Matcher
|
3
|
+
attr_reader :id, :description, :regex
|
4
|
+
def initialize(id, description, regex, disabled: false)
|
5
|
+
@id = id
|
6
|
+
@description = description
|
7
|
+
@regex = regex
|
8
|
+
@disabled = disabled
|
9
|
+
end
|
10
|
+
|
11
|
+
def disabled?
|
12
|
+
@disabled
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,12 +1,23 @@
|
|
1
|
+
require 'sanctify/matcher'
|
2
|
+
|
1
3
|
module Sanctify
|
2
4
|
class ParserError < StandardError; end
|
3
5
|
class MatcherList
|
4
|
-
def initialize
|
5
|
-
|
6
|
+
def initialize(custom_matchers:, disabled_matchers:)
|
7
|
+
# initialize Array in case users have that field blank
|
8
|
+
@disabled_matchers = disabled_matchers || []
|
9
|
+
@custom_matchers = custom_matchers || []
|
10
|
+
|
11
|
+
# Create Matcher objects out of const.
|
12
|
+
@matchers = DEFAULT_MATCHERS.map do |obj|
|
13
|
+
disabled = @disabled_matchers.include?(obj[:id])
|
14
|
+
Matcher.new(obj[:id], obj[:description], obj[:regex], disabled: disabled)
|
15
|
+
end
|
16
|
+
initialize_custom_matchers!
|
6
17
|
end
|
7
18
|
|
8
|
-
def add(
|
9
|
-
if
|
19
|
+
def add(id, description, regex)
|
20
|
+
if description.empty?
|
10
21
|
raise ParserError, "Description must exist and be greater length than zero"
|
11
22
|
end
|
12
23
|
|
@@ -14,60 +25,87 @@ module Sanctify
|
|
14
25
|
raise ParserError, "Regex must be of type Regexp"
|
15
26
|
end
|
16
27
|
|
17
|
-
@matchers <<
|
18
|
-
@matchers
|
28
|
+
@matchers << Matcher.new(id, description, regex)
|
19
29
|
end
|
20
30
|
|
21
31
|
def each(&blk)
|
22
32
|
@matchers.each &blk
|
23
33
|
end
|
24
34
|
|
35
|
+
def initialize_custom_matchers!
|
36
|
+
if @custom_matchers.any?
|
37
|
+
@custom_matchers.each do |cust|
|
38
|
+
if cust['description'] && cust['regex']
|
39
|
+
add(cust['id'], cust['description'], Regexp.new(cust['regex']))
|
40
|
+
else
|
41
|
+
raise ParserError, "Improperly configured custom matcher: #{cust}. Must include 'description' and 'regex'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
25
47
|
DEFAULT_MATCHERS = [
|
26
48
|
{
|
49
|
+
id: "aws_access_key_id",
|
27
50
|
description: "AWS Access Key ID",
|
28
51
|
regex: /AKIA[0-9A-Z]{16}/
|
29
52
|
},
|
30
53
|
{
|
54
|
+
id: "aws_secret_key",
|
31
55
|
description: "AWS Secret Key",
|
32
|
-
|
56
|
+
# NOTE: This regex does not match keys that include a /, which is allowed
|
57
|
+
# in base64. If we added the slash, there would be many false positives
|
58
|
+
# for paths that are exactly 40 characters. PRs welcome if you can figure
|
59
|
+
# out a regex that will match base64 but not paths.
|
60
|
+
regex: /\b(?<![A-Za-z0-9\/+=])(?=.*[&?=-@#$%\\^+])[A-Za-z0-9\/+=]{40}(?![A-Za-z0-9\/+=])\b/
|
33
61
|
},
|
34
62
|
{
|
63
|
+
id: "ssh_rsa_private_key",
|
35
64
|
description: "SSH RSA Private Key",
|
36
65
|
regex: /^-----BEGIN RSA PRIVATE KEY-----$/
|
37
66
|
},
|
38
67
|
{
|
68
|
+
id: "x509_certificate",
|
39
69
|
description: "X.509 Certificate",
|
40
70
|
regex: /^-----BEGIN CERTIFICATE-----$/
|
41
71
|
},
|
42
72
|
{
|
73
|
+
id: "redis_url_with_password",
|
43
74
|
description: "Redis URL with Password",
|
44
75
|
regex: /redis:\/\/[0-9a-zA-Z:@.\\-]+/
|
45
76
|
},
|
46
77
|
{
|
78
|
+
id: "url_basic_auth",
|
47
79
|
description: "URL Basic auth",
|
48
80
|
regex: /https?:\/\/[0-9a-zA-z_]+?:[0-9a-zA-z_]+?@.+?/
|
49
81
|
},
|
50
82
|
{
|
83
|
+
id: "google_access_token",
|
51
84
|
description:"Google Access Token",
|
52
85
|
regex: /ya29.[0-9a-zA-Z_\\-]{68}/
|
53
86
|
},
|
54
87
|
{
|
88
|
+
id: "google_api",
|
55
89
|
description: "Google API",
|
56
90
|
regex: /AIzaSy[0-9a-zA-Z_\\-]{33}/
|
57
91
|
},
|
58
92
|
{
|
93
|
+
id: "slack_api",
|
59
94
|
description: "Slack API",
|
60
95
|
regex: /xoxp-\\d+-\\d+-\\d+-[0-9a-f]+/
|
61
96
|
},
|
62
97
|
{
|
98
|
+
id: "slack_bot",
|
63
99
|
description: "Slack Bot",
|
64
100
|
regex: /xoxb-\\d+-[0-9a-zA-Z]+/
|
65
101
|
},
|
66
102
|
{
|
103
|
+
id: "gem_fury_v1",
|
67
104
|
description: "Gem Fury v1",
|
68
105
|
regex: /https?:\/\/[0-9a-zA-Z]+@[a-z]+\\.(gemfury.com|fury.io)(\/[a-z]+)?/
|
69
106
|
},
|
70
107
|
{
|
108
|
+
id: "gem_fury_v2",
|
71
109
|
description: "Gem Fury v2",
|
72
110
|
regex: /https?:\/\/[a-z]+\\.(gemfury.com|fury.io)\/[0-9a-zA-Z]{20}/
|
73
111
|
}
|
data/lib/sanctify/repo.rb
CHANGED
@@ -2,13 +2,14 @@ require 'git'
|
|
2
2
|
|
3
3
|
module Sanctify
|
4
4
|
class Repo
|
5
|
-
attr_reader :path, :git
|
6
|
-
def initialize(args, ignored_paths
|
5
|
+
attr_reader :path, :git
|
6
|
+
def initialize(args, ignored_paths: [])
|
7
7
|
@path = args[:repo]
|
8
8
|
@to = args[:to] # The default for `to` in git.diff is nil
|
9
9
|
@from = args[:from] || 'HEAD'
|
10
10
|
@git = Git.open(path)
|
11
|
-
|
11
|
+
ignored_paths ||= []
|
12
|
+
@ignored_paths = ignored_paths.map { |pattern| Regexp.new(pattern) }
|
12
13
|
end
|
13
14
|
|
14
15
|
def diff
|
@@ -36,8 +37,8 @@ module Sanctify
|
|
36
37
|
def should_ignore?(path)
|
37
38
|
# Add pattern matching for filenames so users can ignore files that
|
38
39
|
# they know contain secrets that they have accepted as false positive.
|
39
|
-
return false if ignored_paths.empty?
|
40
|
-
ignored_paths.each do |regex|
|
40
|
+
return false if @ignored_paths.empty?
|
41
|
+
@ignored_paths.each do |regex|
|
41
42
|
return true if regex.match(path)
|
42
43
|
end
|
43
44
|
false
|
data/lib/sanctify/scanner.rb
CHANGED
@@ -4,43 +4,30 @@ require 'sanctify/repo'
|
|
4
4
|
module Sanctify
|
5
5
|
class ScannerError < StandardError; end
|
6
6
|
class Scanner
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :repo, :matcher_list
|
8
8
|
def initialize(args)
|
9
|
-
|
10
|
-
@repo = Repo.new(args, ignored_paths)
|
11
|
-
@matcher_list = MatcherList.new
|
9
|
+
config = args[:config] || {}
|
10
|
+
@repo = Repo.new(args, ignored_paths: config['ignored_paths'])
|
11
|
+
@matcher_list = MatcherList.new(
|
12
|
+
custom_matchers: config['custom_matchers'],
|
13
|
+
disabled_matchers: config['disabled_matchers'])
|
12
14
|
end
|
13
15
|
|
14
16
|
def run
|
15
|
-
initialize_custom_matchers!
|
16
17
|
repo.added_lines.each do |line, path|
|
17
18
|
matcher_list.each do |matcher|
|
18
|
-
if matcher
|
19
|
-
|
19
|
+
next if matcher.disabled?
|
20
|
+
if matcher.regex.match(line)
|
21
|
+
raise ScannerError, message(matcher, line, path)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
23
|
-
puts "SUCCESS! No Secrets Found in #{repo.path}"
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
27
28
|
|
28
|
-
def
|
29
|
-
|
30
|
-
patterns.map { |patt| Regexp.new patt }
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize_custom_matchers!
|
34
|
-
custom_matchers = config['custom_matchers'] || []
|
35
|
-
if custom_matchers.any?
|
36
|
-
custom_matchers.each do |cust|
|
37
|
-
if cust['description'] && cust['regex']
|
38
|
-
matcher_list.add(desc: cust['description'], regex: Regexp.new(cust['regex']))
|
39
|
-
else
|
40
|
-
raise ScannerError, "Improperly configured custom matcher: #{cust}. Must include 'description' and 'regex'"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
29
|
+
def message(matcher, line, path)
|
30
|
+
"[ERROR] SECRET FOUND (#{matcher.description}): #{line} : #{path}"
|
44
31
|
end
|
45
32
|
end
|
46
33
|
end
|
data/lib/sanctify/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanctify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Canty
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/sanctify.rb
|
106
106
|
- lib/sanctify/cli.rb
|
107
107
|
- lib/sanctify/console.rb
|
108
|
+
- lib/sanctify/matcher.rb
|
108
109
|
- lib/sanctify/matcher_list.rb
|
109
110
|
- lib/sanctify/repo.rb
|
110
111
|
- lib/sanctify/scanner.rb
|
@@ -131,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
132
|
version: '0'
|
132
133
|
requirements: []
|
133
134
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.
|
135
|
+
rubygems_version: 2.6.14
|
135
136
|
signing_key:
|
136
137
|
specification_version: 4
|
137
138
|
summary: Keep secrets out of your Git repo with Sanctify
|