Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
bundler-cache: true

- name: Configure RubyGems credentials
uses: rubygems/configure-rubygems-credentials@main
uses: rubygems/configure-rubygems-credentials@v2.1.0
with:
api-token: ${{ secrets.RUBYGEMS_API_TOKEN }}

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
# Zerobounce
Ruby client for Zerobounce.net API. Developed based on [this version](https://github.com/afrase/zerobounce) by Aaron Frase.


## Security

- Keep API keys on a trusted server. Do not embed them in mobile apps or browser JavaScript that untrusted users can inspect.
- Custom API base URLs (when supported) must use `https://`. Do not pass end-user-controlled hosts into those settings.
- Request URLs include `api_key` as a query parameter (ZeroBounce API contract). Do not log full request URLs or enable payload debug logging in production.

## Usage
Add this line to your application's Gemfile:
```ruby
Expand Down
22 changes: 18 additions & 4 deletions lib/zerobounce/base_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def self.__safe_file_path__(filepath)
canonical
end

def self.__require_https__(url)
unless url.to_s.downcase.start_with?('https://')
raise ArgumentError, 'API URL must be an https:// URL'
end
url
end

def self._get(root, path, params, content_type='application/json')

# puts path
Expand All @@ -50,8 +57,9 @@ def self._get(root, path, params, content_type='application/json')

params[:api_key] = Zerobounce.config.apikey
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
Zerobounce::BaseRequest.__require_https__(url)

response = RestClient.get(url, {params: params})
response = RestClient::Request.execute(method: :get, url: url, timeout: 120, headers: { params: params })
return response
end

Expand All @@ -61,16 +69,22 @@ def self._post(root, path, params, content_type='application/json', filepath=nil

params[:api_key] = Zerobounce.config.apikey
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
Zerobounce::BaseRequest.__require_https__(url)
response = nil

if filepath or content_type == 'multipart/form-data'
params[:file] = File.new(Zerobounce::BaseRequest.__safe_file_path__(filepath), 'rb')
params[:multipart] = true
response = RestClient.post(url, params)
response = RestClient::Request.execute(method: :post, url: url, payload: params, timeout: 120)

elsif content_type == 'application/json'
response = RestClient.post(url, params.to_json, \
content_type: :json, accept: :json)
response = RestClient::Request.execute(
method: :post,
url: url,
payload: params.to_json,
timeout: 120,
headers: { content_type: :json, accept: :json }
)
else
# this shouldn't happen
raise Error.new('Unknown content type specified in request.'\
Expand Down
12 changes: 12 additions & 0 deletions spec/base_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
# Test protected helpers via send
let(:strip_trailing_slashes) { described_class.method(:__root_without_trailing_slashes__) }
let(:safe_file_path) { described_class.method(:__safe_file_path__) }
let(:require_https) { described_class.method(:__require_https__) }

describe '.__require_https__' do
it 'allows https URLs' do
expect { require_https.call('https://api.zerobounce.net/v2') }.not_to raise_error
end

it 'rejects http and other schemes' do
expect { require_https.call('http://example.com') }.to raise_error(ArgumentError, /https:\/\//)
expect { require_https.call('file:///etc/passwd') }.to raise_error(ArgumentError, /https:\/\//)
end
end

describe '.__root_without_trailing_slashes__' do
it 'returns the string unchanged when there is no trailing slash' do
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/activity-valid-result.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/api-usage-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/batch-validate-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/credits-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-check-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-delete-incorrect-file-id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://bulkapi.zerobounce.net/v2/deletefile?api_key=99e7ef20ceea4480a173b07b1be75371&file_id=invalid-file-id
uri: https://bulkapi.zerobounce.net/v2/deletefile?api_key=REDACTED&file_id=invalid-file-id
body:
encoding: US-ASCII
string: ''
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-delete-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-get-incorrect-file-id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://bulkapi.zerobounce.net/v2/getfile?api_key=99e7ef20ceea4480a173b07b1be75371&file_id=invalid-file-id
uri: https://bulkapi.zerobounce.net/v2/getfile?api_key=REDACTED&file_id=invalid-file-id
body:
encoding: US-ASCII
string: ''
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-get-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-incorrect-file-id.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/file-validate-send-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-domain-valid-company.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-domain-valid-domain.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-email-valid-company-all-names.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-email-valid-company-first-name.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-email-valid-domain-all-names.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-email-valid-domain-first-last-name.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/find-email-valid-domain-first-name.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/guessformat-valid-domain-all-names.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/guessformat-valid-domain-first-name.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/guessformat-valid-domain-last-name.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/guessformat-valid-domain-no-names.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/scoring-check-incorrect-file-id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://bulkapi.zerobounce.net/v2/scoring/filestatus?api_key=99e7ef20ceea4480a173b07b1be75371&file_id=invalid-file-id
uri: https://bulkapi.zerobounce.net/v2/scoring/filestatus?api_key=REDACTED&file_id=invalid-file-id
body:
encoding: US-ASCII
string: ''
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/scoring-check-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/scoring-delete-incorrect-file-id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://bulkapi.zerobounce.net/v2/scoring/deletefile?api_key=99e7ef20ceea4480a173b07b1be75371&file_id=invalid-file-id
uri: https://bulkapi.zerobounce.net/v2/scoring/deletefile?api_key=REDACTED&file_id=invalid-file-id
body:
encoding: US-ASCII
string: ''
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/scoring-delete-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/scoring-get-incorrect-file-id.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://bulkapi.zerobounce.net/v2/scoring/getfile?api_key=99e7ef20ceea4480a173b07b1be75371&file_id=invalid-file-id
uri: https://bulkapi.zerobounce.net/v2/scoring/getfile?api_key=REDACTED&file_id=invalid-file-id
body:
encoding: US-ASCII
string: ''
Expand Down
2 changes: 1 addition & 1 deletion spec/cassettes/scoring-get-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/scoring-send-valid.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/validate-ip-valid-result.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/cassettes/validate-valid-result.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions spec/mock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :webmock
c.ignore_localhost = true
c.filter_sensitive_data('<API_KEY>') { ENV['ZEROBOUNCE_API_KEY'] }
c.filter_sensitive_data('<API_KEY>') { 'REDACTED' }
# Match requests by method and URI but ignore api_key so cassettes work with any key
c.register_request_matcher :uri_ignoring_api_key do |request1, request2|
return false if request1.method != request2.method
Expand Down