cocoapods-whitelist 0.0.10 → 0.2.1

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
- SHA1:
3
- metadata.gz: b5e6eca5022d389d3b4365d67027985c72e8a5e5
4
- data.tar.gz: 6c81391a25a039e470609b4f9c5dbbaf82fa325d
2
+ SHA256:
3
+ metadata.gz: 8db09e9db31c86562ece1491930f2adccc7ed6db5297f63bfbfdfa59ec0eccbb
4
+ data.tar.gz: 5df5e48ef9a8a3ed4e5bb870639cd91ed6e26d2c45409eb5ae83e47bfbdcb3a4
5
5
  SHA512:
6
- metadata.gz: 22e7d9d0427233fb8f3685422f6ba6839beed969c9de482bb98de6c77ba28cb29ef2f6338fedc8a185642bb6e3e01ce55aefee7c5570bdf4c5c21c4ef88c29e1
7
- data.tar.gz: 00c0638eca55e951f4c31820003dd9d2adf75c300a866410e318229c18eef047952289bfeb6b51f6487e793fff0d498d44e43dab39a2324bd2dec72f69a28e5e
6
+ metadata.gz: caa0386ed5f8cfd44e1fadbce177bcdd941eb144b62a9bf4f772a38895e18c98fe6443b8bf116ae007fc97263313521000849256852c5d2ff902d7fc7be71443
7
+ data.tar.gz: 4547337346ebf721b9814d740772a0a12ba3a86fb75fd461c43293c77a1f29cd103909e66d446fc99412c24e1b854b7c80d07eecfc4b235adc20574923574b03
@@ -0,0 +1,30 @@
1
+ version: 2
2
+ jobs:
3
+ build:
4
+ docker:
5
+ - image: circleci/ruby:2.4.4-node
6
+ environment:
7
+ BUNDLER_VERSION: 2.0.2
8
+ resource_class: small
9
+ steps:
10
+ - checkout
11
+ - add_ssh_keys
12
+ - run:
13
+ name: Update Bundler
14
+ command: |
15
+ sudo gem update --system
16
+ sudo gem uninstall bundler
17
+ sudo rm /usr/local/bin/bundle
18
+ sudo rm /usr/local/bin/bundler
19
+ sudo gem install bundler
20
+ - run:
21
+ name: Run Build
22
+ command: |
23
+ bundle install --quiet --without static-dependencies
24
+ gem build cocoapods-whitelist.gemspec --silent -q --backtrace
25
+ gem install *.gem --silent -q --backtrace
26
+ rm -rf *.gem
27
+ rm -rf Gemfile.lock
28
+ - run:
29
+ name: Run Tests
30
+ command: rake
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.2.1
2
+ ### Changed
3
+ - Improve dependency injection message to inform the local source is being used.
4
+
5
+ ## 0.2.0
6
+ ### Changed
7
+ - Avoid using whitelist to validate dependencies source
8
+
9
+ ## 0.1.0
10
+ - Dependency Confusion validation implementation
11
+
12
+ ## 0.0.11
13
+ - Enhancing dependencies linter by checking not only name but also version
14
+ - Enabling CI
15
+
16
+ ## 0.0.10
17
+ - Revert 0.0.8 validation rule
18
+
1
19
  ## 0.0.9
2
20
  - Revert 0.0.8 validation rule
3
21
 
