Repository: swagger-api/swagger-parser
Pull request #935 (2018-11-15 13:05:45)

Fix for #934
Hi Team,

This PR is to solve the issue #934 (Open API parser is not validating the invalid references).

Please review the PR and merge the same.

Thanks,
Sailesh V


Patch:
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
index d2660408..c675e0cf 100644
--- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
@@ -89,7 +89,7 @@ public class OpenAPIDeserializer {
     private static final String COOKIE_PARAMETER = "cookie";
     private static final String PATH_PARAMETER = "path";
     private static final String HEADER_PARAMETER = "header";
-
+    private Components components;
     private final Set<String> operationIDs = new HashSet<>();
 
     public SwaggerParseResult deserialize(JsonNode rootNode) {
@@ -133,17 +133,19 @@ public class OpenAPIDeserializer {
                 openAPI.setInfo(info);
             }
 
+            obj = getObject("components", rootNode, false, location, result);
+            if (obj != null) {
+                Components components = getComponents(obj, "components", result);
+                openAPI.setComponents(components);
+                this.components=components;
+            }
+
             obj = getObject("paths", rootNode, true, location, result);
             if (obj != null) {
                 Paths paths = getPaths(obj, "paths", result);
                 openAPI.setPaths(paths);
             }
 
-            obj = getObject("components", rootNode, false, location, result);
-            if (obj != null) {
-                Components components = getComponents(obj, "components", result);
-                openAPI.setComponents(components);
-            }
 
             ArrayNode array = getArray("servers", rootNode, false, location, result);
             if (array != null && array.size() > 0) {
@@ -1962,6 +1964,25 @@ public class OpenAPIDeserializer {
         JsonNode ref = node.get("$ref");
         if (ref != null) {
             if (ref.getNodeType().equals(JsonNodeType.STRING)) {
+
+                if(location.startsWith("paths")){
+                    try{
+                        String components[]=ref.asText().split("#/components");
+                        if((ref.asText().startsWith("#/components"))&&(components.length>1)) {
+                            String[] childComponents = components[1].split("/");
+                            String[] newChildComponents = Arrays.copyOfRange(childComponents, 1, childComponents.length);
+                            boolean isValidComponent = ReferenceValidator.valueOf(newChildComponents[0])
+                                                                         .validateComponent(this.components,
+                                                                                         newChildComponents[1]);
+                            if (!isValidComponent) {
+                                result.missing(location, ref.asText());
+                            }
+                        }
+                    }catch (Exception e){
+                        result.missing(location, ref.asText());
+                    }
+                }
+
                 String mungedRef = mungedRef(ref.textValue());
                 if (mungedRef != null) {
                     schema.set$ref(mungedRef);
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ReferenceValidator.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ReferenceValidator.java
new file mode 100644
index 00000000..aaa67b7f
--- /dev/null
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ReferenceValidator.java
@@ -0,0 +1,65 @@
+package io.swagger.v3.parser.util;
+
+import io.swagger.v3.oas.models.Components;
+
+
+public enum ReferenceValidator {
+
+    schemas {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getSchemas().containsKey(reference);
+        }
+    },
+    responses {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getResponses().containsKey(reference);
+        }
+    },
+    parameters {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getParameters().containsKey(reference);
+        }
+    },
+    examples {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getExamples().containsKey(reference);
+        }
+    },
+    requestBodies {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getRequestBodies().containsKey(reference);
+        }
+    },
+    headers {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getHeaders().containsKey(reference);
+        }
+    },
+    securitySchemes {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getSecuritySchemes().containsKey(reference);
+        }
+    },
+    links {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getLinks().containsKey(reference);
+        }
+    },
+    callbacks {
+        @Override
+        public boolean validateComponent(Components components,String reference) {
+            return components.getCallbacks().containsKey(reference);
+        }
+    };
+
+
+    public abstract boolean validateComponent(Components components,String reference);
+}


Test patch:
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
index 427563ae..a15af16b 100644
--- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
@@ -792,7 +792,7 @@ public class OpenAPIV3ParserTest {
         SwaggerParseResult result = parser.readLocation("src/test/resources/petstore.yaml", null, options);
 
         assertNotNull(result);
-        assertTrue(result.getMessages().isEmpty());
+        assertTrue(result.getMessages().size()==1);
 
         OpenAPI openAPI = result.getOpenAPI();
         Map<String, Schema> definitions = openAPI.getComponents().getSchemas();
diff --git a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
index 70ad63c4..34c171c8 100644
--- a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
+++ b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
@@ -95,6 +95,16 @@ public class OpenAPIParserTest {
         assertEquals(result.getOpenAPI().getOpenapi(), "3.0.1");
     }
 
+    @Test
+    public void testIssue934() {
+        SwaggerParseResult result = new OpenAPIParser().readLocation("issue-934.yaml", null, null);
+
+        assertNotNull(result);
+        assertEquals(result.getMessages().size(),1);
+        assertNotNull(result.getOpenAPI());
+        assertEquals(result.getOpenAPI().getOpenapi(), "3.0.1");
+    }
+
     @Test
     public void testIssue768() {
         ParseOptions options = new ParseOptions();
diff --git a/modules/swagger-parser/src/test/resources/issue-934.yaml b/modules/swagger-parser/src/test/resources/issue-934.yaml
new file mode 100644
index 00000000..8bf28934
--- /dev/null
+++ b/modules/swagger-parser/src/test/resources/issue-934.yaml
@@ -0,0 +1,109 @@
+openapi: "3.0.1"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+  license:
+    name: MIT
+servers:
+  - url: http://petstore.swagger.io/v1
+paths:
+  /pets:
+    get:
+      summary: List all pets
+      operationId: listPets
+      tags:
+        - pets
+      parameters:
+        - name: limit
+          in: query
+          description: How many items to return at one time (max 100)
+          required: false
+          schema:
+            type: integer
+            format: int32
+      responses:
+        200:
+          description: An paged array of pets
+          headers:
+            x-next:
+              description: A link to the next page of responses
+              schema:
+                type: string
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Pets"
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Error"
+    post:
+      summary: Create a pet
+      operationId: createPets
+      tags:
+        - pets
+      responses:
+        201:
+          description: Null response
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Pets"
+  /pets/{petId}:
+    get:
+      summary: Info for a specific pet
+      operationId: showPetById
+      tags:
+        - pets
+      parameters:
+        - name: petId
+          in: path
+          required: true
+          description: The id of the pet to retrieve
+          schema:
+            type: string
+      responses:
+        200:
+          description: Expected response to a valid request
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/APIResponses"
+        default:
+          description: unexpected error
+          content:
+            application/json:
+              schema:
+                $ref: './issue749-reference.yaml#/SomeId'
+components:
+  schemas:
+    Pet:
+      required:
+        - id
+        - name
+      properties:
+        id:
+          type: integer
+          format: int64
+        name:
+          type: string
+        tag:
+          type: string
+    Pets:
+      type: array
+      items:
+        $ref: "#/components/schemas/Pet"
+    Error:
+      required:
+        - code
+        - message
+      properties:
+        code:
+          type: integer
+          format: int32
+        message:
+          type: string
\ No newline at end of file