sanctify 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|