From 821482514b0a121198149e7a4dcdfb09b7fefd71 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 30 Jan 2023 18:08:28 +1300 Subject: [PATCH 1/4] Update `readme.md` - remove old license. --- readme.md | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/readme.md b/readme.md index 6327954..1f327d2 100644 --- a/readme.md +++ b/readme.md @@ -61,32 +61,10 @@ end ## Contributing -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request - -## License - -Released under the MIT license. - -Copyright, 2019, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +We welcome contributions to this project. + +1. Fork it. +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Commit your changes (`git commit -am 'Add some feature'`). +4. Push to the branch (`git push origin my-new-feature`). +5. Create new Pull Request. From 329c2f420a92f5ab44b6b221c73f616441f8d8d8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 30 Jun 2023 19:24:35 +0900 Subject: [PATCH 2/4] Modernize gem. (#19) --- .github/workflows/test-external.yaml | 1 - .github/workflows/test.yaml | 1 - config/sus.rb | 3 +++ gems.rb | 7 +++++-- lib/protocol/http1/version.rb | 2 +- protocol-http1.gemspec | 6 +----- readme.md | 8 ++++++++ test/protocol/http1/body/chunked.rb | 2 +- test/protocol/http1/body/fixed.rb | 2 +- test/protocol/http1/body/remainder.rb | 2 +- 10 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml index 214149c..cbff675 100644 --- a/.github/workflows/test-external.yaml +++ b/.github/workflows/test-external.yaml @@ -20,7 +20,6 @@ jobs: - macos ruby: - - "2.7" - "3.0" - "3.1" - "3.2" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5c765b6..942ede7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,7 +21,6 @@ jobs: - macos ruby: - - "2.7" - "3.0" - "3.1" - "3.2" diff --git a/config/sus.rb b/config/sus.rb index dc706bc..bb035bd 100644 --- a/config/sus.rb +++ b/config/sus.rb @@ -1,4 +1,7 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2023, by Samuel Williams. + require 'covered/sus' include Covered::Sus diff --git a/gems.rb b/gems.rb index a662205..417ca24 100644 --- a/gems.rb +++ b/gems.rb @@ -7,8 +7,6 @@ gemspec -gem "stringio", git: "https://github.com/ruby/stringio" - group :maintenance, optional: true do gem "bake-modernize" gem "bake-gem" @@ -18,6 +16,11 @@ end group :test do + gem "covered" + gem "sus" + gem "bake-test" gem "bake-test-external" + + gem "stringio", "~> 3.0.7" end diff --git a/lib/protocol/http1/version.rb b/lib/protocol/http1/version.rb index 499ee42..d604df7 100644 --- a/lib/protocol/http1/version.rb +++ b/lib/protocol/http1/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2019-2022, by Samuel Williams. +# Copyright, 2019-2023, by Samuel Williams. module Protocol module HTTP1 diff --git a/protocol-http1.gemspec b/protocol-http1.gemspec index e2fd5e2..e2ef0b0 100644 --- a/protocol-http1.gemspec +++ b/protocol-http1.gemspec @@ -17,11 +17,7 @@ Gem::Specification.new do |spec| spec.files = Dir.glob(['{lib}/**/*', '*.md'], File::FNM_DOTMATCH, base: __dir__) - spec.required_ruby_version = ">= 2.4" + spec.required_ruby_version = ">= 2.7.6b" spec.add_dependency "protocol-http", "~> 0.22" - - spec.add_development_dependency "bundler" - spec.add_development_dependency "covered" - spec.add_development_dependency "sus" end diff --git a/readme.md b/readme.md index 1f327d2..7ac8c33 100644 --- a/readme.md +++ b/readme.md @@ -68,3 +68,11 @@ We welcome contributions to this project. 3. Commit your changes (`git commit -am 'Add some feature'`). 4. Push to the branch (`git push origin my-new-feature`). 5. Create new Pull Request. + +### Developer Certificate of Origin + +This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted. + +### Contributor Covenant + +This project is governed by [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms. diff --git a/test/protocol/http1/body/chunked.rb b/test/protocol/http1/body/chunked.rb index dedb7c4..4a5f9e1 100644 --- a/test/protocol/http1/body/chunked.rb +++ b/test/protocol/http1/body/chunked.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2023, by Samuel Williams. +# Copyright, 2019-2023, by Samuel Williams. require 'protocol/http1/body/chunked' require 'connection_context' diff --git a/test/protocol/http1/body/fixed.rb b/test/protocol/http1/body/fixed.rb index 5af07f7..f21aacf 100644 --- a/test/protocol/http1/body/fixed.rb +++ b/test/protocol/http1/body/fixed.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2023, by Samuel Williams. +# Copyright, 2019-2023, by Samuel Williams. require 'protocol/http1/body/fixed' diff --git a/test/protocol/http1/body/remainder.rb b/test/protocol/http1/body/remainder.rb index f1e346e..3e9c34d 100644 --- a/test/protocol/http1/body/remainder.rb +++ b/test/protocol/http1/body/remainder.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2023, by Samuel Williams. +# Copyright, 2019-2023, by Samuel Williams. require 'protocol/http1/body/remainder' From e11fc164fd2b36f7b7e785e69fa8859eb06bcedd Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 30 Jul 2023 12:46:58 +1200 Subject: [PATCH 3/4] Strict validation of content length and chunk length. (#20) --- lib/protocol/http1/body/chunked.rb | 10 ++- lib/protocol/http1/connection.rb | 6 +- test/protocol/http1/connection.rb | 4 +- test/protocol/http1/connection/bad.rb | 125 ++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 test/protocol/http1/connection/bad.rb diff --git a/lib/protocol/http1/body/chunked.rb b/lib/protocol/http1/body/chunked.rb index 9a02b54..c31177b 100644 --- a/lib/protocol/http1/body/chunked.rb +++ b/lib/protocol/http1/body/chunked.rb @@ -35,12 +35,20 @@ def close(error = nil) super end + VALID_CHUNK_LENGTH = /\A[0-9a-fA-F]+\z/ + # Follows the procedure outlined in https://tools.ietf.org/html/rfc7230#section-4.1.3 def read return nil if @finished + length, extensions = read_line.split(";", 2) + + unless length =~ VALID_CHUNK_LENGTH + raise BadRequest, "Invalid chunk length: #{length.dump}" + end + # It is possible this line contains chunk extension, so we use `to_i` to only consider the initial integral part: - length = read_line.to_i(16) + length = Integer(length, 16) if length == 0 @finished = true diff --git a/lib/protocol/http1/connection.rb b/lib/protocol/http1/connection.rb index 26dcb5a..045a8ec 100644 --- a/lib/protocol/http1/connection.rb +++ b/lib/protocol/http1/connection.rb @@ -398,10 +398,12 @@ def read_tunnel_body HEAD = "HEAD" CONNECT = "CONNECT" + VALID_CONTENT_LENGTH = /\A\d+\z/ + def extract_content_length(headers) if content_length = headers.delete(CONTENT_LENGTH) - if length = Integer(content_length, exception: false) and length >= 0 - yield length + if content_length =~ VALID_CONTENT_LENGTH + yield Integer(content_length, 10) else raise BadRequest, "Invalid content length: #{content_length}" end diff --git a/test/protocol/http1/connection.rb b/test/protocol/http1/connection.rb index 6cd134b..081fa1b 100644 --- a/test/protocol/http1/connection.rb +++ b/test/protocol/http1/connection.rb @@ -208,7 +208,7 @@ with "HEAD" do it "can read length of head response" do - body = client.read_response_body("HEAD", 200, {'content-length' => 3773}) + body = client.read_response_body("HEAD", 200, {'content-length' => '3773'}) expect(body).to be_a ::Protocol::HTTP::Body::Head expect(body.length).to be == 3773 @@ -216,7 +216,7 @@ end it "ignores zero length body" do - body = client.read_response_body("HEAD", 200, {'content-length' => 0}) + body = client.read_response_body("HEAD", 200, {'content-length' => '0'}) expect(body).to be_nil end diff --git a/test/protocol/http1/connection/bad.rb b/test/protocol/http1/connection/bad.rb new file mode 100644 index 0000000..be595f9 --- /dev/null +++ b/test/protocol/http1/connection/bad.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019-2023, by Samuel Williams. + +require 'protocol/http1/connection' +require 'connection_context' + +describe Protocol::HTTP1::Connection do + include_context ConnectionContext + + def before + super + + client.stream.write(input) + client.stream.close + end + + with "invalid hexadecimal content-length" do + def input + <<~HTTP.gsub("\n", "\r\n") + POST / HTTP/1.1 + Host: a.com + Content-Length: 0x10 + Connection: close + + 0123456789abcdef + HTTP + end + + it "should fail to parse the request body" do + expect do + server.read_request + end.to raise_exception(Protocol::HTTP1::BadRequest) + end + end + + with "invalid +integer content-length" do + def input + <<~HTTP.gsub("\n", "\r\n") + POST / HTTP/1.1 + Host: a.com + Content-Length: +16 + Connection: close + + 0123456789abcdef + HTTP + end + + it "should fail to parse the request body" do + expect do + server.read_request + end.to raise_exception(Protocol::HTTP1::BadRequest) + end + end + + with "invalid -integer content-length" do + def input + <<~HTTP.gsub("\n", "\r\n") + POST / HTTP/1.1 + Host: a.com + Content-Length: -16 + Connection: close + + 0123456789abcdef + HTTP + end + + it "should fail to parse the request body" do + expect do + server.read_request + end.to raise_exception(Protocol::HTTP1::BadRequest) + end + end + + with "invalid hexidecimal chunk size" do + def input + <<~HTTP.gsub("\n", "\r\n") + POST / HTTP/1.1 + Host: a.com + Transfer-Encoding: chunked + Connection: close + + 0x10 + 0123456789abcdef + 0 + HTTP + end + + it "should fail to parse the request body" do + authority, method, target, version, headers, body = server.read_request + + expect(body).to be_a(Protocol::HTTP1::Body::Chunked) + + expect do + body.read + end.to raise_exception(Protocol::HTTP1::BadRequest) + end + end + + with "invalid +integer chunk size" do + def input + <<~HTTP.gsub("\n", "\r\n") + POST / HTTP/1.1 + Host: a.com + Transfer-Encoding: chunked + Connection: close + + +10 + 0123456789abcdef + 0 + HTTP + end + + it "should fail to parse the request body" do + authority, method, target, version, headers, body = server.read_request + + expect(body).to be_a(Protocol::HTTP1::Body::Chunked) + + expect do + body.read + end.to raise_exception(Protocol::HTTP1::BadRequest) + end + end +end From 20045ad61ad4d8cf63a58be63e35884b96bb16e2 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 30 Jul 2023 12:48:17 +1200 Subject: [PATCH 4/4] Bump patch version. --- lib/protocol/http1/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/protocol/http1/version.rb b/lib/protocol/http1/version.rb index d604df7..dd7b3bd 100644 --- a/lib/protocol/http1/version.rb +++ b/lib/protocol/http1/version.rb @@ -5,6 +5,6 @@ module Protocol module HTTP1 - VERSION = "0.15.0" + VERSION = "0.15.1" end end