Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,18 @@ Usage

.. code-block:: python

validate(instance, schema, cls=OAS32Validator, **kwargs)
validate(instance, schema, cls=OAS32Validator, allow_remote_references=False, **kwargs)

The first argument is always the value you want to validate.
The second argument is always the OpenAPI schema object.
The ``cls`` keyword argument is optional and defaults to ``OAS32Validator``.
Use ``cls`` when you need a specific validator version/behavior.
Common forwarded keyword arguments include ``registry`` (reference context)
and ``format_checker`` (format validation behavior).
By default, ``validate`` uses a local-only empty registry to avoid implicit
remote ``$ref`` retrieval. To resolve external references, pass an explicit
``registry``. Set ``allow_remote_references=True`` only if you explicitly
accept jsonschema's default remote retrieval behavior.

To validate an OpenAPI schema:

Expand Down
4 changes: 3 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ If you believe you have found a security vulnerability in the repository, please

**Please do not report security vulnerabilities through public GitHub issues.**

Instead, please report them directly to the repository maintainer.
Instead, please report them directly to the repository maintainers or use GitHub's private vulnerability reporting flow
(``Security Advisories``) to report security issues privately:
https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-a-vulnerability/privately-reporting-a-security-vulnerability

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

Expand Down
4 changes: 4 additions & 0 deletions docs/references.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ References
You can resolve JSON Schema references by passing registry.
The ``validate(instance, schema, ...)`` shortcut resolves local references
(``#/...``) against the provided ``schema`` mapping.
By default, the shortcut uses a local-only empty registry and does not
implicitly retrieve remote references.
If needed, ``allow_remote_references=True`` enables jsonschema's default
remote retrieval behavior.

.. code-block:: python

Expand Down
11 changes: 9 additions & 2 deletions docs/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ Validate

.. code-block:: python

validate(instance, schema, cls=OAS32Validator, **kwargs)
validate(instance, schema, cls=OAS32Validator, allow_remote_references=False, **kwargs)

The first argument is always the value you want to validate.
The second argument is always the OpenAPI schema object.
The ``cls`` keyword argument is optional and defaults to ``OAS32Validator``.
Use ``cls`` when you need a specific validator version/behavior.
The ``allow_remote_references`` keyword argument is optional and defaults to
``False``.
Common forwarded keyword arguments include:

- ``registry`` for reference resolution context
- ``registry`` for explicit external reference resolution context
- ``format_checker`` to control format validation behavior

By default, ``validate`` uses a local-only empty registry to avoid implicit
remote ``$ref`` retrieval.
Set ``allow_remote_references=True`` only if you explicitly accept
jsonschema's default remote retrieval behavior.

To validate an OpenAPI schema:

.. code-block:: python
Expand Down
15 changes: 13 additions & 2 deletions openapi_schema_validator/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from jsonschema.exceptions import best_match
from jsonschema.protocols import Validator
from referencing import Registry

from openapi_schema_validator._dialects import OAS31_BASE_DIALECT_ID
from openapi_schema_validator._dialects import OAS32_BASE_DIALECT_ID
Expand All @@ -16,6 +17,7 @@ def validate(
schema: Mapping[str, Any],
cls: type[Validator] = OAS32Validator,
*args: Any,
allow_remote_references: bool = False,
**kwargs: Any
) -> None:
"""
Expand All @@ -33,8 +35,13 @@ def validate(
(``#/...``) are resolved against this mapping.
cls: Validator class to use. Defaults to ``OAS32Validator``.
*args: Positional arguments forwarded to ``cls`` constructor.
allow_remote_references: If ``True`` and no explicit ``registry`` is
provided, allow jsonschema's default remote reference retrieval
behavior.
**kwargs: Keyword arguments forwarded to ``cls`` constructor
(for example ``registry`` and ``format_checker``).
(for example ``registry`` and ``format_checker``). If omitted,
a local-only empty ``Registry`` is used to avoid implicit remote
reference retrieval.

Raises:
jsonschema.exceptions.SchemaError: If ``schema`` is invalid.
Expand All @@ -54,7 +61,11 @@ def validate(
else:
cls.check_schema(schema_dict)

validator = cls(schema_dict, *args, **kwargs)
validator_kwargs = kwargs.copy()
if not allow_remote_references:
validator_kwargs.setdefault("registry", Registry())

validator = cls(schema_dict, *args, **validator_kwargs)
error = best_match(
validator.evolve(schema=schema_dict).iter_errors(instance)
)
Expand Down
62 changes: 62 additions & 0 deletions tests/unit/test_shortcut.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from unittest.mock import patch

import pytest
from referencing import Registry
from referencing import Resource

from openapi_schema_validator import OAS32Validator
from openapi_schema_validator import validate
Expand Down Expand Up @@ -56,3 +58,63 @@ def test_oas32_validate_does_not_fetch_remote_metaschemas(schema):
validate({"email": "foo@bar.com"}, schema, cls=OAS32Validator)

urlopen.assert_not_called()


def test_validate_blocks_implicit_remote_http_references_by_default():
schema = {"$ref": "http://example.com/remote-schema.json"}

with patch("urllib.request.urlopen") as urlopen:
with pytest.raises(Exception, match="Unresolvable"):
validate({}, schema)

urlopen.assert_not_called()


def test_validate_blocks_implicit_file_references_by_default():
schema = {"$ref": "file:///etc/hosts"}

with patch("urllib.request.urlopen") as urlopen:
with pytest.raises(Exception, match="Unresolvable"):
validate({}, schema)

urlopen.assert_not_called()


def test_validate_local_references_still_work_by_default():
schema = {"$defs": {"Value": {"type": "integer"}}, "$ref": "#/$defs/Value"}

with patch("urllib.request.urlopen") as urlopen:
result = validate(1, schema)

assert result is None
urlopen.assert_not_called()


def test_validate_honors_explicit_registry():
schema = {
"type": "object",
"properties": {"name": {"$ref": "urn:name-schema"}},
}
name_schema = Resource.from_contents(
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
}
)
registry = Registry().with_resources(
[("urn:name-schema", name_schema)],
)

result = validate({"name": "John"}, schema, registry=registry)

assert result is None


def test_validate_can_allow_implicit_remote_references():
schema = {"$ref": "http://example.com/remote-schema.json"}

with patch("urllib.request.urlopen") as urlopen:
with pytest.raises(Exception):
validate({}, schema, allow_remote_references=True)

assert urlopen.called
Loading