Filtering sensitive data with VCR - ruby

I'm using VCR gem to record http interactions and replay them in future. I want to filter-out my actual password value in the uri request. Here's sample of what the uri looks like:
http://services.somesite.com/Services.asmx/Cabins
Username=long&Password=john&StartDate=03%2F22%2F2012&EndDate=03%2F29%2F2012
Though an explanation is provided here, I'm still not sure how to do it after a few attempts myself:
https://www.relishapp.com/myronmarston/vcr/v/2-0-0/docs/configuration/filter-sensitive-data
Any help would be appreciated.

VCR.configure do |c|
c.filter_sensitive_data("<SOMESITE_PASSWORD>") do
ENV['SOMESITE_PASSWORD']
# or $credentials['somesite']['password'] or whatever
end
end
Essentially, you give VCR a bit of placeholder text, and then the block needs to return the real password, reading it from whatever the canonical password "repository" is.
Note that the real password is only needed the first time, when the request is recorded; on subsequent runs, it can be a fake password (as long as it's the same fake password used by the code making the request).

for rails 4+, if you are using secrets.yml you might want to do
VCR.configure do |config|
Rails.application.secrets.each do |k,v|
config.filter_sensitive_data("ENV[#{k}]") { v }
end
end
now you're sure not to forget any

Related

Unable to read cookie in Rspec 3 via last_response

I am trying to read in Rspec 3.1 a cookie received after get call.
I see it is returned but the last_response.cookies doesn't exist.
How can I read response's cookie?
it "doesn't signs in" do
get '/ui/pages/Home'
puts last_response.cookies
end
I know it has been a while, but facing exactly this same issue now, after some struggle, I've found an article here that shares an interesting approach. As I also couldn't find any native parsed method for this, that has worked fine for me.
Basically, place this piece of code below on your spec/spec_helper.rb:
def cookies_from_response(response=last_response)
Hash[response["Set-Cookie"].lines.map { |line|
cookie = Rack::Test::Cookie.new(line.chomp)
[cookie.name, cookie]
}]
end
and you could use this to see the parsed hash:
puts cookies_from_response
For a cookie's value check, you could then use something like:
# Given your cookie name is 'foo' and the content is 'bar'
expect(cookies['foo'].value).to eq 'bar'
Hopefully this becomes helpful to others facing similar issues.

How to record/playback in different cassettes split by host?

In the project I am working on, we use VCR to store cassettes for both local and external services. The local ones are micro services that are constantly modified while the external ones are hardly modified.
Due this reason and plus that the external services takes a long time to be re-record, makes sense for us re-record just the local cassettes most of the time.
In order to solve that we tried to separate the cassettes in different folders (cassettes/localhost and cassettes/external/sample.com).
Then we came up with:
VCR.configure do |config|
config.around_http_request do |request|
host = URI(request.uri).host
vcr_name = VCR.current_cassette.name
folder = host
folder = "external/#{folder}" if host != 'localhost'
VCR.use_cassette("#{folder}/#{vcr_name}", &request)
end
[...]
end
But the problem is that we have some tests that need to make repeated requests (exactly the same request) where the server returns different results. So, using the code above makes the cassettes be reset for each http call. The first request is recorded, and the second is a playback of the first, even if the response was expected to be different.
Then we tried a different approach using tags and nested cassettes:
RSpec.configure do |config|
config.around(:each) do |spec|
name = spec.metadata[:full_description]
VCR.use_cassette "external/#{name}", tag: :external do
VCR.use_cassette "local/#{name}", tag: :internal do
spec.call
end
end
end
[...]
end
VCR.configure do |config|
config.before_record(:external) do |i|
i.ignore! if URI(i.request.uri).host == 'localhost'
end
config.before_record(:internal) do |i|
i.ignore! if URI(i.request.uri).host != 'localhost'
end
[...]
end
But also this doesn't work. The outcome was that all localhost requests were recorded on the internal cassette. The rest of the requests were ignored by VCR.
So do you have any suggestion to solve this?
Then we tried a different approach using tags and nested cassettes...But also this doesn't work.
Yeah, I didn't design cassette nesting with this kind of use case in mind. HTTP interactions are always recorded to the innermost cassette, but can be played back from any level of nesting (it tries the innermost cassette first, then searches up the parent chain). The main use case I had in mind for nesting cassettes was for cucumber: you may want to use a single cassette for an entire scenario, but then you may want to use a particular cassette for an individual step definition (i.e. for any scenario that uses that step). The inner cassette "takes over" while it is in use, but the outer cassette is still there to be available for when the inner cassette is ejected.
This is an interesting use case, though...if you think VCR should work that way, please consider opening a github issue for it and we can discuss it more.
As for your original question: I think #operand's answer will work.
I think you want to look into using the :match_requests_on setting. Read the docs here: https://www.relishapp.com/myronmarston/vcr/v/2-3-0/docs/request-matching
This should allow you to record multiple requests to the same URL but have them replayed in sequence.
In addition to that, I think your method of splitting up the cassettes into different directories sounds good. One thing I've done in the past to force re-recording of specific cassettes, is to just delete the cassettes themselves before rerunning the specs. In your case that should be easy since you've separated them nicely.
Instead of or in addition to that, you could possibly use the :all record setting when you know it's a local request, and patch that into your configure block. Something like:
VCR.configure do |config|
config.around_http_request do |request|
host = URI(request.uri).host
vcr_name = VCR.current_cassette.name
folder = host
if host != 'localhost'
folder = "external/#{folder}"
record_mode = :once
else
record_mode = :all
end
VCR.use_cassette("#{folder}/#{vcr_name}", :record => record_mode, &request)
end
[...]
end
Note, I haven't tested this, so please double check me on that. Of course, you'd also want to not use the :all record setting when you just want to play things back. Maybe you can develop a switch somehow when you invoke the tests.
It's not a complete answer, but I hope this helps.

Ruby tweetstream stop unexpectedly

I use tweetstream gem to get sample tweets from Twitter Streaming API:
TweetStream.configure do |config|
config.username = 'my_username'
config.password = 'my_password'
config.auth_method = :basic
end
#client = TweetStream::Client.new
#client.sample do |status|
puts "#{status.text}"
end
However, this script will stop printing out tweets after about 100 tweets (the script continues to run). What could be the problem?
The Twitter Search API sets certain arbitrary (from the outside) limits for things, from the docs:
GET statuses/:id/retweeted_by Show user objects of up to 100 members who retweeted the status.
From the gem, the code for the method is:
# Returns a random sample of all public statuses. The default access level
# provides a small proportion of the Firehose. The "Gardenhose" access
# level provides a proportion more suitable for data mining and
# research applications that desire a larger proportion to be statistically
# significant sample.
def sample(query_parameters = {}, &block)
start('statuses/sample', query_parameters, &block)
end
I checked the API docs but don't see an entry for 'statuses/sample', but looking at the one above I'm assuming you've reached 100 of whatever statuses/xxx has been accessed.
Also, correct me if I'm wrong, but I believe Twitter no longer accepts basic auth and you must use an OAuth key. If this is so, then that means you're unauthenticated, and the search API will also limit you in other ways too, see https://dev.twitter.com/docs/rate-limiting
Hope that helps.
Ok, I made a mistake there, I was looking at the search API when I should've been looking at the streaming API (my apologies), but it's possible some of the things I was talking about could be the cause of your problems so I'll leave it up. Twitter definitely has moved away from basic auth, so I'd try resolving that first, see:
https://dev.twitter.com/docs/auth/oauth/faq

Get absolute (base) url in sinatra

Right now, I do a
get '/' do
set :base_url, "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}"
# ...
haml :index
end
to be able to use options.base_url in the HAML index.haml.
But I am sure there is a far better, DRY, way of doing this. Yet I cannot see, nor find it. (I am new to Sinatra :))
Somehow, outside of get, I don't have request.env available, or so it seems. So putting it in an include did not work.
How do you get your base url?
You can get it using request.base_url too =D (take a look at rack/request.rb)
A couple things.
set is a class level method, which means you are modifying the whole app's state with each request
The above is a problem because potentially, the base url could be different on different requests eg http://foo.com and https://foo.com or if you have multiple domains pointed at the same app server using DNS
A better tactic might be to define a helper
helpers do
def base_url
#base_url ||= "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}"
end
end
If you need the base url outside of responding to queries(not in a get/post/put/delete block or a view), it would be better to set it manually somewhere.

Generate an HTTP response in Ruby

I'm working on an application that reaches out to a web service. I'd like to develop a proxy class that returns a fake response from the service, so I don't have to constantly be hitting it with requests while I'm developing/testing other parts of the app.
My application is expecting a response generated via Net::HTTP.
response = Net::HTTP.get(URI.parse('http://foo.com'))
case response
when Net::HTTPOK
# do something fun
when Net::HTTPUnauthorized
# you get the idea
How can I manufacture a response object, give it all the right headers, return a body string, etc?
response = ProxyClass.response_object
case response
when Net::HTTPOk
# my app doesn't know it's being lied to
Thanks.
It's actually not that hard to roll your own fake responses directly with Net::HTTP. Here's a simple 200 OK with a cookie header:
def fake_response
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
net_http_resp.add_field 'Set-Cookie', 'Monster'
RestClient::Response.create("Body goes here", net_http_resp, nil)
end
Since few of us are using raw Net::HTTP anymore, the (optional) last line wraps it up as a RestClient::Response, which can then be stubbed into RestClient:
stub(RestClient).post(anything) { fake_response }
I would start with FakeWeb and see if that meets your needs. If it doesn't you can probably gut whatever you need out of the internals and create your own solution.
I know this post is old, but instead of FakeWeb which seems to be largely dead, try webmock. It seems to be more full-featured and very active.
I would look into a mocking library like mocha.
Then you should be able to setup a mock object to help test:
Then following example is from Tim Stephenson's RaddOnline blog, which also includes a more complete tutorial:
def setup
#http_mock = mock('Net::HTTPResponse')
#http_mock .stubs(:code => '200', :message => "OK", :content_type => > "text/html", :body => '<title>Test</title><body>Body of the page</body>')
end
For testing a web service client, we use Sinatra, a lovely little lightweight web framework that lets you get something up and running very quickly and easily. Check out the home page; it has an entire Hello World app in 5 lines of code, and two commands to install and run the whole thing.
I ended up using a Struct.
FakeHttpResponse = Struct.new(:status, :body)
http = FakeHttpResponse.new('success', 'body goes here')
http['status'] # = 'success'
http.body # = 'body goes here'
The drawback is that .status and ['body'] are also valid, but I don't think that matters much.
I would either use FakeWeb as mentioned above, or have my rake test task start a Webrick instance to a little sinatra app which mocks the various test responses you're hoping to see.
You could look into using Rack for this which should allow you to do everything you need.

Resources