data/Gemfile.lock CHANGED
@@ -1,30 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cocoapods-whitelist (0.0.9)
4
+ cocoapods-whitelist (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- CFPropertyList (3.0.0)
10
- activesupport (4.2.11.1)
11
- i18n (~> 0.7)
9
+ CFPropertyList (3.0.3)
10
+ activesupport (5.2.4.5)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
12
13
  minitest (~> 5.1)
13
- thread_safe (~> 0.3, >= 0.3.4)
14
14
  tzinfo (~> 1.1)
15
+ addressable (2.7.0)
16
+ public_suffix (>= 2.0.2, < 5.0)
17
+ algoliasearch (1.27.5)
18
+ httpclient (~> 2.8, >= 2.8.3)
19
+ json (>= 1.5.1)
15
20
  atomos (0.1.3)
16
21
  bacon (1.2.0)
17
- claide (1.0.2)
18
- cocoapods (1.7.3)
19
- activesupport (>= 4.0.2, < 5)
22
+ claide (1.0.3)
23
+ cocoapods (1.10.1)
24
+ addressable (~> 2.6)
20
25
  claide (>= 1.0.2, < 2.0)
21
- cocoapods-core (= 1.7.3)
26
+ cocoapods-core (= 1.10.1)
22
27
  cocoapods-deintegrate (>= 1.0.3, < 2.0)
23
- cocoapods-downloader (>= 1.2.2, < 2.0)
28
+ cocoapods-downloader (>= 1.4.0, < 2.0)
24
29
  cocoapods-plugins (>= 1.0.0, < 2.0)
25
30
  cocoapods-search (>= 1.0.0, < 2.0)
26
- cocoapods-stats (>= 1.0.0, < 2.0)
27
- cocoapods-trunk (>= 1.3.1, < 2.0)
31
+ cocoapods-trunk (>= 1.4.0, < 2.0)
28
32
  cocoapods-try (>= 1.1.0, < 2.0)
29
33
  colored2 (~> 3.1)
30
34
  escape (~> 0.0.4)
@@ -33,52 +37,63 @@ GEM
33
37
  molinillo (~> 0.6.6)
34
38
  nap (~> 1.0)
35
39
  ruby-macho (~> 1.4)
36
- xcodeproj (>= 1.10.0, < 2.0)
37
- cocoapods-core (1.7.3)
38
- activesupport (>= 4.0.2, < 6)
40
+ xcodeproj (>= 1.19.0, < 2.0)
41
+ cocoapods-core (1.10.1)
42
+ activesupport (> 5.0, < 6)
43
+ addressable (~> 2.6)
44
+ algoliasearch (~> 1.0)
45
+ concurrent-ruby (~> 1.1)
39
46
  fuzzy_match (~> 2.0.4)
40
47
  nap (~> 1.0)
48
+ netrc (~> 0.11)
49
+ public_suffix
50
+ typhoeus (~> 1.0)
41
51
  cocoapods-deintegrate (1.0.4)
42
- cocoapods-downloader (1.2.2)
52
+ cocoapods-downloader (1.4.0)
43
53
  cocoapods-plugins (1.0.0)
44
54
  nap
45
55
  cocoapods-search (1.0.0)
46
- cocoapods-stats (1.1.0)
47
- cocoapods-trunk (1.3.1)
56
+ cocoapods-trunk (1.5.0)
48
57
  nap (>= 0.8, < 2.0)
49
58
  netrc (~> 0.11)
50
- cocoapods-try (1.1.0)
59
+ cocoapods-try (1.2.0)
51
60
  colored2 (3.1.2)
52
- concurrent-ruby (1.1.5)
61
+ concurrent-ruby (1.1.8)
53
62
  escape (0.0.4)
63
+ ethon (0.12.0)
64
+ ffi (>= 1.3.0)
65
+ ffi (1.14.2)
54
66
  fourflusher (2.3.1)
55
67
  fuzzy_match (2.0.4)
56
68
  gh_inspector (1.1.3)
57
- i18n (0.9.5)
69
+ httpclient (2.8.3)
70
+ i18n (1.8.9)
58
71
  concurrent-ruby (~> 1.0)
59
- metaclass (0.0.4)
60
- minitest (5.11.3)
61
- mocha (1.9.0)
62
- metaclass (~> 0.0.1)
72
+ json (2.5.1)
73
+ minitest (5.14.4)
74
+ mocha (1.12.0)
63
75
  mocha-on-bacon (0.2.3)
64
76
  mocha (>= 0.13.0)
65
77
  molinillo (0.6.6)
66
- nanaimo (0.2.6)
78
+ nanaimo (0.3.0)
67
79
  nap (1.1.0)
68
80
  netrc (0.11.0)
69
81
  prettybacon (0.0.2)
70
82
  bacon (~> 1.2)
71
- rake (12.3.2)
83
+ public_suffix (4.0.6)
84
+ rake (12.3.3)
72
85
  ruby-macho (1.4.0)
73
86
  thread_safe (0.3.6)
74
- tzinfo (1.2.5)
87
+ typhoeus (1.4.0)
88
+ ethon (>= 0.9.0)
89
+ tzinfo (1.2.9)
75
90
  thread_safe (~> 0.1)
76
- xcodeproj (1.10.0)
91
+ xcodeproj (1.19.0)
77
92
  CFPropertyList (>= 2.3.3, < 4.0)
78
93
  atomos (~> 0.1.3)
79
94
  claide (>= 1.0.2, < 2.0)
80
95
  colored2 (~> 3.1)
81
- nanaimo (~> 0.2.6)
96
+ nanaimo (~> 0.3.0)
82
97
 
83
98
  PLATFORMS
84
99
  ruby
@@ -94,4 +109,4 @@ DEPENDENCIES
94
109
  rake (~> 12.0)
95
110
 
96
111
  BUNDLED WITH
97
- 2.0.1
112
+ 2.1.4
@@ -0,0 +1,46 @@
1
+ require 'singleton'
2
+ require 'cocoapods-whitelist/model/allowed_dependency'
3
+
4
+ class WhitelistResolver
5
+ include Singleton
6
+ attr_accessor :whitelist
7
+ attr_accessor :whitelist_loaded
8
+ attr_accessor :whitelist_url
9
+
10
+ def config
11
+ @whitelist ||= []
12
+ end
13
+
14
+ def initialize()
15
+ @whitelist_url = DEFAULT_WHITELIST_URL
16
+ load_whitelist()
17
+ end
18
+
19
+ def get_whitelist(whitelist_url = DEFAULT_WHITELIST_URL)
20
+ @whitelist_loaded = @whitelist_url == whitelist_url
21
+ @whitelist_url = whitelist_url
22
+
23
+ load_whitelist() unless @whitelist_loaded
24
+ return @whitelist
25
+ end
26
+
27
+ def load_whitelist
28
+ begin
29
+ open(@whitelist_url) { |io|
30
+ buffer = io.read
31
+ @whitelist = parse_whitelist(buffer)
32
+ @whitelist_loaded = true
33
+ }
34
+ rescue OpenURI::HTTPError => e
35
+ status = e.io.status.join(' ')
36
+ raise "Failed to fetch whitelist from '#{@whitelist_url}'.\n Error: #{status}"
37
+ end
38
+ end
39
+
40
+ def parse_whitelist(raw_whitelist)
41
+ json = JSON.parse(raw_whitelist)
42
+ return json["whitelist"].map { |dependencyJson|
43
+ AllowedDependency.new(dependencyJson["name"], dependencyJson["version"], dependencyJson["expire"], dependencyJson["source"], dependencyJson["target"])
44
+ }
45
+ end
46
+ end
@@ -1,26 +1,10 @@
1
1
  require 'open-uri'
2
+ require_relative '../client/whitelist_resolver'
2
3
 
4
+ POD_NAME_REGEX = /^([^\/]+)(?:\/.*)*$/
5
+ POD_BASE_REGEX_POSITION = 0
3
6
  DEFAULT_WHITELIST_URL = "https://raw.githubusercontent.com/mercadolibre/mobile-dependencies_whitelist/master/ios-whitelist.json"
4
7
 
5
- class AllowedDependency
6
- attr_accessor :name
7
- attr_accessor :version
8
- attr_accessor :expire
9
- def initialize(name, version, expire)
10
- @name = name
11
- @version = version
12
- @expire = expire
13
- end
14
-
15
- def expire?
16
- if @expire != nil
17
- expire = DateTime.parse(@expire,"%Y-%m-%d")
18
- return expire < DateTime.now
19
- end
20
- return false
21
- end
22
- end
23
-
24
8
  module Pod
25
9
  class Command
26
10
  class Whitelist < Command
@@ -57,7 +41,7 @@ module Pod
57
41
 
58
42
  def run
59
43
  prepare_outfile
60
- whitelist = get_whitelist
44
+ whitelist = WhitelistResolver.instance.get_whitelist(@whitelist_url)
61
45
  specifications = get_podspec_specifications
62
46
 
63
47
  if specifications.empty?
@@ -93,19 +77,25 @@ module Pod
93
77
  # Skip subspec dependency
94
78
  next if parentName && name.start_with?("#{parentName}/")
95
79
 
80
+ if versions.length != 1
81
+ not_allowed.push("#{name} (#{versions.join(", ")}) Reason: A specific version must be defined for every dependency (just one). " +
82
+ "Suggestion: find this dependency in your Podspec and add the version listed in the whitelist.")
83
+ next
84
+ end
85
+
96
86
  allowedDependency = whitelist.select { |item|
97
- /#{item.name}/ =~ name && (versions.empty? || !item.version || versions.grep(/#{item.version}/).any?)
87
+ name.start_with?(item.name.match(POD_NAME_REGEX).captures[POD_BASE_REGEX_POSITION]) && (!item.version || versions.grep(/#{item.version}/).any?) && (item.target == 'production')
98
88
  }
99
89
 
100
90
  # Checks if any of the allowed dependencies are expired, if so, fail with error
101
91
  allowedDependency.each { |dependency|
102
92
  if dependency.expire?
103
- not_allowed.push("#{name} (Expired)")
93
+ not_allowed.push("#{name} Reason: Expired version. Please check the whitelist.")
104
94
  end
105
95
  }
106
96
 
107
97
  if allowedDependency.empty?
108
- not_allowed.push("#{name} (#{versions.join(", ")})")
98
+ not_allowed.push("#{name} (#{versions.join(", ")}) Reason: Specified version hasn't match any whitelisted version or Pod name is not valid")
109
99
  next
110
100
  end
111
101
  end
@@ -127,25 +117,6 @@ module Pod
127
117
  end
128
118
  end
129
119
 
130
- def get_whitelist
131
- begin
132
- open(@whitelist_url) { |io|
133
- buffer = io.read
134
- parse_whitelist(buffer)
135
- }
136
- rescue OpenURI::HTTPError => e
137
- status = e.io.status.join(' ')
138
- raise "Failed to fetch whitelist from '#{@whitelist_url}'.\n Error: #{status}"
139
- end
140
- end
141
-
142
- def parse_whitelist(raw_whitelist)
143
- json = JSON.parse(raw_whitelist)
144
- return json["whitelist"].map { |dependencyJson|
145
- AllowedDependency.new(dependencyJson["name"], dependencyJson["version"], dependencyJson["expire"])
146
- }
147
- end
148
-
149
120
  def get_podspec_specifications
150
121
  if @pospec_path
151
122
  return [Pod::Specification.from_file(@pospec_path)]
@@ -1,3 +1,3 @@
1
1
  module CocoapodsWhitelist
2
- VERSION = "0.0.10"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,29 @@
1
+ require_relative '../model/sources'
2
+ require 'singleton'
3
+
4
+ # In charge of host the Pods names that comes from our private sources
5
+ #
6
+ # NOTE: The safest way to handle this was to host the complete specification and not only the pods
7
+ # names as rubydocs specified
8
+ # (https://www.rubydoc.info/github/CocoaPods/Core/Pod/Source#all_specs-instance_method)
9
+ # However, the execution time was considerably slower.
10
+ #
11
+ class SourceHelper
12
+ include Singleton
13
+ attr_accessor :specs
14
+
15
+ def initialize()
16
+ @specs ||= []
17
+ end
18
+
19
+ def setup(sources, private_sources)
20
+ private_sources = sources.select {|s| private_sources.include? s.url}
21
+ private_sources.each do |s|
22
+ @specs.concat s.pods
23
+ end
24
+ end
25
+
26
+ def is_filled
27
+ return !@specs.empty?
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../validator/source_validator'
2
+ require_relative '../helpers/source_helper'
3
+ require_relative '../model/sources'
4
+
5
+ module Pod
6
+ class Resolver
7
+ alias original_search_for search_for
8
+ ## Filter specifications
9
+ def search_for(dependency)
10
+ ## If you have a dependency problem, then no specification is returned from :search_for
11
+ specifications = original_search_for(dependency)
12
+
13
+ valid_specifications = validate_dependency(dependency, specifications)
14
+
15
+ ## If we have removed some specifications due to dependency injection, inform the user once.
16
+ if valid_specifications.size != specifications.size and !informed_di_set.include?(dependency.root_name)
17
+ Pod::UI.puts "WARNING: More than 1 specification for dependency #{dependency.root_name} was found. Using private source.".yellow
18
+ informed_di_set.add(dependency.root_name)
19
+ end
20
+
21
+ valid_specifications
22
+ end
23
+
24
+ private
25
+
26
+ # Returns the valids specifications for a given dependency
27
+ # Params:
28
+ # +dependency+:: dependency to be validated
29
+ # +specifications+:: potencial unsecure specs
30
+ # @returns the result of the validation
31
+ def validate_dependency(dependency, specifications)
32
+ private_sources = get_private_sources()
33
+ SourceHelper.instance.setup(sources, private_sources) unless SourceHelper.instance.is_filled
34
+ validator = SourceValidator.new(SourceHelper.instance.specs, private_sources)
35
+ return validator.filter_dependency(dependency.root_name, specifications)
36
+ end
37
+
38
+ # A set where we save the dependencies which already informed of a dependency injection.
39
+ # @returns a set of dependency_name
40
+ def informed_di_set
41
+ @invalid_specifications_set ||= Set.new
42
+ end
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ require 'cocoapods-whitelist/hook/resolver'
@@ -0,0 +1,23 @@
1
+ class AllowedDependency
2
+ attr_accessor :name
3
+ attr_accessor :version
4
+ attr_accessor :expire
5
+ attr_accessor :source
6
+ attr_accessor :target
7
+
8
+ def initialize(name, version, expire, source, target)
9
+ @name = name
10
+ @version = version
11
+ @expire = expire
12
+ @source = source
13
+ @target = target
14
+ end
15
+
16
+ def expire?
17
+ if @expire != nil
18
+ expire = DateTime.parse(@expire,"%Y-%m-%d")
19
+ return expire < DateTime.now
20
+ end
21
+ return false
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ def get_private_sources
2
+ [ "git@github.com:mercadolibre/mobile-ios_specs.git" ]
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'singleton'
2
+
3
+ class SourceValidator
4
+ attr_accessor :private_specs
5
+ attr_accessor :private_sources
6
+ def initialize(private_specs, private_sources)
7
+ @private_specs = private_specs
8
+ @private_sources = private_sources
9
+ end
10
+
11
+ # Filters the valids specifications for a given pod
12
+ # Params:
13
+ # +pod+:: podname to be validated
14
+ # +specifications+:: potencial unsecure specs
15
+ # @returs valid specs
16
+ def filter_dependency(pod, specifications)
17
+ return specifications.select { |spec| spec_is_valid(pod, spec) }
18
+ end
19
+
20
+ def spec_is_valid(pod, spec)
21
+ # Allow external dependencies (using :git or :path), which create a local podspec
22
+ return true if !spec.defined_in_file.nil? && spec.defined_in_file.to_s.include?('/Pods/Local Podspecs')
23
+
24
+ # Allow every dependency that comes from our privates sources
25
+ return true if @private_sources.include? spec.spec_source.url
26
+
27
+ # NO dependency that comes from a public source should be in our private specs
28
+ return true if !@private_specs.include? spec.name
29
+
30
+ return false
31
+ end
32
+
33
+ end
@@ -1,4 +1,5 @@
1
1
  require 'cocoapods-whitelist/command'
2
+ require 'cocoapods-whitelist/hook'
2
3
 
3
4
  module Whitelist
4
5
  Pod::HooksManager.register('cocoapods-whitelist', :pre_install) do |context, options|
@@ -2,18 +2,37 @@
2
2
  "whitelist": [
3
3
  {
4
4
  "name": "MeliSDK",
5
- "version": "^~>5.[0-9]+$"
6
- }, {
5
+ "version": "^~>5.[0-9]+$",
6
+ "target": "production"
7
+ },
8
+ {
7
9
  "name": "MLRecommendations",
8
- "version": null
9
- }, {
10
- "name": "MLBilling",
11
- "version": null,
12
- "expire" : "2100-11-15"
13
- }, {
14
- "name": "MLOnDemandResources",
15
- "version": "^~>5.[0-9]+$",
16
- "expire" : "2016-11-15"
10
+ "version": null,
11
+ "target": "production"
12
+ },
13
+ {
14
+ "expire": "2100-11-15",
15
+ "name": "MLBilling",
16
+ "version": null,
17
+ "target": "production"
18
+ },
19
+ {
20
+ "expire": "2016-11-15",
21
+ "name": "MLOnDemandResources",
22
+ "version": "^~>5.[0-9]+$",
23
+ "source": "public",
24
+ "target": "production"
25
+ },
26
+ {
27
+ "name": "MLMyPod",
28
+ "version": "^~>1.[0-9]+$",
29
+ "source": "public",
30
+ "target": "production"
31
+ },
32
+ {
33
+ "name": "MLMyDependency",
34
+ "version": "1.0.0|2.0.0",
35
+ "target": "production"
17
36
  }
18
37
  ]
19
38
  }
@@ -0,0 +1,13 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLMyPod"
3
+ s.version = "1.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+
12
+ end
13
+
@@ -0,0 +1,18 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+
12
+ s.subspec "Suggestion" do |suggestion|
13
+ suggestion.resource = "LibraryComponents/Suggestions/assets/*.*", "LibraryComponents/Suggestions/classes/*.xib"
14
+ suggestion.source_files = "LibraryComponents/Suggestions/classes/*.{h,m,c}"
15
+ suggestion.dependency "MeliSDK/Error", "~>5.0"
16
+ end
17
+
18
+ end
@@ -0,0 +1,19 @@
1
+
2
+ Pod::Spec.new do |s|
3
+ s.name = "MLSearch"
4
+ s.version = "3.17.0"
5
+ s.summary = "Componente de search"
6
+ s.homepage = "http://www.mercadolibre.com.ar"
7
+ s.license = "none"
8
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
9
+ s.platform = :ios, "7.0"
10
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
11
+ s.requires_arc = true
12
+
13
+ s.subspec "Suggestion" do |suggestion|
14
+ suggestion.resource = "LibraryComponents/Suggestions/assets/*.*", "LibraryComponents/Suggestions/classes/*.xib"
15
+ suggestion.source_files = "LibraryComponents/Suggestions/classes/*.{h,m,c}"
16
+ suggestion.dependency "MeliSDK", ">5.0", "<6.0"
17
+ end
18
+
19
+ end
@@ -0,0 +1,18 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+
12
+ s.subspec "Suggestion" do |suggestion|
13
+ suggestion.resource = "LibraryComponents/Suggestions/assets/*.*", "LibraryComponents/Suggestions/classes/*.xib"
14
+ suggestion.source_files = "LibraryComponents/Suggestions/classes/*.{h,m,c}"
15
+ suggestion.dependency "MyMeliSDK/Error", "~>5.0"
16
+ end
17
+
18
+ end
@@ -0,0 +1,13 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+ s.dependency 'MercadoPagoSDK', '~>1.0.0'
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+ s.dependency 'MLMyDependency', '1.0.0'
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+ s.dependency 'MLMyDependency', '2.0.0'
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "MLSearch"
3
+ s.version = "3.17.0"
4
+ s.summary = "Componente de search"
5
+ s.homepage = "http://www.mercadolibre.com.ar"
6
+ s.license = "none"
7
+ s.author = { "Mobile team" => "mobile@mercadolibre.com" }
8
+ s.platform = :ios, "7.0"
9
+ s.source = { :git => "git@github.com:mercadolibre/search-ios.git", :tag => s.version.to_s }
10
+ s.requires_arc = true
11
+ s.dependency 'MLMyDependency', '~> 1.0'
12
+
13
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe SourceHelper do
4
+ describe 'behaviour' do
5
+ it 'should group the private specs from the differents private sources' do
6
+
7
+ SourceHelper.instance.specs = [] # Avoid a non-empty SourceHelper
8
+
9
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git", "git@github.com:testable/testable_specs.git" ]
10
+
11
+ private_meli_specs = ["MyMELIPod1", "MyMELIPod2", "MyMELIPod3"]
12
+ meli_mock = mock()
13
+ meli_mock.stubs(:url).returns("git@github.com:mercadolibre/mobile-ios_specs.git")
14
+ meli_mock.stubs(:pods).returns(private_meli_specs)
15
+
16
+ private_cocoapods_cdn_specs = ["MyPublicPod1", "MyPublicPod2", "MyPublicPod2"]
17
+ cocoapods_cdn_mock = mock()
18
+ cocoapods_cdn_mock.stubs(:url).returns("https://cdn.cocoapods.org/")
19
+ cocoapods_cdn_mock.stubs(:pods).returns(private_cocoapods_cdn_specs)
20
+
21
+ private_testable_specs = ["MyTestablePod1", "MyTestablePod2", "MyTestablePod2"]
22
+ testable_mock = mock()
23
+ testable_mock.stubs(:url).returns("git@github.com:testable/testable_specs.git")
24
+ testable_mock.stubs(:pods).returns(private_testable_specs)
25
+
26
+ sources = [meli_mock, cocoapods_cdn_mock, testable_mock]
27
+
28
+ expected_result = [ "MyMELIPod1", "MyMELIPod2", "MyMELIPod3", "MyTestablePod1", "MyTestablePod2", "MyTestablePod2" ]
29
+
30
+ validator = SourceHelper.instance.setup(sources, private_sources)
31
+
32
+ SourceHelper.instance.specs.size.should.equal 6
33
+ SourceHelper.instance.is_filled.should.be.true
34
+ SourceHelper.instance.specs.should.equal expected_result
35
+ end
36
+
37
+ it 'should not be filled if not specs been added' do
38
+
39
+ SourceHelper.instance.specs = [] # Avoid a non-empty SourceHelper
40
+
41
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git", "git@github.com:testable/testable_specs.git" ]
42
+
43
+ private_meli_specs = []
44
+ meli_mock = mock()
45
+ meli_mock.stubs(:url).returns("git@github.com:mercadolibre/mobile-ios_specs.git")
46
+ meli_mock.stubs(:pods).returns(private_meli_specs)
47
+
48
+ private_cocoapods_cdn_specs = []
49
+ cocoapods_cdn_mock = mock()
50
+ cocoapods_cdn_mock.stubs(:url).returns("https://cdn.cocoapods.org/")
51
+ cocoapods_cdn_mock.stubs(:pods).returns(private_cocoapods_cdn_specs)
52
+
53
+ private_testable_specs = []
54
+ testable_mock = mock()
55
+ testable_mock.stubs(:url).returns("git@github.com:testable/testable_specs.git")
56
+ testable_mock.stubs(:pods).returns(private_testable_specs)
57
+
58
+ sources = [meli_mock, cocoapods_cdn_mock, testable_mock]
59
+
60
+ validator = SourceHelper.instance.setup(sources, private_sources)
61
+
62
+ SourceHelper.instance.specs.size.should.equal 0
63
+ SourceHelper.instance.is_filled.should.not.be.true
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,72 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe SourceValidator do
4
+ describe 'functionality' do
5
+ it 'external dependency that comes from a public source should be valid' do
6
+
7
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git" ]
8
+ private_specs = ["MyPod1", "MyPod1", "MyPod3"]
9
+
10
+ stub_url = stub(:url => 'https://cdn.cocoapods.org/')
11
+ spec_mock = mock()
12
+ spec_mock.stubs(:spec_source).returns(stub_url)
13
+ spec_mock.stubs(:defined_in_file).returns(nil?)
14
+ spec_mock.stubs(:name).returns("MyExternalPod")
15
+
16
+ validator = SourceValidator.new(private_specs, private_sources)
17
+ filtered = validator.filter_dependency("MyExternalPod", [spec_mock])
18
+
19
+ filtered.size.should.equal 1
20
+ end
21
+
22
+ it 'internal dependency that comes from a public source should not be valid' do
23
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git" ]
24
+ private_specs = ["MyPod1", "MyPod1", "MyPod3", "MyInternalPod"]
25
+
26
+ stub_url = stub(:url => 'https://cdn.cocoapods.org/')
27
+ spec_mock = mock()
28
+ spec_mock.stubs(:spec_source).returns(stub_url)
29
+ spec_mock.stubs(:defined_in_file).returns(nil?)
30
+ spec_mock.stubs(:name).returns("MyInternalPod")
31
+
32
+ validator = SourceValidator.new(private_specs, private_sources)
33
+ filtered = validator.filter_dependency("MyInternalPod", [spec_mock])
34
+
35
+ filtered.should.empty?
36
+ end
37
+
38
+ it 'internal dependency that comes from private source should be valid' do
39
+
40
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git" ]
41
+ private_specs = ["MyPod1", "MyPod1", "MyPod3", "MyInternalPod"]
42
+
43
+ stub_url = stub(:url => 'git@github.com:mercadolibre/mobile-ios_specs.git')
44
+ spec_mock = mock()
45
+ spec_mock.stubs(:spec_source).returns(stub_url)
46
+ spec_mock.stubs(:defined_in_file).returns(nil?)
47
+ spec_mock.stubs(:name).returns("MyInternalPod")
48
+
49
+ validator = SourceValidator.new(private_specs, private_sources)
50
+ filtered = validator.filter_dependency("MyInternalPod", [spec_mock])
51
+
52
+ filtered.size.should.equal 1
53
+ end
54
+
55
+ it 'development pods should be valid' do
56
+
57
+ private_sources = [ "git@github.com:mercadolibre/mobile-ios_specs.git" ]
58
+ private_specs = ["MyPod1", "MyPod1", "MyPod3", "MyInternalPod"]
59
+
60
+ stub_url = stub(:url => '')
61
+ spec_mock = mock()
62
+ spec_mock.stubs(:spec_source).returns(stub_url)
63
+ spec_mock.stubs(:defined_in_file).returns('./Users/Pods/Local Podspecs/MLMyDevelopmentPod.podspec')
64
+ spec_mock.stubs(:name).returns("MLMyDevelopmentPod")
65
+
66
+ validator = SourceValidator.new(private_specs, private_sources)
67
+ filtered = validator.filter_dependency('MLMyDevelopmentPod', [spec_mock])
68
+
69
+ filtered.size.should.equal 1
70
+ end
71
+ end
72
+ end
data/spec/spec_helper.rb CHANGED
@@ -10,7 +10,7 @@ require 'pretty_bacon'
10
10
  require 'pathname'
11
11
  require 'cocoapods'
12
12
 
13
- Mocha::Configuration.prevent(:stubbing_non_existent_method)
13
+ Mocha.configure { |c| c.stubbing_non_existent_method = :prevent }
14
14
 
15
15
  require 'cocoapods_plugin'
16
16
 
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ WHITELIST_PATH = './spec/mocks/whitelist.json'
4
+
5
+ describe WhitelistResolver do
6
+ describe 'functionality' do
7
+ it 'whitelist should be loaded from an specific url' do
8
+ whitelist = WhitelistResolver.instance.get_whitelist(WHITELIST_PATH)
9
+ whitelist.size.should.equal 6
10
+ end
11
+
12
+ it 'if not URL is specified, whitelist comes from default URL' do
13
+ whitelist = WhitelistResolver.instance.get_whitelist
14
+ whitelist.should.not.empty?
15
+ end
16
+
17
+ it 'whitelist should not be loaded twice' do
18
+ WhitelistResolver.instance.get_whitelist(WHITELIST_PATH)
19
+ loaded = WhitelistResolver.instance.whitelist_loaded
20
+
21
+ loaded.should.be.true
22
+ end
23
+ end
24
+ end
@@ -8,21 +8,15 @@ module Pod
8
8
  it 'registers itself' do
9
9
  Command.parse(%w{ whitelist }).should.be.instance_of Command::Whitelist
10
10
  end
11
-
12
- it 'parse whitelist json' do
13
- command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}"])
14
- dependencies = command.get_whitelist
15
- dependencies.size.should.equal 4
16
- end
17
11
  end
18
12
 
19
13
  describe 'validations' do
20
- it 'dependency without version should be valid' do
14
+ it 'dependency without version should not be valid' do
21
15
  # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('MeliSDK')
22
16
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
23
17
  specification = Pod::Specification.from_file('./spec/mocks/without_version.podspec')
24
18
  command.expects(:get_podspec_specifications).returns([specification])
25
- lambda { command.run }.should.not.raise
19
+ lambda { command.run }.should.raise Informative
26
20
  end
27
21
 
28
22
  it 'dependency with major version fixed should be valid' do
@@ -48,6 +42,14 @@ module Pod
48
42
  lambda { command.run }.should.raise Informative
49
43
  end
50
44
 
45
+ it 'not allowed similar dependency should not be valid' do
46
+ # Whitelist: ('MercadoPagoSDKV4', '~>5.*') | Podspec: ('MercadoPagoSDK')
47
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
48
+ specification = Pod::Specification.from_file('./spec/mocks/with_similar_name_not_allowed.podspec')
49
+ command.expects(:get_podspec_specifications).returns([specification])
50
+ lambda { command.run }.should.raise Informative
51
+ end
52
+
51
53
  it 'dependency with not allowed version should be valid' do
52
54
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
53
55
  specification = Pod::Specification.from_file('./spec/mocks/with_fixed_version.podspec')
@@ -79,6 +81,14 @@ module Pod
79
81
  lambda { command.run }.should.raise Informative
80
82
  end
81
83
 
84
+ it 'dependency with two versions requierement should not be valid' do
85
+ # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('AFNetworking')
86
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
87
+ specification = Pod::Specification.from_file('./spec/mocks/with_more_than_one_version_in_subspec.podspec')
88
+ command.expects(:get_podspec_specifications).returns([specification])
89
+ lambda { command.run }.should.raise Informative
90
+ end
91
+
82
92
  it 'dependency not allowed in subspec should not be valid' do
83
93
  # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('AFNetworking')
84
94
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
@@ -87,6 +97,22 @@ module Pod
87
97
  lambda { command.run }.should.raise Informative
88
98
  end
89
99
 
100
+ it 'subspec dependency allowed in the whitelist should be valid' do
101
+ # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('MeliSDK/Error')
102
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
103
+ specification = Pod::Specification.from_file('./spec/mocks/with_allowed_subspec.podspec')
104
+ command.expects(:get_podspec_specifications).returns([specification])
105
+ lambda { command.run }.should.not.raise
106
+ end
107
+
108
+ it 'subspec dependency not allowed in the whitelist should not be valid' do
109
+ # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('MyMeliSDK/Error')
110
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
111
+ specification = Pod::Specification.from_file('./spec/mocks/with_not_allowed_subspec.podspec')
112
+ command.expects(:get_podspec_specifications).returns([specification])
113
+ lambda { command.run }.should.raise Informative
114
+ end
115
+
90
116
  it 'podspec without dependencies should be valid' do
91
117
  # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('AFNetworking')
92
118
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
@@ -111,6 +137,30 @@ module Pod
111
137
  lambda { command.run }.should.not.raise
112
138
  end
113
139
 
140
+ it 'fixed mayor dependency in whitelist and podspec should not fail on first option' do
141
+ # Whitelist: ('MLMyDependency', '1.0.0|2.0.0') | Podspec: ('MLMyDependency', '1.0.0')
142
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
143
+ specification = Pod::Specification.from_file('./spec/mocks/with_whitelisted_dependency_fixed_versions_v1.podspec')
144
+ command.expects(:get_podspec_specifications).returns([specification])
145
+ lambda { command.run }.should.not.raise
146
+ end
147
+
148
+ it 'fixed mayor dependency in whitelist and podspec should not fail on second option' do
149
+ # Whitelist: ('MLMyDependency', '1.0.0|2.0.0') | Podspec: ('MLMyDependency', '2.0.0')
150
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
151
+ specification = Pod::Specification.from_file('./spec/mocks/with_whitelisted_dependency_fixed_versions_v2.podspec')
152
+ command.expects(:get_podspec_specifications).returns([specification])
153
+ lambda { command.run }.should.not.raise
154
+ end
155
+
156
+ it 'fixed mayor dependency in whitelist but not in podspec should fail' do
157
+ # Whitelist: ('MLMyDependency', '1.0.0|2.0.0') | Podspec: ('MLMyDependency', '~> 1.0')
158
+ command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--fail-on-error"])
159
+ specification = Pod::Specification.from_file('./spec/mocks/with_whitelisted_dependency_fixed_versions_variable.podspec')
160
+ command.expects(:get_podspec_specifications).returns([specification])
161
+ lambda { command.run }.should.raise Informative
162
+ end
163
+
114
164
  it 'not allowed dependency should not raise exception if --fail-on-error is not present' do
115
165
  # Whitelist: ('MeliSDK', '~>5.*') | Podspec: ('AFNetworking')
116
166
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}", "--podspec=./spec/mocks/not_allowed.podspec"])
@@ -121,7 +171,6 @@ module Pod
121
171
  command = Command.parse(['whitelist', "--config=#{WHITELIST_FILE}"])
