Web API Fuzz Testing fails using OpenAPI v3.0.1 with error "this tool only supports OpenAPI v2 or OpenAPI v3.x files."

Summary

After enabling Web API Fuzz Testing in our pipeline, the job successfully runs, but fails with the following error:

$ /peach/analyzer-fuzz-api
16:58:32 [ERR] API Security: Error, this tool only supports OpenAPI v2 or OpenAPI v3.x files.
Stopping scanner...

This error should not be occuring, as the OpenAPI JSON file that we are providing is version 3.0.1, as shown below. Additionally, the JSON has been validated using Stoplight Studio to have no errors or warnings:

{
	"openapi": "3.0.1",
	"info": {
		"title": "Data Services - Contacts API",
		"description": "API to retrieve contact information. Pass the Content-Type of application/json to receive JSON formatted data. Otherwise all data returned is XML.",
		"contact": {
			"name": "REDACTED Team",
			"email": "REDACTED"
		},
		"version": "v1"
	},
	"servers": [
		{
			"url": "https://REDACTED/contacts",
			"description": "Production Server"
		},
		{
			"url": "https://REDACTED/contacts",
			"description": "Dev Server"
		}
	],
	"paths": {
		"/search/{term}/{page}/{size}": {
			"get": {
				"tags": [
					"Contacts"
				],
				"summary": "Returns a listing of contacts based on the search parameters and sizing.",
				"description": "Returns a listing of contacts based on the search parameters and sizing.",
				"operationId": "Contacts_GET_972b9506-dbfe-460f-85cf-cef58f2223fb",
				"parameters": [
					{
						"name": "term",
						"in": "path",
						"description": "The search word or phrase to match against phone number, email, name, and location data.",
						"required": true,
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "page",
						"in": "path",
						"description": "The page number of returning records, 0 based. Default is 0.",
						"required": true,
						"schema": {
							"type": "integer",
							"format": "int32",
							"default": 0
						}
					},
					{
						"name": "size",
						"in": "path",
						"description": "The number of records to return at a time. Default is 20.",
						"required": true,
						"schema": {
							"type": "integer",
							"format": "int32",
							"default": 100
						}
					}
				],
				"responses": {
					"200": {
						"description": "Returns the list of contacts in JSON or XML.",
						"content": {
							"application/xml": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Contact"
									}
								}
							},
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Contact"
									}
								}
							}
						}
					},
					"404": {
						"description": "No records found from the search.",
						"content": {
							"application/xml": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							},
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							}
						}
					}
				}
			}
		},
		"/advancedsearch": {
			"get": {
				"tags": [
					"Contacts"
				],
				"summary": "Returns a listing of contacts based on the search parameters and sizing.",
				"description": "Returns a listing of contacts based on the search parameters and sizing.",
				"operationId": "Contacts_GET_bb052340-412c-4f55-80d8-615a2a646f2a",
				"parameters": [
					{
						"name": "lastName",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "firstName",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "title",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "email",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "phone",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "mobile",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "division",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "office",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "baseName",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "address",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "city",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "state",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "zip",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "acctName",
						"in": "query",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "page",
						"in": "query",
						"schema": {
							"type": "integer",
							"format": "int32"
						}
					},
					{
						"name": "size",
						"in": "query",
						"schema": {
							"type": "integer",
							"format": "int32"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Returns the list of contacts in JSON or XML.",
						"content": {
							"application/xml": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Contact"
									}
								}
							},
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Contact"
									}
								}
							}
						}
					},
					"404": {
						"description": "No records found from the search.",
						"content": {
							"application/xml": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							},
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							}
						}
					}
				}
			}
		},
		"/Health": {
			"get": {
				"tags": [
					"Health"
				],
				"summary": "Returns a health check / heartbeat of the API",
				"description": "Returns a health check / heartbeat of the API",
				"operationId": "Health_GET_5bb07d42-230a-46fc-8f86-4da646759584",
				"responses": {
					"200": {
						"description": "Returns OK.",
						"content": {
							"text/plain": {
								"schema": {
									"type": "string"
								}
							},
							"application/json": {
								"schema": {
									"type": "string"
								}
							},
							"text/json": {
								"schema": {
									"type": "string"
								}
							}
						}
					},
					"404": {
						"description": "Returns a message for things not being hunky dorrey.",
						"content": {
							"text/plain": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							},
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							},
							"text/json": {
								"schema": {
									"$ref": "#/components/schemas/ProblemDetails"
								}
							}
						}
					}
				}
			}
		}
	},
	"components": {
		"schemas": {
			"Contact": {
				"required": [
					"givenName",
					"mail",
					"sn"
				],
				"type": "object",
				"properties": {
					"givenName": {
						"type": "string"
					},
					"telephoneNumber": {
						"type": "string",
						"nullable": true
					},
					"telephoneNumberDisplay": {
						"type": "string",
						"nullable": true,
						"readOnly": true
					},
					"extension": {
						"type": "string",
						"nullable": true
					},
					"division": {
						"type": "string",
						"nullable": true
					},
					"displayName": {
						"type": "string",
						"nullable": true
					},
					"title": {
						"type": "string",
						"nullable": true
					},
					"mobile": {
						"type": "string",
						"nullable": true
					},
					"mobileDisplay": {
						"type": "string",
						"nullable": true,
						"readOnly": true
					},
					"floor": {
						"type": "string",
						"nullable": true
					},
					"l": {
						"type": "string",
						"nullable": true
					},
					"personalTitle": {
						"type": "string",
						"nullable": true
					},
					"employeeType": {
						"type": "string",
						"nullable": true
					},
					"streetAddress": {
						"type": "string",
						"nullable": true
					},
					"baseName": {
						"type": "string",
						"nullable": true
					},
					"siteName": {
						"type": "string",
						"nullable": true
					},
					"sn": {
						"type": "string"
					},
					"mail": {
						"type": "string"
					},
					"postalCode": {
						"type": "string",
						"nullable": true
					},
					"physicalDeliveryOfficeName": {
						"type": "string",
						"nullable": true
					},
					"roomCube": {
						"type": "string",
						"nullable": true
					},
					"st": {
						"type": "string",
						"nullable": true
					},
					"initials": {
						"type": "string",
						"nullable": true
					},
					"fsEmail": {
						"type": "string",
						"nullable": true
					}
				},
				"additionalProperties": false
			},
			"ProblemDetails": {
				"type": "object",
				"properties": {
					"type": {
						"type": "string",
						"nullable": true
					},
					"title": {
						"type": "string",
						"nullable": true
					},
					"status": {
						"type": "integer",
						"format": "int32",
						"nullable": true
					},
					"detail": {
						"type": "string",
						"nullable": true
					},
					"instance": {
						"type": "string",
						"nullable": true
					}
				},
				"additionalProperties": {
				}
			}
		}
	},
	"tags": [
		{
			"name": "Contacts",
			"description": "Interacts with Contact models"
		},
		{
			"name": "Health",
			"description": "Retrieves health / heartbeat information"
		}
	]
}

Steps to reproduce

  1. Add Web API Fuzzing to pipeline
  2. Add the appropriate environment variables:
Variable Value
FUZZAPI_OPENAPI https://REDACTED/contacts/swagger/v1/swagger.json
FUZZAPI_TARGET_URL https://REDACTED/contacts/
TEST_API_BEARERAUTH '{"headers":{"key-auth":"REDACTED"}}'
FUZZAPI_OVERRIDES_ENV $TEST_API_BEARERAUTH
  1. Run the pipeline

Example Project

Privately hosted GitLab instance, version 14.7 - unable to share the repo.

What is the current bug behavior?

The Web API Fuzz Testing job fails.

What is the expected correct behavior?

The Web API Fuzz Testing job should pass

Relevant logs and/or screenshots

Output of checks

Results of GitLab environment info

  • Self hosted, v14.7
Expand for output related to GitLab environment info

(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)

(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)

Results of GitLab application Check

Expand for output related to the GitLab application check

(For installations with omnibus-gitlab package run and paste the output of: sudo gitlab-rake gitlab:check SANITIZE=true)

(For installations from source run and paste the output of: sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true)

(we will only investigate if the tests are passing)

Possible fixes