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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf59b1007b7caeecd87c0ddd8e7ba24f161d7b09
4
- data.tar.gz: 1761816197a0ff15372dfa777494b1dad5e63189
3
+ metadata.gz: fb2d4bc5fa6962ffe6d54ff7881fe1aa876d4fb1
4
+ data.tar.gz: 5ac1b2ac6cb425f7c72cf55318fad1fee2a87f03
5
5
  SHA512:
6
- metadata.gz: 78b2a2a600785a4937480755ab7a14b615a03e6e9509ad863ea537d44756ad88a9a704dac9a958aca826f4c7735f8a33ed19764e32b44cdff2f84e0700f16d4b
7
- data.tar.gz: 21ed93a8d1c341bf2d8c673fc754e03939e0dabece00f42de0077efaa5bb134a103106f29971a1f22ddcb34b7ea059c66ab83c8080d1b6b42f7e28d7d17569fb
6
+ metadata.gz: 012dcb7a0de9b152fa3e94804926d664ebe206f513615b7cfda835952256fa8c6bb2cc227bb6d80e1916df68b5eabd38b3c06c7b4af4b0a73ad0ce96ac751dc1
7
+ data.tar.gz: 9794b0c234c5bd50a5bee592498fa54998d45d116ad5e534c4f22e49fb0924a470ce02b47699f2b061c355cbb44c5139361ade5435bec8de14cb2411265b7507
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sanctify (0.2.4)
4
+ sanctify (0.3.0)
5
5
  git (~> 1.3)
6
6
 
7
7
  GEM
@@ -40,4 +40,4 @@ DEPENDENCIES
40
40
  sanctify!
41
41
 
42
42
  BUNDLED WITH
43
- 1.16.0
43
+ 1.16.1
data/README.md CHANGED
@@ -48,14 +48,15 @@ repos:
48
48
 
49
49
  ## Configuration
50
50
 
51
- Sanctify supports two top-level objects in the config: `ignored_paths` and `custom_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.
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
- - description: "Test Description"
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:
@@ -42,6 +42,7 @@ module Sanctify
42
42
  end
43
43
  end
44
44
  Scanner.new(args).run
45
+ puts "SUCCESS! No Secrets Found in #{args[:repo]}"
45
46
  end
46
47
  end
47
48
  end
@@ -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
- @matchers = DEFAULT_MATCHERS
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(desc:, regex:)
9
- if desc.length.zero?
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 << { description: desc, regex: regex }
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
- regex: /\b(?<![A-Za-z0-9\/+=])(?=.*[\/&?=-@#$%\\^+])[A-Za-z0-9\/+=]{40}(?![A-Za-z0-9\/+=])\b/
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
  }
@@ -2,13 +2,14 @@ require 'git'
2
2
 
3
3
  module Sanctify
4
4
  class Repo
5
- attr_reader :path, :git, :ignored_paths
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
- @ignored_paths = ignored_paths
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
@@ -4,43 +4,30 @@ require 'sanctify/repo'
4
4
  module Sanctify
5
5
  class ScannerError < StandardError; end
6
6
  class Scanner
7
- attr_reader :config, :repo, :matcher_list
7
+ attr_reader :repo, :matcher_list
8
8
  def initialize(args)
9
- @config = args[:config] || {}
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[:regex].match(line)
19
- raise ScannerError, "[ERROR] SECRET FOUND (#{matcher[:description]}): #{line} : #{path}"
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 ignored_paths
29
- patterns = config['ignored_paths'] || []
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
@@ -1,3 +1,3 @@
1
1
  module Sanctify
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.5
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-16 00:00:00.000000000 Z
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.5.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