dap 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88bd3d23abc85411de3354902069d7bfae980809
4
- data.tar.gz: 84459c4a10ba66f8c3b716c0f7e8f9bde1dc9c02
3
+ metadata.gz: 1cc592fe131d06b96989baa2072f92ac1c513465
4
+ data.tar.gz: 2550d569f533d19d43c05f7a1e778e111f4c96e2
5
5
  SHA512:
6
- metadata.gz: 40c689fa8281c1dfcd2befea059657caaed9e6fcbaa42247371194d52730227145a5c7c2bc740e56a89f241d97e4d261cba40f4ecf997abbd5fb4a0903793ac6
7
- data.tar.gz: b8a6f3f48509c4ae224a2a0750aefb0d8f4313764fe368fcc94e54604628e962535b94b93688ef8c69b5431875c57618f6f12d2780c7de95592853c10f2f7b26
6
+ metadata.gz: 4a162fc9fb0ae6f9caa0ac9fe1b4c6a3437eccb083d4c6127b334da28906b88584f06ef49ddd3f3a760c844cefe030c90141c6933359e5f44b18b21ba37c1f15
7
+ data.tar.gz: bbecce1d8ae4a2b1edcbc0e89c282fc04066431954a99520a80a910980dab9b552d4346aaf0016039885a5582d0c7c78c93a092be892a09cbd7f12e93d49532d
@@ -134,18 +134,77 @@ end
134
134
  class FilterDecodeHTTPReply
135
135
  include BaseDecoder
136
136
 
137
- # TODO: Decode transfer-chunked responses
138
137
  def decode(data)
139
138
  lines = data.split(/\r?\n/)
140
- resp = lines.shift
139
+ resp = lines.shift
141
140
  save = {}
142
141
  return save if resp !~ /^HTTP\/\d+\.\d+\s+(\d+)(?:\s+(.*))?/
143
142
 
144
143
  save["http_code"] = $1.to_i
145
144
  save["http_message"] = ($2 ? $2.strip : '')
146
145
  save["http_raw_headers"] = {}
146
+ save.merge!(parse_headers(lines))
147
147
 
