diff --git a/src/main/java/com/networknt/schema/keyword/OneOfValidator.java b/src/main/java/com/networknt/schema/keyword/OneOfValidator.java index a93632085..3895d0fbe 100644 --- a/src/main/java/com/networknt/schema/keyword/OneOfValidator.java +++ b/src/main/java/com/networknt/schema/keyword/OneOfValidator.java @@ -168,7 +168,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo * schema can be determined and validation SHOULD fail. Mapping keys MUST be * string values, but tooling MAY convert response values to strings for * comparison. - * + * * https://spec.openapis.org/oas/v3.1.2#examples-0 */ DiscriminatorState state = executionContext.getDiscriminatorMapping().get(instanceLocation); @@ -183,7 +183,22 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo .messageKey("discriminator.oneOf.no_match_found") .arguments(state.getDiscriminatingValue()).build()); } + /* + * Issue 1225: When the discriminator-indicated schema failed (discriminatorErrors + * is set) but another non-discriminated schema happened to pass (numberOfValidSchema == 1), + * validation incorrectly succeeds. The discriminator mapping constitutes an assertion + * that the payload should validate against the mapped schema. If the mapped schema + * fails but a different schema passes, this is a discriminator mismatch and should fail. + */ + if (discriminatorErrors != null && numberOfValidSchema == 1 && state != null && state.hasDiscriminatingValue()) { + existingErrors + .add(error().keyword("discriminator").instanceNode(node).instanceLocation(instanceLocation) + .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale()) + .messageKey("discriminator.oneOf.no_match_found") + .arguments(state.getDiscriminatingValue()).build()); + } } + } finally { // Restore flag executionContext.setFailFast(failFast); diff --git a/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java b/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java index 1c4857c08..f8eb97194 100644 --- a/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java +++ b/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java @@ -980,5 +980,76 @@ void anyOfRedefinedDiscriminatorAndDiscriminatorWithMissingPropertyName() { // There is still a schema in the anyOf that matches assertTrue(list.isEmpty()); } - + + /** + * Issue 1225. + *
+ * When oneOf with discriminator mapping is used and the discriminating value
+ * maps to a specific schema (e.g., type=string -> $defs/string), but the
+ * data only validates against a different schema (e.g., $defs/number),
+ * validation should fail because the discriminator-indicated schema is not
+ * the one that matches.
+ */
+ @Test
+ void oneOfDiscriminatorEnabledWithDiscriminatorMismatch() {
+ String schemaData = "{\r\n"
+ + " \"discriminator\": {\r\n"
+ + " \"propertyName\": \"type\",\r\n"
+ + " \"mapping\": {\r\n"
+ + " \"string\": \"#/$defs/string\",\r\n"
+ + " \"number\": \"#/$defs/number\"\r\n"
+ + " }\r\n"
+ + " },\r\n"
+ + " \"oneOf\": [\r\n"
+ + " {\r\n"
+ + " \"$ref\": \"#/$defs/string\"\r\n"
+ + " },\r\n"
+ + " {\r\n"
+ + " \"$ref\": \"#/$defs/number\"\r\n"
+ + " }\r\n"
+ + " ],\r\n"
+ + " \"$defs\": {\r\n"
+ + " \"string\": {\r\n"
+ + " \"properties\": {\r\n"
+ + " \"type\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " },\r\n"
+ + " \"value\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " },\r\n"
+ + " \"number\": {\r\n"
+ + " \"properties\": {\r\n"
+ + " \"type\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " },\r\n"
+ + " \"value\": {\r\n"
+ + " \"type\": \"number\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + "}";
+
+ SchemaRegistry factory = SchemaRegistry.withDialect(Dialects.getOpenApi31());
+ Schema schema = factory.getSchema(schemaData);
+
+ // type=string maps to $defs/string via explicit discriminator mapping.
+ // However, value=1 is a number, so $defs/string fails (value must be string).
+ // $defs/number succeeds (value=1 is a valid number).
+ // oneOf passes because exactly one schema matches, but it's the WRONG schema.
+ // The discriminator says type=string should map to $defs/string, so this should fail.
+ String inputData = "{\r\n"
+ + " \"type\": \"string\",\r\n"
+ + " \"value\": 1\r\n"
+ + "}";
+ List
+ * When petType=dog maps to the Dog schema via discriminator mapping, but the
+ * data only validates against Lizard (because lovesRocks=true satisfies Lizard),
+ * validation should fail because the discriminator-indicated schema (Dog) is not
+ * the one that matched.
+ *
+ * Fix for issue 1225.
*/
@Test
- void discriminatorOneOfOneMatchWrongDiscriminatorShouldSucceed() {
+ void discriminatorOneOfOneMatchWrongDiscriminatorShouldFail() {
SchemaRegistry factory = SchemaRegistry.withDialect(Dialects.getOpenApi31());
Schema schema = factory
.getSchema(SchemaLocation.of("classpath:schema/oas/3.1/petstore.yaml#/components/schemas/PetResponse"));
@@ -142,7 +148,10 @@ void discriminatorOneOfOneMatchWrongDiscriminatorShouldSucceed() {
+ " \"lovesRocks\": true\r\n"
+ "}";
List