122
172
  lambda { command.run }.should.not.raise
123
173
  end
124
-
125
174
  end
126
175
  end
127
176
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-whitelist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mobile Team
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-10 00:00:00.000000000 Z
11
+ date: 2021-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -45,6 +45,7 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".circleci/config.yml"
48
49
  - ".gitignore"
49
50
  - CHANGELOG.md
50
51
  - Gemfile
@@ -54,9 +55,16 @@ files:
54
55
  - Rakefile
55
56
  - cocoapods-whitelist.gemspec
56
57
  - lib/cocoapods-whitelist.rb
58
+ - lib/cocoapods-whitelist/client/whitelist_resolver.rb
57
59
  - lib/cocoapods-whitelist/command.rb
58
60
  - lib/cocoapods-whitelist/command/whitelist.rb
59
61
  - lib/cocoapods-whitelist/gem_version.rb
62
+ - lib/cocoapods-whitelist/helpers/source_helper.rb
63
+ - lib/cocoapods-whitelist/hook.rb
64
+ - lib/cocoapods-whitelist/hook/resolver.rb
65
+ - lib/cocoapods-whitelist/model/allowed_dependency.rb
66
+ - lib/cocoapods-whitelist/model/sources.rb
67
+ - lib/cocoapods-whitelist/validator/source_validator.rb
60
68
  - lib/cocoapods_plugin.rb
