From 3ab57a3d4ec44bd005922c17f87175947982a266 Mon Sep 17 00:00:00 2001 From: thc202 Date: Tue, 24 Mar 2026 15:29:11 +0000 Subject: [PATCH] Prevent `StackOverflowError` in `ResolverFully` Add guard to resolve just once to prevent loops/stack overflows. Issue #1751. Signed-off-by: thc202 --- .../swagger/v3/parser/util/ResolverFully.java | 31 ++++++++++--------- .../v3/parser/test/OpenAPIResolverTest.java | 14 +++++++++ .../src/test/resources/issue_1751.yaml | 26 ++++++++++++++++ 3 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 modules/swagger-parser-v3/src/test/resources/issue_1751.yaml diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java index 777c7742f0..5ce3c58ca5 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java @@ -66,7 +66,7 @@ public ResolverFully(ParseOptions options) { private Map requestBodies; private Map headers; private Map links; - private Map resolvedProperties = new IdentityHashMap<>(); + private Map resolvedSchemas = new IdentityHashMap<>(); private Map callbacks; public void resolveFully(OpenAPI openAPI) { @@ -337,6 +337,18 @@ public Schema resolveSchema(Schema schema) { return null; } + Schema resolved = resolvedSchemas.get(schema); + if (resolved != null) { + return resolved; + } + + resolved = resolveSchemaImpl(schema); + resolvedSchemas.put(schema, resolved); + return resolved; + } + + private Schema resolveSchemaImpl(Schema schema) { + if(schema.get$ref() != null) { String ref= schema.get$ref(); Schema resolved; @@ -394,7 +406,7 @@ public Schema resolveSchema(Schema schema) { Schema innerProperty = obj.getProperties().get(propertyName); // reference check if(schema != innerProperty) { - updated.put(propertyName, resolveSchemaProperty(propertyName, innerProperty)); + updated.put(propertyName, resolveSchema(innerProperty)); } } obj.setProperties(updated); @@ -499,7 +511,7 @@ public Schema resolveSchema(Schema schema) { Map properties = model.getProperties(); for (String propertyName : properties.keySet()) { Schema property = (Schema) model.getProperties().get(propertyName); - updated.put(propertyName, resolveSchemaProperty(propertyName, property)); + updated.put(propertyName, resolveSchema(property)); } for (String key : updated.keySet()) { @@ -573,7 +585,7 @@ private void aggregateSchemaCombinators(ComposedSchema sourceSchema, Schema targ if (resolved.getProperties() != null) { for (String key : properties.keySet()) { Schema prop = (Schema) resolved.getProperties().get(key); - targetSchema.addProperties(key, resolveSchemaProperty(key, prop)); + targetSchema.addProperties(key, resolveSchema(prop)); } if (resolved.getRequired() != null) { @@ -698,15 +710,4 @@ private void aggregateSchemaCombinators(ComposedSchema sourceSchema, Schema targ targetSchema.setRequired(required); } } - - private Schema resolveSchemaProperty(String propertyName, Schema innerProperty) { - if (resolvedProperties.get(propertyName) == null || resolvedProperties.get(propertyName) != innerProperty) { - LOGGER.debug("avoiding infinite loop"); - Schema resolved = resolveSchema(innerProperty); - resolvedProperties.put(propertyName, resolved); - return resolved; - } else { - return resolvedProperties.get(propertyName); - } - } } diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIResolverTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIResolverTest.java index ff1b353014..2d42eaeb2e 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIResolverTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIResolverTest.java @@ -1432,6 +1432,20 @@ public void recursiveResolving2() { } } + @Test + public void recursiveResolvingIssue1751() { + ParseOptions parseOptions = new ParseOptions(); + parseOptions.setResolve(true); + parseOptions.setResolveFully(true); + OpenAPI openAPI = new OpenAPIV3Parser().read("issue_1751.yaml", null, parseOptions); + try { + Json.mapper().writeValueAsString(openAPI); + } + catch (Exception e) { + fail("Recursive loop found"); + } + } + @Test public void recursiveIssue984() { ParseOptions parseOptions = new ParseOptions(); diff --git a/modules/swagger-parser-v3/src/test/resources/issue_1751.yaml b/modules/swagger-parser-v3/src/test/resources/issue_1751.yaml new file mode 100644 index 0000000000..7f4fc6544e --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue_1751.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +paths: + /first: + get: + parameters: + - "$ref": "#/components/parameters/p_one" + - "$ref": "#/components/parameters/p_one" + - "$ref": "#/components/parameters/p_one" + responses: + 200: + description: ok +components: + parameters: + p_one: + in: query + schema: + "$ref": "#/components/schemas/NestedObject" + style: deepObject + schemas: + NestedObject: + additionalProperties: + oneOf: + - "$ref": "#/components/schemas/NestedObject" + - not: + type: object + type: object \ No newline at end of file