codeclimate-services 1.8.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -3
- data/Gemfile +5 -0
- data/circle.yml +4 -0
- data/codeclimate-services.gemspec +0 -1
- data/lib/cc/fixed_resolv.rb +29 -0
- data/lib/cc/service/http.rb +2 -10
- data/lib/cc/service/safe_webhook.rb +29 -31
- data/lib/cc/services/version.rb +1 -1
- data/spec/cc/service/github_issues_spec.rb +1 -3
- data/spec/cc/service/github_pull_requests_spec.rb +1 -3
- data/spec/cc/service/gitlab_merge_requests_spec.rb +1 -31
- data/spec/cc/service/safe_webhook_spec.rb +23 -43
- data/spec/cc/service_spec.rb +7 -7
- data/spec/spec_helper.rb +6 -4
- data/spec/support/resolv_helpers.rb +10 -0
- metadata +6 -18
- data/spec/support/resolv_helper.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4df2ee48a6422a56813233250049c38979fc3cb8
|
4
|
+
data.tar.gz: 7e58584424589e2c0b2ca337a853a4ea37e7b0fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebb9f1f93b763a3858d6c66283ad1b27ff6507e57612301b71eeaf2077a1a034e808f98e787bbd9d9a9b2091f7f2e0bb397f38b3710c726bcd6907d5e37d51ad
|
7
|
+
data.tar.gz: db694bb54c853bf76dab733883350b38e1c3123706104e3b2a7730ebb41618b65badd9b6c7afc4ccade4ecc26db5997935cd8c5392a052987a06c156f75ab541
|
data/.codeclimate.yml
CHANGED
@@ -10,10 +10,8 @@ engines:
|
|
10
10
|
rubocop:
|
11
11
|
enabled: true
|
12
12
|
exclude_fingerprints:
|
13
|
-
# Using #=== intentionally
|
13
|
+
# Using #=== intentionally to do subnet masking
|
14
14
|
- d1afe90be49c43e85a76bfa58f637804
|
15
|
-
# High complexity in http method due to SafeWebhook check
|
16
|
-
- f05cea2d219c0f8119eb826067a18eda
|
17
15
|
ratings:
|
18
16
|
paths:
|
19
17
|
- "**.rb"
|
data/Gemfile
CHANGED
data/circle.yml
ADDED
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency "activemodel", ">= 3.0"
|
24
24
|
spec.add_dependency "activesupport", ">= 3.0"
|
25
25
|
spec.add_development_dependency "bundler", ">= 1.6.2"
|
26
|
-
spec.add_development_dependency "codeclimate-test-reporter"
|
27
26
|
spec.add_development_dependency "rake"
|
28
27
|
spec.add_development_dependency "rspec"
|
29
28
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "resolv-replace"
|
2
|
+
|
3
|
+
module CC
|
4
|
+
class FixedResolv < Resolv::DNS
|
5
|
+
def self.enable!
|
6
|
+
new.tap do |instance|
|
7
|
+
Resolv::DefaultResolver.replace_resolvers([instance])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@addresses = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def setaddress(name, address)
|
16
|
+
addresses[name] = address
|
17
|
+
end
|
18
|
+
|
19
|
+
def each_address(name)
|
20
|
+
if addresses.key?(name)
|
21
|
+
yield addresses.fetch(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :addresses
|
28
|
+
end
|
29
|
+
end
|
data/lib/cc/service/http.rb
CHANGED
@@ -53,16 +53,13 @@ module CC::Service::HTTP
|
|
53
53
|
def http_method(method, url = nil, body = nil, headers = nil)
|
54
54
|
block = Proc.new if block_given?
|
55
55
|
|
56
|
+
CC::Service::SafeWebhook.ensure_safe!(url)
|
57
|
+
|
56
58
|
http.send(method) do |req|
|
57
59
|
req.url(url) if url
|
58
60
|
req.headers.update(headers) if headers
|
59
61
|
req.body = body if body
|
60
62
|
block.call req if block
|
61
|
-
|
62
|
-
unless allow_internal_webhooks?
|
63
|
-
safe_webhook = CC::Service::SafeWebhook.new(url)
|
64
|
-
safe_webhook.validate!(req)
|
65
|
-
end
|
66
63
|
end
|
67
64
|
end
|
68
65
|
|
@@ -103,9 +100,4 @@ module CC::Service::HTTP
|
|
103
100
|
message: "Success",
|
104
101
|
}
|
105
102
|
end
|
106
|
-
|
107
|
-
def allow_internal_webhooks?
|
108
|
-
var = ENV["CODECLIMATE_ALLOW_INTERNAL_WEBHOOKS"] || ""
|
109
|
-
var == "1" || var == "true"
|
110
|
-
end
|
111
103
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require "ipaddr"
|
2
|
-
require "
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
require "cc/fixed_resolv"
|
3
5
|
|
4
6
|
module CC
|
5
7
|
class Service
|
6
8
|
class SafeWebhook
|
7
|
-
|
9
|
+
InternalWebhookError = Class.new(StandardError)
|
8
10
|
|
9
|
-
# https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
|
10
|
-
# https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses
|
11
11
|
PRIVATE_ADDRESS_SUBNETS = [
|
12
12
|
IPAddr.new("10.0.0.0/8"),
|
13
13
|
IPAddr.new("172.16.0.0/12"),
|
@@ -17,54 +17,52 @@ module CC
|
|
17
17
|
IPAddr.new("0:0:0:0:0:0:0:1"),
|
18
18
|
].freeze
|
19
19
|
|
20
|
+
def self.ensure_safe!(url)
|
21
|
+
instance = new(url)
|
22
|
+
instance.ensure_safe!
|
23
|
+
end
|
24
|
+
|
20
25
|
def self.getaddress(host)
|
21
|
-
@
|
22
|
-
@
|
26
|
+
@dns ||= Resolv::DNS.new
|
27
|
+
@dns.getaddress(host)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.setaddress(host, address)
|
31
|
+
@fixed_resolv ||= CC::FixedResolv.enable!
|
32
|
+
@fixed_resolv.setaddress(host, address)
|
23
33
|
end
|
24
34
|
|
25
35
|
def initialize(url)
|
26
36
|
@url = url
|
27
37
|
end
|
28
38
|
|
29
|
-
|
30
|
-
# anything internal, then alter the request to be for the IP directly with
|
31
|
-
# an explicit Host header given.
|
32
|
-
#
|
33
|
-
# See http://blog.fanout.io/2014/01/27/how-to-safely-invoke-webhooks/#ip-address-blacklisting
|
34
|
-
def validate!(request)
|
39
|
+
def ensure_safe!
|
35
40
|
uri = URI.parse(url)
|
36
|
-
address = self.class.getaddress(uri.host)
|
37
41
|
|
38
|
-
if internal?(
|
39
|
-
|
42
|
+
if !allow_internal_webhooks? && internal?(uri.host)
|
43
|
+
raise InternalWebhookError, "#{url.inspect} maps to an internal address"
|
40
44
|
end
|
41
|
-
|
42
|
-
alter_request(request, uri, address)
|
43
|
-
rescue URI::InvalidURIError, Resolv::ResolvError, Resolv::ResolvTimeout => ex
|
44
|
-
raise_invalid(ex.message)
|
45
45
|
end
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
49
|
attr_reader :url
|
50
50
|
|
51
|
-
def internal?(
|
52
|
-
|
51
|
+
def internal?(host)
|
52
|
+
address = self.class.getaddress(host)
|
53
|
+
|
54
|
+
self.class.setaddress(host, address)
|
53
55
|
|
54
56
|
PRIVATE_ADDRESS_SUBNETS.any? do |subnet|
|
55
|
-
subnet ===
|
57
|
+
subnet === IPAddr.new(address.to_s)
|
56
58
|
end
|
59
|
+
rescue Resolv::ResolvError
|
60
|
+
true # localhost
|
57
61
|
end
|
58
62
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
request.url(address_uri.to_s)
|
63
|
-
request.headers.update(Host: uri.host)
|
64
|
-
end
|
65
|
-
|
66
|
-
def raise_invalid(message)
|
67
|
-
raise InvalidWebhookURL, "The Webhook URL #{url} is invalid: #{message}"
|
63
|
+
def allow_internal_webhooks?
|
64
|
+
var = ENV["CODECLIMATE_ALLOW_INTERNAL_WEBHOOKS"] || ""
|
65
|
+
var == "1" || var == "true"
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
data/lib/cc/services/version.rb
CHANGED
@@ -67,10 +67,8 @@ describe CC::Service::GitHubIssues, type: :service do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
it "different base url" do
|
70
|
-
stub_resolv("example.com", "1.1.1.2")
|
71
|
-
|
72
70
|
http_stubs.post request_url do |env|
|
73
|
-
expect(env[:url].to_s).to eq("http://
|
71
|
+
expect(env[:url].to_s).to eq("http://example.com/#{request_url}")
|
74
72
|
[200, {}, '{"number": 2, "html_url": "http://foo.bar"}']
|
75
73
|
end
|
76
74
|
|
@@ -107,10 +107,8 @@ describe CC::Service::GitHubPullRequests, type: :service do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
it "different base url" do
|
110
|
-
stub_resolv("example.com", "1.1.1.2")
|
111
|
-
|
112
110
|
http_stubs.post("/repos/pbrisbin/foo/statuses/#{"0" * 40}") do |env|
|
113
|
-
expect(env[:url].to_s).to eq("http://
|
111
|
+
expect(env[:url].to_s).to eq("http://example.com/repos/pbrisbin/foo/statuses/#{"0" * 40}")
|
114
112
|
[422, { "x-oauth-scopes" => "gist, user, repo" }, ""]
|
115
113
|
end
|
116
114
|
|
@@ -134,44 +134,14 @@ describe CC::Service::GitlabMergeRequests, type: :service do
|
|
134
134
|
end
|
135
135
|
|
136
136
|
it "different base url" do
|
137
|
-
stub_resolv("gitlab.hal.org", "1.1.1.2")
|
138
|
-
|
139
137
|
http_stubs.post("api/v3/projects/hal%2Fhal9000/statuses/#{"0" * 40}") do |env|
|
140
|
-
expect(env[:url].to_s).to eq("https://
|
138
|
+
expect(env[:url].to_s).to eq("https://gitlab.hal.org/api/v3/projects/hal%2Fhal9000/statuses/#{"0" * 40}")
|
141
139
|
[404, {}, ""]
|
142
140
|
end
|
143
141
|
|
144
142
|
expect(receive_test({ base_url: "https://gitlab.hal.org" }, git_url: "ssh://git@gitlab.com/hal/hal9000.git")[:ok]).to eq(true)
|
145
143
|
end
|
146
144
|
|
147
|
-
context "SafeWebhook" do
|
148
|
-
it "rewrites the request to be for the resolved IP" do
|
149
|
-
stub_resolv("my.gitlab.com", "1.1.1.2")
|
150
|
-
|
151
|
-
http_stubs.post("api/v3/projects/hal%2Fhal9000/statuses/#{"0" * 40}") do |env|
|
152
|
-
expect(env[:url].to_s).to eq("https://1.1.1.2/api/v3/projects/hal%2Fhal9000/statuses/#{"0" * 40}")
|
153
|
-
expect(env[:request_headers][:Host]).to eq("my.gitlab.com")
|
154
|
-
[404, {}, ""]
|
155
|
-
end
|
156
|
-
|
157
|
-
expect(receive_test({ base_url: "https://my.gitlab.com" }, git_url: "ssh://git@gitlab.com/hal/hal9000.git")[:ok]).to eq(true)
|
158
|
-
end
|
159
|
-
|
160
|
-
it "validates that the host doesn't resolve to something internal" do
|
161
|
-
stub_resolv("my.gitlab.com", "127.0.0.1")
|
162
|
-
|
163
|
-
expect do
|
164
|
-
receive_test({ base_url: "https://my.gitlab.com" }, git_url: "")
|
165
|
-
end.to raise_error(CC::Service::SafeWebhook::InvalidWebhookURL)
|
166
|
-
|
167
|
-
stub_resolv("my.gitlab.com", "10.0.0.9")
|
168
|
-
|
169
|
-
expect do
|
170
|
-
receive_test({ base_url: "https://my.gitlab.com" }, git_url: "")
|
171
|
-
end.to raise_error(CC::Service::SafeWebhook::InvalidWebhookURL)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
145
|
private
|
176
146
|
|
177
147
|
def expect_status_update(repo, commit_sha, params)
|
@@ -2,59 +2,39 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
class CC::Service
|
4
4
|
describe SafeWebhook do
|
5
|
-
describe ".
|
6
|
-
it "
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
describe "#validate!" do
|
15
|
-
context "valid webhook URLs" do
|
16
|
-
it "rewrites the request to be safe" do
|
17
|
-
stub_resolv("example.com", "2.2.2.2")
|
18
|
-
|
19
|
-
request = double(headers: double)
|
20
|
-
expect(request).to receive(:url).with("https://2.2.2.2:3000/foo")
|
21
|
-
expect(request.headers).to receive(:update).with(Host: "example.com")
|
22
|
-
|
23
|
-
safe_webhook = SafeWebhook.new("https://example.com:3000/foo")
|
24
|
-
safe_webhook.validate!(request)
|
5
|
+
describe ".ensure_safe!" do
|
6
|
+
it "does not allow internal URLs" do
|
7
|
+
%w[ 127.0.0.1 192.168.0.1 10.0.1.18 ].each do |address|
|
8
|
+
stub_resolv("github.com", address)
|
9
|
+
|
10
|
+
expect do
|
11
|
+
SafeWebhook.ensure_safe!("https://github.com/api/v1/user")
|
12
|
+
end.to raise_error(SafeWebhook::InternalWebhookError)
|
25
13
|
end
|
26
14
|
end
|
27
15
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
expect { validate("http://example.com") }.to raise_error(SafeWebhook::InvalidWebhookURL)
|
33
|
-
end
|
16
|
+
it "allows internal URLs when configured to do so" do
|
17
|
+
allow(ENV).to receive(:[]).
|
18
|
+
with("CODECLIMATE_ALLOW_INTERNAL_WEBHOOKS").
|
19
|
+
and_return("1")
|
34
20
|
|
35
|
-
|
36
|
-
allow(SafeWebhook).to receive(:getaddress).and_raise(Resolv::ResolvError)
|
21
|
+
stub_resolv("github.com", "10.0.1.18")
|
37
22
|
|
38
|
-
|
39
|
-
|
23
|
+
SafeWebhook.ensure_safe!("https://github.com/api/v1/user")
|
24
|
+
end
|
40
25
|
|
41
|
-
|
42
|
-
|
26
|
+
it "allows non-internal URLs" do
|
27
|
+
stub_resolv("github.com", "1.1.1.2")
|
43
28
|
|
44
|
-
|
45
|
-
|
29
|
+
SafeWebhook.ensure_safe!("https://github.com/api/v1/user")
|
30
|
+
end
|
46
31
|
|
47
|
-
|
48
|
-
|
32
|
+
it "ensures future dns queries get the same answer" do
|
33
|
+
stub_resolv("github.com", "1.1.1.3")
|
49
34
|
|
50
|
-
|
51
|
-
end
|
35
|
+
SafeWebhook.ensure_safe!("https://github.com/api/v1/user")
|
52
36
|
|
53
|
-
|
54
|
-
request = double
|
55
|
-
safe_webhook = SafeWebhook.new(url)
|
56
|
-
safe_webhook.validate!(request)
|
57
|
-
end
|
37
|
+
expect(Resolv.getaddress("github.com").to_s).to eq "1.1.1.3"
|
58
38
|
end
|
59
39
|
end
|
60
40
|
end
|
data/spec/cc/service_spec.rb
CHANGED
@@ -32,42 +32,42 @@ describe CC::Service, type: :service do
|
|
32
32
|
it "post success" do
|
33
33
|
stub_http("/my/test/url", [200, {}, '{"ok": true, "thing": "123"}'])
|
34
34
|
|
35
|
-
response = service_post("/my/test/url", { token: "1234" }.to_json, {}) do |inner_response|
|
35
|
+
response = service_post("http://example.com/my/test/url", { token: "1234" }.to_json, {}) do |inner_response|
|
36
36
|
body = JSON.parse(inner_response.body)
|
37
37
|
{ thing: body["thing"] }
|
38
38
|
end
|
39
39
|
|
40
40
|
expect(response[:ok]).to eq(true)
|
41
41
|
expect(response[:params]).to eq('{"token":"1234"}')
|
42
|
-
expect(response[:endpoint_url]).to eq("/my/test/url")
|
42
|
+
expect(response[:endpoint_url]).to eq("http://example.com/my/test/url")
|
43
43
|
expect(response[:status]).to eq(200)
|
44
44
|
end
|
45
45
|
|
46
46
|
it "post redirect success" do
|
47
|
-
stub_http("/my/test/url", [307, { "Location" => "/my/redirect/url" }, '{"ok": false, "redirect": true}'])
|
47
|
+
stub_http("/my/test/url", [307, { "Location" => "http://example.com/my/redirect/url" }, '{"ok": false, "redirect": true}'])
|
48
48
|
stub_http("/my/redirect/url", [200, {}, '{"ok": true, "thing": "123"}'])
|
49
49
|
|
50
|
-
response = service_post_with_redirects("/my/test/url", { token: "1234" }.to_json, {}) do |inner_response|
|
50
|
+
response = service_post_with_redirects("http://example.com/my/test/url", { token: "1234" }.to_json, {}) do |inner_response|
|
51
51
|
body = JSON.parse(inner_response.body)
|
52
52
|
{ thing: body["thing"] }
|
53
53
|
end
|
54
54
|
|
55
55
|
expect(response[:ok]).to eq(true)
|
56
56
|
expect(response[:params]).to eq('{"token":"1234"}')
|
57
|
-
expect(response[:endpoint_url]).to eq("/my/test/url")
|
57
|
+
expect(response[:endpoint_url]).to eq("http://example.com/my/test/url")
|
58
58
|
expect(response[:status]).to eq(200)
|
59
59
|
end
|
60
60
|
|
61
61
|
it "post http failure" do
|
62
62
|
stub_http("/my/wrong/url", [404, {}, ""])
|
63
63
|
|
64
|
-
expect { service_post("/my/wrong/url", { token: "1234" }.to_json, {}) }.to raise_error(CC::Service::HTTPError)
|
64
|
+
expect { service_post("http://example.com/my/wrong/url", { token: "1234" }.to_json, {}) }.to raise_error(CC::Service::HTTPError)
|
65
65
|
end
|
66
66
|
|
67
67
|
it "post some other failure" do
|
68
68
|
stub_http("/my/wrong/url") { raise ArgumentError, "lol" }
|
69
69
|
|
70
|
-
expect { service_post("/my/wrong/url", { token: "1234" }.to_json, {}) }.to raise_error(ArgumentError)
|
70
|
+
expect { service_post("http://example.com/my/wrong/url", { token: "1234" }.to_json, {}) }.to raise_error(ArgumentError)
|
71
71
|
end
|
72
72
|
|
73
73
|
it "services" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
require "
|
2
|
-
|
1
|
+
require "simplecov"
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter "/spec/"
|
4
|
+
end
|
3
5
|
|
4
6
|
cwd = File.expand_path(File.dirname(__FILE__))
|
5
7
|
require "#{cwd}/../config/load"
|
@@ -34,8 +36,8 @@ RSpec.configure do |config|
|
|
34
36
|
# This setting enables warnings. It's recommended, but in some cases may
|
35
37
|
# be too noisy due to issues in dependencies.
|
36
38
|
config.warnings = true
|
37
|
-
|
38
|
-
|
39
|
+
config.before do
|
40
|
+
# Disable actual DNS resolution during specs by default
|
39
41
|
stub_resolv(anything, "1.1.1.1")
|
40
42
|
end
|
41
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codeclimate-services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Helmkamp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -94,20 +94,6 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.6.2
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: codeclimate-test-reporter
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: rake
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,10 +144,12 @@ files:
|
|
158
144
|
- bin/nokogiri
|
159
145
|
- bin/pry
|
160
146
|
- bin/rake
|
147
|
+
- circle.yml
|
161
148
|
- codeclimate-services.gemspec
|
162
149
|
- config/cacert.pem
|
163
150
|
- config/load.rb
|
164
151
|
- lib/axiom/types/password.rb
|
152
|
+
- lib/cc/fixed_resolv.rb
|
165
153
|
- lib/cc/formatters/linked_formatter.rb
|
166
154
|
- lib/cc/formatters/plain_formatter.rb
|
167
155
|
- lib/cc/formatters/snapshot_formatter.rb
|
@@ -225,7 +213,7 @@ files:
|
|
225
213
|
- spec/fixtures.rb
|
226
214
|
- spec/spec_helper.rb
|
227
215
|
- spec/support/fake_logger.rb
|
228
|
-
- spec/support/
|
216
|
+
- spec/support/resolv_helpers.rb
|
229
217
|
- spec/support/service_context.rb
|
230
218
|
homepage: ''
|
231
219
|
licenses:
|
@@ -276,5 +264,5 @@ test_files:
|
|
276
264
|
- spec/fixtures.rb
|
277
265
|
- spec/spec_helper.rb
|
278
266
|
- spec/support/fake_logger.rb
|
279
|
-
- spec/support/
|
267
|
+
- spec/support/resolv_helpers.rb
|
280
268
|
- spec/support/service_context.rb
|