61
69
  - spec/mocks/bad_name.podspec
62
70
  - spec/mocks/free_version.podspec
@@ -64,21 +72,32 @@ files:
64
72
  - spec/mocks/not_allowed.podspec
65
73
  - spec/mocks/whitelist.json
66
74
  - spec/mocks/whitelist_with_expired_dependencies.json
75
+ - spec/mocks/whitelisted_podname.podspec
67
76
  - spec/mocks/with_allowed_in_subspec.podspec
77
+ - spec/mocks/with_allowed_subspec.podspec
68
78
  - spec/mocks/with_expired_dependencies.podspec
69
79
  - spec/mocks/with_fixed_version.podspec
80
+ - spec/mocks/with_more_than_one_version_in_subspec.podspec
70
81
  - spec/mocks/with_not_allowed_in_subspec.podspec
82
+ - spec/mocks/with_not_allowed_subspec.podspec
71
83
  - spec/mocks/with_not_yet_expired_dependencies.podspec
84
+ - spec/mocks/with_similar_name_not_allowed.podspec
72
85
  - spec/mocks/with_two_requirement.podspec
86
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_v1.podspec
87
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_v2.podspec
88
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_variable.podspec
73
89
  - spec/mocks/without_dependencies.podspec
74
90
  - spec/mocks/without_version.podspec
91
+ - spec/source_helper_spec.rb
92
+ - spec/source_validator_spec.rb
75
93
  - spec/spec_helper.rb
94
+ - spec/whitelist_resolver_spec.rb
76
95
  - spec/whitelist_spec.rb
77
96
  homepage: https://github.com/mercadolibre/mobile-cocoapods_whitelist
78
97
  licenses:
79
98
  - MIT
80
99
  metadata: {}
81
- post_install_message:
100
+ post_install_message:
82
101
  rdoc_options: []
83
102
  require_paths:
84
103
  - lib
@@ -93,9 +112,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
112
  - !ruby/object:Gem::Version
94
113
  version: '0'
95
114
  requirements: []
96
- rubyforge_project:
97
- rubygems_version: 2.6.13
98
- signing_key:
115
+ rubygems_version: 3.0.8
116
+ signing_key:
99
117
  specification_version: 4
100
118
  summary: A longer description of cocoapods-whitelist.
101
119
  test_files:
@@ -105,13 +123,24 @@ test_files:
105
123
  - spec/mocks/not_allowed.podspec
106
124
  - spec/mocks/whitelist.json
107
125
  - spec/mocks/whitelist_with_expired_dependencies.json
126
+ - spec/mocks/whitelisted_podname.podspec
108
127
  - spec/mocks/with_allowed_in_subspec.podspec
128
+ - spec/mocks/with_allowed_subspec.podspec
109
129
  - spec/mocks/with_expired_dependencies.podspec
110
130
  - spec/mocks/with_fixed_version.podspec
131
+ - spec/mocks/with_more_than_one_version_in_subspec.podspec
111
132
  - spec/mocks/with_not_allowed_in_subspec.podspec
133
+ - spec/mocks/with_not_allowed_subspec.podspec
112
134
  - spec/mocks/with_not_yet_expired_dependencies.podspec
135
+ - spec/mocks/with_similar_name_not_allowed.podspec
113
136
  - spec/mocks/with_two_requirement.podspec
137
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_v1.podspec
138
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_v2.podspec
139
+ - spec/mocks/with_whitelisted_dependency_fixed_versions_variable.podspec
114
140
  - spec/mocks/without_dependencies.podspec
115
141
  - spec/mocks/without_version.podspec
142
+ - spec/source_helper_spec.rb
143
+ - spec/source_validator_spec.rb
116
144
  - spec/spec_helper.rb
145
+ - spec/whitelist_resolver_spec.rb
117
146
  - spec/whitelist_spec.rb