Last active
July 19, 2016 06:58
-
-
Save rodionovd/4113ed644a078913c606 to your computer and use it in GitHub Desktop.
Curbside's secrets fetcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 [email protected] with your code |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment