Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Path Parameter Validation #73

Closed
triptesh1212 opened this issue Apr 29, 2024 · 6 comments
Closed

Issue with Path Parameter Validation #73

triptesh1212 opened this issue Apr 29, 2024 · 6 comments
Labels
duplicate This issue or pull request already exists

Comments

@triptesh1212
Copy link

Hi community, I have been using libopenapi-validator library. I have the following api-spec.

{
  "openapi": "3.0.0",
  "info": {
    "title": "API Spec With Mandatory Header and Query Parameters",
    "version": "1.0.0"
  },
  "paths": {
    "/api-endpoint/{id}": {
      "get": {
        "summary": "Restricted API Endpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "apiKey",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "name": "apiKey",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "ApiKeyHeader": []
    }
  ]
}

(1) /<host>/api-endpoint gives the following error which is expected

GET Path '/api-endpoint' not found

(2) /<host>/api-endpoint/ does not give any error. Can anyone confirm if the library is taking empty string as path parameter ? (I have kept path parameter required as true. So, my expectation is if its empty then it should fail)

@daveshanley
Copy link
Member

Hello.

I wrote a test to check this.

func TestNewValidator_CheckMissingSlash(t *testing.T) {

	spec := `openapi: 3.0.0
info:
  title: API Spec With Mandatory Header and Query Parameters
  version: 1.0.0
paths:
  "/api-endpoint/{id}":
    get:
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: apiKey
        in: header
        required: true
        schema:
          type: string
      - name: userId
        in: query
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      name: apiKey
      in: header
security:
- ApiKeyHeader: []`

	doc, err := libopenapi.NewDocument([]byte(spec))
	if err != nil {
		t.Fatal(err)
	}
	m, _ := doc.BuildV3Model()

	request, _ := http.NewRequest(http.MethodGet, "https://things.com/host/api-endpoint", nil)

	_, errs, _ := FindPath(request, &m.Model)
	assert.Len(t, errs, 1)
	assert.Equal(t, "GET Path '/host/api-endpoint' not found", errs[0].Message)

	request, _ = http.NewRequest(http.MethodGet, "https://things.com/host/api-endpoint/", nil)

	_, errs, _ = FindPath(request, &m.Model)
	assert.Len(t, errs, 1)
	assert.Equal(t, "GET Path '/host/api-endpoint/' not found", errs[0].Message)

}

Test passes just fine.

I am unable to recreate this.

@triptesh1212
Copy link
Author

triptesh1212 commented May 4, 2024

Hi, I was directly using GetParameterValidator() method. It usually returned error if path does not match. Here is the code.

package main

import (
	"fmt"
	"github.com/pb33f/libopenapi"
	libopenapiValidator "github.com/pb33f/libopenapi-validator"
	"net/http"
	"os"
)

func main() {

	specBytes, _ := os.ReadFile("temp.json")

	doc, err := libopenapi.NewDocument(specBytes)
	if err != nil {
		fmt.Println("error while creating open api spec document", err)
		return
	}

	req, err := http.NewRequest("GET", "/api-endpoint/", nil)
	if err != nil {
		fmt.Println("error while creating new HTTP request", err)
		return
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("apiKey", "someKey")

	v3Model, errs := doc.BuildV3Model()
	if len(errs) > 0 {
		fmt.Println("error while building a Open API spec V3 model", errs)
		return
	}

	v3Model.Model.Servers = nil
	// render the document back to bytes and reload the model.
	_, doc, v3Model, errs = doc.RenderAndReload()

	validator, errs := libopenapiValidator.NewValidator(doc)
	if len(errs) > 0 {
		fmt.Println("error while getting validator", errs)
		return
	}

	paramValidator := validator.GetParameterValidator()

	isSuccess, valErrs := paramValidator.ValidateHeaderParams(req)

	fmt.Println("is validation successful-", isSuccess)

	if len(valErrs) > 0 {
		fmt.Println("error during validation ", valErrs)
		return
	}

}

(1) For path /api-endpoint

is validation successful- false
error during validation [Error: GET Path '/api-endpoint' not found, Reason: The GET request contains a path of '/api-endpoint' however that path, or the GET method for that path does not exist in the specification]

(2) For path /api-endpoint/

is validation successful- true

temp.json :

{
  "openapi": "3.0.0",
  "info": {
    "title": "API Spec With Mandatory Header and Query Parameters",
    "version": "1.0.0"
  },
  "paths": {
    "/api-endpoint/{id}": {
      "get": {
        "summary": "Restricted API Endpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "apiKey",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "userId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "name": "apiKey",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "ApiKeyHeader": []
    }
  ]
}

@triptesh1212
Copy link
Author

Hello.

I wrote a test to check this.

func TestNewValidator_CheckMissingSlash(t *testing.T) {

	spec := `openapi: 3.0.0
info:
  title: API Spec With Mandatory Header and Query Parameters
  version: 1.0.0
paths:
  "/api-endpoint/{id}":
Test passes just fine.

I am unable to recreate this.

@daveshanley , could you modify the paths in the spec and try this "host/api-endpoint/{id}". It would also reproduce the issue.

func TestNewValidator_CheckMissingSlash(t *testing.T) {

	spec := `openapi: 3.0.0
info:
  title: API Spec With Mandatory Header and Query Parameters
  version: 1.0.0
paths:
  "/host/api-endpoint/{id}":
    get:
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: apiKey
        in: header
        required: true
        schema:
          type: string
      - name: userId
        in: query
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      name: apiKey
      in: header
security:
- ApiKeyHeader: []`

	doc, err := libopenapi.NewDocument([]byte(spec))
	if err != nil {
		t.Fatal(err)
	}
	m, _ := doc.BuildV3Model()

	request, _ := http.NewRequest("GET", "https://things.com/host/api-endpoint", nil)

	_, errs, _ := FindPath(request, &m.Model)
	assert.Len(t, errs, 1)
	assert.Equal(t, "GET Path '/host/api-endpoint' not found", errs[0].Message)

	request, _ = http.NewRequest("GET", "https://things.com/host/api-endpoint/", nil)

	_, errs, _ = FindPath(request, &m.Model)
	assert.Len(t, errs, 0) // error is nil here
       
}

@budanm
Copy link

budanm commented May 6, 2024

Hi @daveshanley - i think you can remove the "cannot reproduce" label as the test case which you have written does not match the use-case which is described in this issue .

If you remove /host from this test case below

        request, _ = http.NewRequest(http.MethodGet, "https://things.com/host/api-endpoint/", nil)
	_, errs, _ = FindPath(request, &m.Model)
	assert.Len(t, errs, 1)
	assert.Equal(t, "GET Path '/host/api-endpoint/' not found", errs[0].Message)

the test case fails and that is the actual issue

@daveshanley
Copy link
Member

@daveshanley daveshanley added duplicate This issue or pull request already exists and removed cannot reproduce labels May 6, 2024
@daveshanley
Copy link
Member

added in v0.0.56

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

3 participants