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 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