dap 0.1.3 → 0.1.4

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.
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