diff --git a/lib/mpp/parsing.rb b/lib/mpp/parsing.rb index 73c0548..f6318b1 100644 --- a/lib/mpp/parsing.rb +++ b/lib/mpp/parsing.rb @@ -56,12 +56,29 @@ def unescape_quoted(str) sig { params(params_str: T.untyped).returns(T::Hash[T.untyped, T.untyped]) } def parse_auth_params(params_str) params = {} + pos = 0 + first = true + params_str.scan(AUTH_PARAM_RE) do |key, quoted_val, token_val| + match = T.must(Regexp.last_match) + separator = T.must(params_str[pos...match.begin(0)]) + if first + Kernel.raise Mpp::ParseError, "Malformed authentication parameters" unless separator.strip.empty? + else + Kernel.raise Mpp::ParseError, "Malformed authentication parameters" unless separator.match?(/\A\s*,\s*\z/) + end + Kernel.raise Mpp::ParseError, "Duplicate parameter: #{key}" if params.key?(key) value = quoted_val.nil? ? token_val : unescape_quoted(quoted_val) params[key] = value + pos = match.end(0) + first = false end + + tail = T.must(params_str[pos..]) + Kernel.raise Mpp::ParseError, "Malformed authentication parameters" unless tail.strip.empty? + params end diff --git a/test/mpp/test_parsing.rb b/test/mpp/test_parsing.rb index f8e269e..b8c002f 100644 --- a/test/mpp/test_parsing.rb +++ b/test/mpp/test_parsing.rb @@ -67,6 +67,23 @@ def test_parse_www_authenticate_rejects_missing_fields assert_raises(Mpp::ParseError) { Mpp::Challenge.from_www_authenticate('Payment id="abc"') } end + def test_parse_www_authenticate_rejects_malformed_auth_params + challenge = Mpp::Challenge.create( + secret_key: "test-secret", + realm: "api.example.com", + method: "tempo", + intent: "charge", + request: {"amount" => "1000000"} + ) + header = challenge.to_www_authenticate("api.example.com") + + missing_separator = header.sub(", realm=", " realm=") + trailing_junk = "#{header} not-a-param" + + assert_raises(Mpp::ParseError) { Mpp::Challenge.from_www_authenticate(missing_separator) } + assert_raises(Mpp::ParseError) { Mpp::Challenge.from_www_authenticate(trailing_junk) } + end + def test_credential_roundtrip echo = Mpp::ChallengeEcho.new( id: "test-id",