148
- clen = nil
148
+ head, raw_body = data.split(/\r?\n\r?\n/, 2)
149
+
150
+ # Some buggy systems exclude the header entirely
151
+ raw_body ||= head
152
+
153
+ save["http_raw_body"] = [raw_body].pack("m*").gsub(/\s+/n, "")
154
+ body = raw_body
155
+
156
+ transfer_encoding = save["http_raw_headers"]["transfer-encoding"]
157
+ if transfer_encoding && transfer_encoding.include?("chunked")
158
+ offset = 0
159
+ body = ''
160
+ while (true)
161
+ # read the chunk size from where we currently are. The chunk size will
162
+ # be specified in hex, at the beginning, and is followed by \r\n.
163
+ if /^(?<chunk_size_str>[a-z0-9]+)\r\n/i =~ raw_body.slice(offset, raw_body.size)
164
+ # convert chunk size
165
+ chunk_size = chunk_size_str.to_i(16)
166
+ # advance past this chunk marker and its trailing \r\n
167
+ offset += chunk_size_str.size + 2
168
+ # read this chunk, starting from just past the chunk marker and
169
+ # stopping at the supposed end of the chunk
170
+ body << raw_body.slice(offset, chunk_size)
171
+ # advance the offset to past the end of the chunk and its trailing \r\n
172
+ offset += chunk_size + 2
173
+ else
174
+ break
175
+ end
176
+ end
177
+
178
+ # chunked-encoding allows headers to occur after the chunks, so parse those
179
+ if offset < raw_body.size
180
+ save.merge!(parse_headers(raw_body.slice(offset, raw_body.size).split(/\r?\n/)))
181
+ end
182
+ end
183
+
184
+ content_encoding = save["http_raw_headers"]["content-encoding"]
185
+ if content_encoding && content_encoding.include?("gzip")
186
+ begin
187
+ gunzip = Zlib::GzipReader.new(StringIO.new(body))
188
+ body = gunzip.read.encode('UTF-8', :invalid=>:replace, :replace=>'?')
189
+ gunzip.close()
190
+ rescue
191
+ end
192
+ end
193
+ save["http_body"] = body
194
+
195
+ if body =~ /<title>([^>]+)</mi
196
+ save["http_title"] = $1.strip
197
+ end
198
+
199
+ save
200
+ end
201
+
202
+ def valid_header_name?(name)
203
+ return name !~ /[\x00-\x1f()<>@,;:\\\"\/\[\]?={}\s]/
204
+ end
205
+
206
+ def parse_headers(lines)
207
+ headers = {}
149
208
 
150
209
  while lines.length > 0
151
210
  hline = lines.shift
@@ -154,41 +213,41 @@ class FilterDecodeHTTPReply
154
213
  header_name.downcase!
155
214
 
156
215
  if valid_header_name?(header_name)
157
- save["http_raw_headers"] ||= {}
158
- save["http_raw_headers"][header_name] ||= []
159
- save["http_raw_headers"][header_name] << header_value
216
+ headers["http_raw_headers"] ||= {}
217
+ headers["http_raw_headers"][header_name] ||= []
218
+ headers["http_raw_headers"][header_name] << header_value
160
219
 
161
220
  # XXX: warning, all of these mishandle duplicate headers
162
221
  case header_name
163
222
  when 'etag'
164
- save["http_etag"] = header_value
223
+ headers["http_etag"] = header_value
165
224
 
166
225
  when 'set-cookie'
167
226
  bits = header_value.gsub(/\;?\s*path=.*/i, '').gsub(/\;?\s*expires=.*/i, '').gsub(/\;\s*HttpOnly.*/, '')
168
- save["http_cookie"] = bits
227
+ headers["http_cookie"] = bits
169
228
 
170
229
  when 'server'
171
- save["http_server"] = header_value
230
+ headers["http_server"] = header_value
172
231
 
173
232
  when 'x-powered-by'
174
- save["http_powered"] = header_value
233
+ headers["http_powered"] = header_value
175
234
 
176
235
  when 'date'
177
236
  d = DateTime.parse(header_value) rescue nil
178
- save["http_date"] = d.to_time.utc.strftime("%Y%m%dT%H:%M:%S%z") if d
237
+ headers["http_date"] = d.to_time.utc.strftime("%Y%m%dT%H:%M:%S%z") if d
179
238
 
180
239
  when 'last-modified'
181
240
  d = DateTime.parse(header_value) rescue nil
182
- save["http_modified"] = d.to_time.utc.strftime("%Y%m%dT%H:%M:%S%z") if d
241
+ headers["http_modified"] = d.to_time.utc.strftime("%Y%m%dT%H:%M:%S%z") if d
183
242
 
184
243
  when 'location'
185
- save["http_location"] = header_value
244
+ headers["http_location"] = header_value
186
245
 
187
246
  when 'www-authenticate'
188
- save["http_auth"] = header_value
247
+ headers["http_auth"] = header_value
189
248
 
190
249
  when 'content-length'
191
- save["content-length"] = header_value.to_i
250
+ headers["content-length"] = header_value.to_i
192
251
  end
193
252
  else
194
253
  # not a valid header. XXX, eventually we should log or do something more useful here
@@ -198,36 +257,8 @@ class FilterDecodeHTTPReply
198
257
  end
199
258
  end
200
259
 
201
- head, body = data.split(/\r?\n\r?\n/, 2)
202
-
203
- # Some buggy systems exclude the header entirely
204
- body ||= head
205
-
206
- save["http_raw_body"] = [body].pack("m*").gsub(/\s+/n, "")
207
-
208
- content_encoding = save["http_raw_headers"]["content-encoding"]
209
-
210
- if content_encoding && content_encoding.include?("gzip")
211
- begin
212
- gunzip = Zlib::GzipReader.new(StringIO.new(body))
213
- body = gunzip.read.encode('UTF-8', :invalid=>:replace, :replace=>'?')
214
- gunzip.close()
215
- rescue
216
- end
217
- end
218
- save["http_body"] = body
219
-
220
- if body =~ /<title>([^>]+)</mi
221
- save["http_title"] = $1.strip
222
- end
223
-
224
- save
225
- end
226
-
227
- def valid_header_name?(name)
228
- return name !~ /[\x00-\x1f()<>@,;:\\\"\/\[\]?={}\s]/
260
+ return headers
229
261
  end
230
262
  end
231
-
232
263
  end
233
264
  end
@@ -1,3 +1,3 @@
1
1
  module Dap
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -72,6 +72,19 @@ describe Dap::Filter::FilterDecodeHTTPReply do
72
72
  end
73
73
  end
74
74
 
75
+ context 'decoding chunked response' do
76
+ let(:body) { "5\r\nabcde\r\n0F\r\nfghijklmnopqrst\r\n06\r\nuvwxyz\r\n0\r\n" }
77
+ let(:decode) { filter.decode("HTTP/1.0 200 OK\r\nTransfer-encoding: chunked\r\n\r\n#{body}\r\nSecret: magic\r\n") }
78
+
79
+ it 'correctly dechunks body' do
80
+ expect(decode['http_body']).to eq(('a'..'z').to_a.join)
81
+ end
82
+
83
+ it 'finds trailing headers' do
84
+ expect(decode['http_raw_headers']['secret']).to eq(%w(magic))
85
+ end
86
+ end
87
+
75
88
  context 'decoding responses that are missing the "reason phrase", an RFC anomaly' do
76
89
  let(:decode) { filter.decode("HTTP/1.1 301\r\nDate: Tue, 28 Mar 2017 20:46:52 GMT\r\nContent-Type: text/html\r\nContent-Length: 177\r\nConnection: close\r\nLocation: http://www.example.com/\r\n\r\nstuff") }
77
90
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rapid7 Research
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-29 00:00:00.000000000 Z
11
+ date: 2017-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec