davishmcclurg / json_schemer

JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI 3.0, and OpenAPI 3.1.
MIT License
397 stars 65 forks source link

URI::RFC3986_PARSER.parse is too restrictive for the JSON pointers #54

Closed skryukov closed 4 years ago

skryukov commented 4 years ago

URI::RFC3986_PARSER.parse is too restrictive for the JSON pointers. Here is the fragment from RFC 6901:

 For example, given the JSON document

   {
      "foo": ["bar", "baz"],
      "": 0,
      "a/b": 1,
      "c%d": 2,
      "e^f": 3,
      "g|h": 4,
      "i\\j": 5,
      "k\"l": 6,
      " ": 7,
      "m~n": 8
   }

   The following JSON strings evaluate to the accompanying values:

    ""           // the whole document
    "/foo"       ["bar", "baz"]
    "/foo/0"     "bar"
    "/"          0
    "/a~1b"      1
    "/c%d"       2
    "/e^f"       3
    "/g|h"       4
    "/i\\j"      5
    "/k\"l"      6
    "/ "         7
    "/m~0n"      8
davishmcclurg commented 4 years ago

I'm not sure what you mean. Can you provide an example using a JSONSchemer schema?

skryukov commented 4 years ago

@davishmcclurg sure =)

This example inspired by OpenAPI 3 schemas that contain keys like /some/path/{id}:

require 'json_schemer'

schema = {
  'type' => 'object',
  'properties' => { 'foo' => { '$ref' => '#/definitions/~1some~1{id}'} },
  'definitions' => { '/some/{id}' => { 'type' => 'string' } }
}

Hana::Pointer.new('/definitions/~1some~1{id}').eval(schema)
# => {"type"=>"string"}

schemer = JSONSchemer.schema(schema)
schemer.valid?('foo' => 'bar')
# raises URI::InvalidURIError (bad URI(is not URI?): "#/definitions/~1some~1{id}")
davishmcclurg commented 4 years ago

Fix released in 0.2.9

davishmcclurg commented 1 year ago

The fix for this is going to be reverted in v1.0.0. The json schema spec says that $ref needs to be a URI reference, which is defined in RFC 3986. URI fragments are defined there as:

unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
                 / "*" / "+" / "," / ";" / "="
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
fragment      = *( pchar / "/" / "?" )

unreserved and sub-delims don't include { or } so they must be percent encoded. In your example, it would be #/definitions/~1some~1%7Bid%7D.

Tested in v1.0.0 (unreleased):

>> JSONSchemer.valid_schema?({ '$ref' => '#/definitions/~1some~1%7Bid%7D' })
=> true
>> JSONSchemer.valid_schema?({ '$ref' => '#/definitions/~1some~1{id}' })
=> false