diff --git a/CHANGELOG.md b/CHANGELOG.md index 3589dd7f..c0162fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Return "Unknown specification" error on `https`-prefixed `$schema` for Draft 4, 5, 6. [#629](https://github.com/Stranger6667/jsonschema/issues/629) + ## [0.26.0] - 2024-10-26 **Important:** This release contains breaking changes. See the [Migration Guide](MIGRATION.md) for details on transitioning to the new API. diff --git a/crates/jsonschema-py/CHANGELOG.md b/crates/jsonschema-py/CHANGELOG.md index c4f978f1..e5c1fe33 100644 --- a/crates/jsonschema-py/CHANGELOG.md +++ b/crates/jsonschema-py/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Return "Unknown specification" error on `https`-prefixed `$schema` for Draft 4, 5, 6. [#629](https://github.com/Stranger6667/jsonschema/issues/629) + ### Performance - Speedup Python -> Rust data serialization. diff --git a/crates/jsonschema-py/tests-py/test_jsonschema.py b/crates/jsonschema-py/tests-py/test_jsonschema.py index f367174c..33ec79f2 100644 --- a/crates/jsonschema-py/tests-py/test_jsonschema.py +++ b/crates/jsonschema-py/tests-py/test_jsonschema.py @@ -155,6 +155,13 @@ def test_invalid_value(method): getattr(schema, method)(object()) +def test_invalid_schema_keyword(): + # Note `https`, not `http` + schema = {"$schema": "https://json-schema.org/draft-07/schema"} + with pytest.raises(ValidationError, match="Unknown specification: https://json-schema.org/draft-07/schema"): + validator_for(schema) + + def test_error_message(): schema = {"properties": {"foo": {"type": "integer"}}} instance = {"foo": None} diff --git a/crates/jsonschema/src/compiler.rs b/crates/jsonschema/src/compiler.rs index 1ef998b7..39d93ecf 100644 --- a/crates/jsonschema/src/compiler.rs +++ b/crates/jsonschema/src/compiler.rs @@ -285,7 +285,7 @@ pub(crate) fn build_validator( mut config: ValidationOptions, schema: &Value, ) -> Result> { - let draft = config.draft_for(schema); + let draft = config.draft_for(schema)?; let resource_ref = draft.create_resource_ref(schema); let resource = draft.create_resource(schema.clone()); let base_uri = resource.id().unwrap_or(DEFAULT_ROOT_URL).to_string(); diff --git a/crates/jsonschema/src/lib.rs b/crates/jsonschema/src/lib.rs index 867f7674..c59caa2d 100644 --- a/crates/jsonschema/src/lib.rs +++ b/crates/jsonschema/src/lib.rs @@ -1335,6 +1335,19 @@ mod tests { assert!(validate_fn(&schema, &invalid_instance).is_err()); } + #[test] + fn test_invalid_schema_keyword() { + let schema = json!({ + // Note `https`, not `http` + "$schema": "https://json-schema.org/draft-07/schema", + }); + let error = crate::validator_for(&schema).expect_err("Should fail"); + assert_eq!( + error.to_string(), + "Unknown specification: https://json-schema.org/draft-07/schema" + ); + } + #[test_case(Draft::Draft4)] #[test_case(Draft::Draft6)] #[test_case(Draft::Draft7)] diff --git a/crates/jsonschema/src/options.rs b/crates/jsonschema/src/options.rs index 7db51f61..7a6b4fad 100644 --- a/crates/jsonschema/src/options.rs +++ b/crates/jsonschema/src/options.rs @@ -55,28 +55,28 @@ impl ValidationOptions { pub(crate) fn draft(&self) -> Draft { self.draft.unwrap_or_default() } - pub(crate) fn draft_for(&self, contents: &Value) -> Draft { + pub(crate) fn draft_for(&self, contents: &Value) -> Result> { // Preference: // - Explicitly set // - Autodetected // - Default if let Some(draft) = self.draft { - draft + Ok(draft) } else { let default = Draft::default(); match default.detect(contents) { - Ok(draft) => draft, + Ok(draft) => Ok(draft), Err(referencing::Error::UnknownSpecification { specification }) => { // Try to retrieve the specification and detect its draft if let Ok(Ok(retrieved)) = uri::from_str(&specification) .map(|uri| self.retriever.retrieve(&uri.borrow())) { - default.detect(&retrieved).unwrap_or_default() + Ok(default.detect(&retrieved)?) } else { - default + Err(referencing::Error::UnknownSpecification { specification }.into()) } } - _ => default, + Err(error) => Err(error.into()), } } }