ssrfs-up-v2 0.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/ssrfs-up.rb ADDED
@@ -0,0 +1,201 @@
1
+ require "aws-sdk-lambda"
2
+ require "uri"
3
+ require "ssrfs-up/version"
4
+ require "ostruct"
5
+ require "ssrf_filter"
6
+ require "cgi"
7
+
8
+ # Common files
9
+ require "openapi_client/lib/openapi_client/api_client"
10
+ require "openapi_client/lib/openapi_client/api_error"
11
+ require "openapi_client/lib/openapi_client/version"
12
+ require "openapi_client/lib/openapi_client/configuration"
13
+
14
+ # Models
15
+ require "openapi_client/lib/openapi_client/models/content_type"
16
+ require "openapi_client/lib/openapi_client/models/method"
17
+ require "openapi_client/lib/openapi_client/models/redirect"
18
+ require "openapi_client/lib/openapi_client/models/request"
19
+ require "openapi_client/lib/openapi_client/models/response"
20
+ require "openapi_client/lib/openapi_client/models/response_error"
21
+ require "openapi_client/lib/openapi_client/models/response_success"
22
+
23
+ # APIs
24
+ require "openapi_client/lib/openapi_client/api/default_api"
25
+ ##
26
+ # This module contains the AWS lambda client and helper methods to easily
27
+ # make requests to it. All methods take a hostname or URI and a hash or options
28
+ # for the request.
29
+ module SSRFsUp
30
+ class Configuration
31
+ attr_accessor :func_name, :invoke_type, :log_type, :region, :test, :proxy
32
+
33
+ def initialize
34
+ @func_name = "arn:aws:lambda:us-west-2:871040364337:function:sec-czi-sec-ssrfs-up:sec-czi-sec-ssrfs-up"
35
+ @invoke_type = "RequestResponse"
36
+ @log_type = "None"
37
+ @region = "us-west-2"
38
+ @test = false
39
+ @proxy = true
40
+ end
41
+ end
42
+
43
+ class << self
44
+ attr_accessor :config, :client
45
+
46
+ # These methods take a string like "www.google.com" or "https://google.com" and parse
47
+ # the respective parameters from the string to make the request. If only a hostname
48
+ # is provided, the default options are applied. A hash of options can also be
49
+ # supplied to configure the request. The set of options can be found at
50
+ # https://github.com/chanzuckerberg/SSRFs-Up/blob/0e18fd30bee3f2b99ff4bc512cb967b83e8d9dcb/openapi.yaml#L97-L119
51
+ def do(method, host, opts = {})
52
+ case method.downcase
53
+ when "get"
54
+ get(host, opts)
55
+ when "put"
56
+ put(host, opts)
57
+ when "post"
58
+ post(host, opts)
59
+ when "patch"
60
+ patch(host, opts)
61
+ when "delete"
62
+ delete(host, opts)
63
+ end
64
+ end
65
+
66
+ # convenience method for making a GET request with do.
67
+ def get(host, opts = {})
68
+ opts[:method] = "GET"
69
+ invoke(host, opts)
70
+ end
71
+
72
+ # convenience method for making a PUT request with do.
73
+ def put(host, opts = {})
74
+ opts[:method] = "PUT"
75
+ invoke(host, opts)
76
+ end
77
+
78
+ # convenience method for making a POST request with do.
79
+ def post(host, opts = {})
80
+ opts[:method] = "POST"
81
+ invoke(host, opts)
82
+ end
83
+
84
+ # convenience method for making a patch request with do.
85
+ def patch(host, opts = {})
86
+ opts[:method] = "PATCH"
87
+ invoke(host, opts)
88
+ end
89
+
90
+ # convenience method for making a DELETE request with do.
91
+ def delete(host, opts = {})
92
+ opts[:method] = "DELETE"
93
+ invoke(host, opts)
94
+ end
95
+
96
+ # takes an ambiguous string or URI and sets the appropriate options based
97
+ # on if it can be parsed as URI object. If it can't, then the string is assumed
98
+ # to be a hostname only.
99
+ def parseAsUri(uri = "")
100
+ uri = uri.to_s
101
+ opts = { :host => uri.split("/")[0].split("?")[0].split("#")[0] }
102
+ u = URI(uri)
103
+
104
+ # if the scheme was present, we can parse most of the options from the URI.
105
+ # otherwise, we can assume the URI was an actual hostname
106
+ unless u.scheme.nil?
107
+ opts[:secure] = !(u.scheme == "http")
108
+ opts[:host] = u.host
109
+ opts[:path] = u.path unless u.path == ""
110
+ opts[:params] = CGI.parse(u.query) unless u.query.nil?
111
+ end
112
+ opts
113
+ end
114
+
115
+ # converts a hash of options to a valid OpenapiClient Request so that it
116
+ # can be properly consumed by the lambda.
117
+ def toOpenAPIClient(opts = {})
118
+ OpenapiClient::Request.new(opts).to_hash
119
+ end
120
+
121
+ # configures the SSRFsUp module and recreates the AWS Lambda Client from
122
+ # the updated configuration.
123
+ def configure
124
+ yield(configuration)
125
+ @client = Aws::Lambda::Client.new({ region: configuration.region, stub_responses: configuration.test })
126
+ end
127
+
128
+ def configuration
129
+ @config ||= Configuration.new
130
+ end
131
+
132
+ def client
133
+ @client ||= Aws::Lambda::Client.new(region: configuration.region)
134
+ end
135
+
136
+ def fast_check(host, opts)
137
+ scheme = opts[:secure] ? "https://" : "http://"
138
+ path = opts[:path].nil? ? "" : opts[:path]
139
+ params = opts[:params].nil? ? "" : "?" + URI.encode_www_form(opts[:params])
140
+ url = scheme + host + path + params
141
+
142
+ filter_opts = { :max_redirects => opts[:redirect].nil? ? 3 : opts[:redirect] }
143
+ filter_opts[:params] = opts[:params] unless opts[:params].nil?
144
+ filter_opts[:body] = opts[:body] unless opts[:body].nil?
145
+ filter_opts[:headers] = opts[:headers] unless opts[:headers].nil?
146
+
147
+ begin
148
+ case opts[:method].downcase
149
+ when "get"
150
+ resp = SsrfFilter.get(url, filter_opts)
151
+ when "put"
152
+ resp = SsrfFilter.put(url, filter_opts)
153
+ when "post"
154
+ resp = SsrfFilter.post(url, filter_opts)
155
+ when "delete"
156
+ resp = SsrfFilter.delete(url, filter_opts)
157
+ when "patch"
158
+ return { status_code: 404, status_text: "Unsupported method", body: "Cannot use patch with fast path." }
159
+ end
160
+
161
+ { status_code: resp.code.to_i, status_text: resp.message, body: resp.body }
162
+ rescue SsrfFilter::PrivateIPAddress => exception
163
+ { status_code: 404, status_text: "Invalid destination", body: exception.to_s }
164
+ end
165
+ end
166
+
167
+ # invokes the lambda with the provided arguments. It handles all lambda
168
+ # related errors so developers should assume the data they receive back is straight
169
+ # from the server they are speaking to.
170
+ def invoke(host = nil, opts = {})
171
+ opts = opts.merge(parseAsUri(host))
172
+ if (!opts[:proxy].nil? && !opts[:proxy]) || !configuration.proxy
173
+ OpenStruct.new(fast_check(opts[:host], opts))
174
+ else
175
+ begin
176
+ resp = client.invoke({
177
+ function_name: configuration.func_name,
178
+ invocation_type: configuration.invoke_type,
179
+ log_type: configuration.log_type,
180
+ payload: payload(opts),
181
+ })
182
+
183
+ if resp["status_code"] == 200
184
+ OpenStruct.new(JSON.parse(resp&.payload&.string))
185
+ else
186
+ OpenStruct.new({ body: "", status_code: resp[status_code], status_text: "500 Error with proxy" })
187
+ end
188
+ rescue StandardError => e
189
+ # fall back to local check if the lambda wasn't reachable.
190
+ OpenStruct.new(fast_check(opts[:host], opts))
191
+ end
192
+ end
193
+ end
194
+
195
+ # payload builds an API client Request object with the proper defaults and
196
+ # returns its JSON serialization.
197
+ def payload(opts = {})
198
+ toOpenAPIClient(opts).to_json
199
+ end
200
+ end
201
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ssrfs-up-v2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.21.2
5
+ platform: ruby
6
+ authors:
7
+ - Jake Heath
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-lambda
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '1'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1'
33
+ - !ruby/object:Gem::Dependency
34
+ name: ssrf_filter
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pry
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.6'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 3.6.0
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '3.6'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 3.6.0
109
+ description: A gem that simplifies connecting to out AWS Lambda used to proxy requests.
110
+ Make your third-party requests secure by default.
111
+ email:
112
+ - jheath@chanzuckerberg.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - lib/openapi_client/lib/openapi_client.rb
118
+ - lib/openapi_client/lib/openapi_client/api/default_api.rb
119
+ - lib/openapi_client/lib/openapi_client/api_client.rb
120
+ - lib/openapi_client/lib/openapi_client/api_error.rb
121
+ - lib/openapi_client/lib/openapi_client/configuration.rb
122
+ - lib/openapi_client/lib/openapi_client/models/content_type.rb
123
+ - lib/openapi_client/lib/openapi_client/models/method.rb
124
+ - lib/openapi_client/lib/openapi_client/models/redirect.rb
125
+ - lib/openapi_client/lib/openapi_client/models/request.rb
126
+ - lib/openapi_client/lib/openapi_client/models/response.rb
127
+ - lib/openapi_client/lib/openapi_client/models/response_error.rb
128
+ - lib/openapi_client/lib/openapi_client/models/response_success.rb
129
+ - lib/openapi_client/lib/openapi_client/version.rb
130
+ - lib/ssrfs-up.rb
131
+ - lib/ssrfs-up/version.rb
132
+ homepage: https://github.com/chanzuckerberg/ssrf-proxy
133
+ licenses:
134
+ - MIT
135
+ metadata:
136
+ homepage_uri: https://github.com/chanzuckerberg/ssrf-proxy
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.3.0
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.1.6
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Proxy all requests to avoid SSRF.
156
+ test_files: []