Skip to content

Instantly share code, notes, and snippets.

@rodionovd
Last active July 19, 2016 06:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rodionovd/4113ed644a078913c606 to your computer and use it in GitHub Desktop.
Save rodionovd/4113ed644a078913c606 to your computer and use it in GitHub Desktop.
Curbside's secrets fetcher
#!/usr/bin/ruby
#
# (c) Dmitry Rodionov, 2016
# http://internals.exposed
require 'net/http'
require 'json'
# Sends a GET request to /get-session in order to acquire a new session token.
# Due to the nature of this token -- it's valid for 10 requests only -- we'll
# call this function more than once.
def acquire_session()
endpoint = URI('http://challenge.shopcurbside.com/get-session')
http = Net::HTTP.new(endpoint.host, endpoint.port)
req = Net::HTTP::Get.new(endpoint)
return http.request(req).body
end
# Converts all keys of the given hash to lowercase.
def lowecase_hash(hash)
result = {}
hash.each {|k, v|
result.merge!({k.downcase => v})
}
return result
end
# The whole puzzle is a tree: you receive either a node containing URLs
# of its sibling nodes or a leaf containing a single secret character.
# This function will traverse the tree recursively and fetch all the secrets it
# hides.
def fetch_secrets(node="start", session)
begin
uri = URI("http://challenge.shopcurbside.com/#{node}")
http = Net::HTTP.new(uri.host, uri.port)
req = Net::HTTP::Get.new(uri)
req.add_field("Session", session)
res = http.request(req)
body = JSON.parse(res.body)
# In some nodes there're keys with mixed capitalization
# (e.g. "neXT" vs "nExt" vs "next"), so here's a workaround
body = lowecase_hash(body)
# A session token lasts for only 10 requests, and we have to
# renew it once in a while
if body.key?("error") # TODO(rodionovd): maybe check an actual error msg?
return fetch_secrets(node, acquire_session)
end
# So this's a leaf of the tree and we just pop up a secret value
return body["secret"] if body.key? "secret"
# It's not a leaf, but a regular node, so go down and ask siblings
# for their secrets
siblings = body["next"]
# Caveat: some nodes have only one sibling which is represented as a
# plain string; we wrap it into a single-item array for consistency
siblings = Array(siblings) if siblings.is_a? String
secrets = siblings.map { |buddy|
fetch_secrets(buddy, session)
}.join("")
return secrets
rescue StandardError => e
puts "HTTP Request failed: #{e.message}"
end
end
puts "Fetching the Curbside's secrets. It may take a while, so go grab some coffee ☕️"
secrets = fetch_secrets(acquire_session)
puts "Got it: \"#{secrets}\"!"
# > Apply at systemd@shopcurbside.com with your code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment