api-quality-spectral-ruleset 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +69 -0
- package/apq-spectral.yaml +1119 -0
- package/functions/apq-alternate-paths.js +35 -0
- package/functions/apq-at-most-one-body-parameter.js +24 -0
- package/functions/apq-compare-insensitive.js +33 -0
- package/functions/apq-custom-schema.js +88 -0
- package/functions/apq-default-value.js +23 -0
- package/functions/apq-naming-convention.js +21 -0
- package/functions/apq-parameter-naming-convention.js +76 -0
- package/functions/apq-properties-schema-format.js +29 -0
- package/functions/apq-resources-by-verb.js +117 -0
- package/functions/apq-responses.js +26 -0
- package/functions/apq-schema-format.js +98 -0
- package/functions/apq-standard-response-codes.js +76 -0
- package/functions/apq-truthy-insensitive.js +18 -0
- package/package.json +47 -0
|
@@ -0,0 +1,1119 @@
|
|
|
1
|
+
functionsDir: './functions'
|
|
2
|
+
functions:
|
|
3
|
+
- apq-alternate-paths
|
|
4
|
+
- apq-custom-schema
|
|
5
|
+
- apq-compare-insensitive
|
|
6
|
+
- apq-parameter-naming-convention
|
|
7
|
+
- apq-resources-by-verb
|
|
8
|
+
- apq-at-most-one-body-parameter
|
|
9
|
+
- apq-standard-response-codes
|
|
10
|
+
extends:
|
|
11
|
+
- spectral:asyncapi
|
|
12
|
+
- spectral:oas
|
|
13
|
+
rules:
|
|
14
|
+
apiq:OAR001:
|
|
15
|
+
description: "For security reasons and as a REST best practice, the HTTPS protocol is mandatory."
|
|
16
|
+
message: "OAR001: HTTPS protocol is mandatory."
|
|
17
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR001.md"
|
|
18
|
+
severity: "error"
|
|
19
|
+
given: "$.servers[*].url"
|
|
20
|
+
then:
|
|
21
|
+
function: pattern
|
|
22
|
+
functionOptions:
|
|
23
|
+
match: "^https://"
|
|
24
|
+
apiq:OAR002:
|
|
25
|
+
description: "A wrong scope definition may cause problems to import the API definition into WSO2."
|
|
26
|
+
message: "OAR002: Scope definition must include the 'key' attribute along with 'name' and 'roles'."
|
|
27
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR002.md"
|
|
28
|
+
severity: "error"
|
|
29
|
+
given: "$.x-wso2-security.apim.x-wso2-scopes[*]"
|
|
30
|
+
then:
|
|
31
|
+
- function: "truthy"
|
|
32
|
+
field: "key"
|
|
33
|
+
message: "Scope must define a 'key' attribute."
|
|
34
|
+
apiq:OAR003:
|
|
35
|
+
description: "A description can help other developers to understand the correct use of the scope."
|
|
36
|
+
message: "OAR003: Scope must define a 'description' attribute."
|
|
37
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR003.md"
|
|
38
|
+
severity: "error"
|
|
39
|
+
given: "$.x-wso2-security.apim.x-wso2-scopes[*]"
|
|
40
|
+
then:
|
|
41
|
+
- function: "truthy"
|
|
42
|
+
field: "description"
|
|
43
|
+
message: "Scope must define a 'description' attribute."
|
|
44
|
+
apiq:OAR004:
|
|
45
|
+
description: "A role with forbidden characters may cause problems in some applications."
|
|
46
|
+
message: "OAR004: Role should not contain forbidden characters (e.g. special characters like €, *, etc.)."
|
|
47
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR004.md"
|
|
48
|
+
severity: "error"
|
|
49
|
+
given: "$.x-wso2-security.apim.x-wso2-scopes[*].roles"
|
|
50
|
+
then:
|
|
51
|
+
function: "pattern"
|
|
52
|
+
functionOptions:
|
|
53
|
+
match: "^[A-Za-z0-9_]+$"
|
|
54
|
+
apiq:OAR005:
|
|
55
|
+
description: "A wrong scope may cause problems to import the API definition into WSO2 or allow all users to call the endpoint."
|
|
56
|
+
message: "OAR005: Scope in the operation must be defined correctly and match an existing scope."
|
|
57
|
+
severity: "error"
|
|
58
|
+
given: "$.paths[*][*].x-scope"
|
|
59
|
+
then:
|
|
60
|
+
- function: "truthy"
|
|
61
|
+
message: "Scope must be defined in the operation."
|
|
62
|
+
- function: enumeration
|
|
63
|
+
functionOptions:
|
|
64
|
+
values:
|
|
65
|
+
- read
|
|
66
|
+
- write
|
|
67
|
+
apiq:OAR006:
|
|
68
|
+
description: "Routes must define request media types supported by the API."
|
|
69
|
+
message: "OAR006: Specify at least one Media Type in the content of the request body."
|
|
70
|
+
severity: "error"
|
|
71
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR006.md"
|
|
72
|
+
given: "$.paths[*][post,put,patch]"
|
|
73
|
+
then:
|
|
74
|
+
- field: "requestBody"
|
|
75
|
+
function: truthy
|
|
76
|
+
- field: "requestBody.content"
|
|
77
|
+
function: truthy
|
|
78
|
+
apiq:OAR007:
|
|
79
|
+
description: "Routes must define response media types supported by the API"
|
|
80
|
+
message: "OAR007: Specify at least one Media Type in the content of the response body."
|
|
81
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR007.md"
|
|
82
|
+
severity: "error"
|
|
83
|
+
given: "$.paths[*][get,post,put,patch,delete].responses[?(@property != '204')]"
|
|
84
|
+
then:
|
|
85
|
+
- field: "content"
|
|
86
|
+
function: truthy
|
|
87
|
+
apiq:OAR008:
|
|
88
|
+
description: "HTTP verbs not encouraged."
|
|
89
|
+
message: "OAR008: Only the REST standard verbs are allowed (POST, GET, PUT, PATCH and DELETE)."
|
|
90
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR008.md"
|
|
91
|
+
severity: error
|
|
92
|
+
given: "$.paths[*][?(@property == 'get' || @property == 'post' || @property == 'put' || @property == 'delete' || @property == 'patch')]"
|
|
93
|
+
then:
|
|
94
|
+
function: truthy
|
|
95
|
+
apiq:OAR009:
|
|
96
|
+
description: "Default request media type should be defined for operations."
|
|
97
|
+
message: "OAR009: Default request media type is mandatory."
|
|
98
|
+
severity: "error"
|
|
99
|
+
given: "$..content"
|
|
100
|
+
then:
|
|
101
|
+
field: "application/json"
|
|
102
|
+
function: truthy
|
|
103
|
+
apiq:OAR010:
|
|
104
|
+
description: "Default response media type should be defined for responses."
|
|
105
|
+
message: "OAR010: Default response media type is mandatory."
|
|
106
|
+
severity: warn
|
|
107
|
+
given: "$.paths[*][get,post,put,patch,delete].responses[*]"
|
|
108
|
+
then:
|
|
109
|
+
field: "content['application/json']"
|
|
110
|
+
function: truthy
|
|
111
|
+
apiq:OAR011:
|
|
112
|
+
description: "URLs should follow the specified naming convention: all literal path segments must be in kebab-case."
|
|
113
|
+
message: "OAR011: The resource names with more than two words must be compliant with the standard naming convention (kebab-case)."
|
|
114
|
+
severity: error
|
|
115
|
+
given: "$.paths[*]~"
|
|
116
|
+
then:
|
|
117
|
+
function: pattern
|
|
118
|
+
functionOptions:
|
|
119
|
+
match: "^(\/|[a-z0-9-]+|{[a-zA-Z0-9_|-]+})+$"
|
|
120
|
+
apiq:OAR012:
|
|
121
|
+
description: "Path params, query params, object names and property names should follow the configured naming convention. You can configure snake_case (default), kebab-case, camelCase or UpperCamelCase"
|
|
122
|
+
message: "OAR012: Path params, query params, object names and property names must follow the configured naming convention."
|
|
123
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR012.md"
|
|
124
|
+
severity: warn
|
|
125
|
+
given:
|
|
126
|
+
- "$.paths[*][*].parameters[?(@.in == 'path' || @.in == 'query')].name"
|
|
127
|
+
- "$.paths[*][*].parameters[*].schema.properties.*~"
|
|
128
|
+
- "$.paths[*][*].requestBody..schema.properties.*~"
|
|
129
|
+
- "$.paths[*][*].responses..schema.properties.*~"
|
|
130
|
+
- "$.components.schemas.*~"
|
|
131
|
+
then:
|
|
132
|
+
function: apq-parameter-naming-convention
|
|
133
|
+
functionOptions:
|
|
134
|
+
namingConvention: "snake_case"
|
|
135
|
+
apiq:OAR013:
|
|
136
|
+
description: "Default response is required for all operations."
|
|
137
|
+
message: "OAR013: Default response is required."
|
|
138
|
+
severity: error
|
|
139
|
+
given: "$.paths[*][get,post,put,patch,delete].responses"
|
|
140
|
+
then:
|
|
141
|
+
field: "default"
|
|
142
|
+
function: truthy
|
|
143
|
+
apiq:OAR014:
|
|
144
|
+
description: "Resources depth level should be below the non-suggested range."
|
|
145
|
+
message: "OAR014: The number of parts of the path must be less than 4."
|
|
146
|
+
severity: warn
|
|
147
|
+
given: "$.paths.*~"
|
|
148
|
+
then:
|
|
149
|
+
function: pattern
|
|
150
|
+
functionOptions:
|
|
151
|
+
notMatch: "^(?:\\/[^\\/]+){4,}"
|
|
152
|
+
apiq:OAR015:
|
|
153
|
+
description: "Resources depth level should be smaller than 5."
|
|
154
|
+
message: "OAR015: Path exceeds the maximum allowed depth level of 5."
|
|
155
|
+
severity: error
|
|
156
|
+
given: "$.paths.*~"
|
|
157
|
+
then:
|
|
158
|
+
function: pattern
|
|
159
|
+
functionOptions:
|
|
160
|
+
match: "^\/[^\/]*((\/{[^}]*})*\/[^\/]*(\/{[^}]*})*){0,4}\/?$"
|
|
161
|
+
apiq:OAR016:
|
|
162
|
+
description: "Numeric types requires a valid format."
|
|
163
|
+
message: "OAR016: Numeric types must use a valid format for their type."
|
|
164
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR016.md"
|
|
165
|
+
severity: error
|
|
166
|
+
given:
|
|
167
|
+
- "$..[?(@.type == 'integer' && @.format)]"
|
|
168
|
+
- "$..[?(@.type == 'number' && @.format)]"
|
|
169
|
+
then:
|
|
170
|
+
function: schema
|
|
171
|
+
functionOptions:
|
|
172
|
+
schema:
|
|
173
|
+
oneOf:
|
|
174
|
+
- properties:
|
|
175
|
+
type:
|
|
176
|
+
const: integer
|
|
177
|
+
format:
|
|
178
|
+
enum: [int32, int64]
|
|
179
|
+
- properties:
|
|
180
|
+
type:
|
|
181
|
+
const: number
|
|
182
|
+
format:
|
|
183
|
+
enum: [float, double]
|
|
184
|
+
apiq:OAR017:
|
|
185
|
+
description: "Resource path should alternate static and parametrized parts."
|
|
186
|
+
message: "OAR017: Resource path should alternate static and parametrized parts."
|
|
187
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR017.md"
|
|
188
|
+
severity: error
|
|
189
|
+
given: "$.paths.*~"
|
|
190
|
+
then:
|
|
191
|
+
function: apq-alternate-paths
|
|
192
|
+
functionOptions:
|
|
193
|
+
except: ["me"]
|
|
194
|
+
apiq:OAR018:
|
|
195
|
+
description: Operation not recommended for resource path depending on HTTP verb
|
|
196
|
+
message: "OAR018: Operation not recommended for resource path: {{path}} ({{verb}})"
|
|
197
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR018.md"
|
|
198
|
+
severity: warn
|
|
199
|
+
given: "$.paths"
|
|
200
|
+
then:
|
|
201
|
+
function: apq-resources-by-verb
|
|
202
|
+
functionOptions:
|
|
203
|
+
allowed-resources-paths: |
|
|
204
|
+
;get:^/[^/{}]+$
|
|
205
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+$
|
|
206
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+$
|
|
207
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
208
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
209
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
210
|
+
|
|
211
|
+
;post:^/[^/{}]+$
|
|
212
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+$
|
|
213
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+$
|
|
214
|
+
;post:^/[^/{}]+/get$
|
|
215
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/get$
|
|
216
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/get$
|
|
217
|
+
;post:^/[^/{}]+/delete$
|
|
218
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/delete$
|
|
219
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/delete$
|
|
220
|
+
|
|
221
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
222
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
223
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
224
|
+
|
|
225
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
226
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
227
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
228
|
+
|
|
229
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
230
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
231
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)/[^/{}]+/(\{[^/{}]+\}|me)$
|
|
232
|
+
apiq:OAR019:
|
|
233
|
+
description: "$select must be defined as a query parameter in all operations."
|
|
234
|
+
message: "OAR019: $select must be defined as a query parameter in this operation."
|
|
235
|
+
severity: warn
|
|
236
|
+
given: "$.paths[*].get.parameters"
|
|
237
|
+
then:
|
|
238
|
+
field: "$[?(@.name == '$select' && @.in == 'query')]"
|
|
239
|
+
function: truthy
|
|
240
|
+
apiq:OAR020:
|
|
241
|
+
description: "$expand must be defined as a query parameter in all operations."
|
|
242
|
+
message: "OAR020: $expand must be defined as a query parameter in this operation."
|
|
243
|
+
severity: warn
|
|
244
|
+
given: "$.paths[*].get.parameters"
|
|
245
|
+
then:
|
|
246
|
+
field: "$[?(@.name == '$expand' && @.in == 'query')]"
|
|
247
|
+
function: truthy
|
|
248
|
+
apiq:OAR021:
|
|
249
|
+
description: "$exclude must be defined as a query parameter in all operations."
|
|
250
|
+
message: "OAR021: $exclude must be defined as a query parameter in this operation."
|
|
251
|
+
severity: warn
|
|
252
|
+
given: "$.paths[*].get.parameters"
|
|
253
|
+
then:
|
|
254
|
+
field: "$[?(@.name == '$exclude' && @.in == 'query')]"
|
|
255
|
+
function: truthy
|
|
256
|
+
apiq:OAR022:
|
|
257
|
+
description: "$orderby must be defined as a query parameter in all operations."
|
|
258
|
+
message: "OAR022: $orderby must be defined as a query parameter in this operation."
|
|
259
|
+
severity: warn
|
|
260
|
+
given: "$.paths[*].get.parameters"
|
|
261
|
+
then:
|
|
262
|
+
field: "$[?(@.name == '$orderby' && @.in == 'query')]"
|
|
263
|
+
function: truthy
|
|
264
|
+
apiq:OAR023:
|
|
265
|
+
description: "$total must be defined as a query parameter in all operations."
|
|
266
|
+
message: "OAR023: $total must be defined as a query parameter in this operation."
|
|
267
|
+
severity: error
|
|
268
|
+
given: "$.paths[*].get.parameters"
|
|
269
|
+
then:
|
|
270
|
+
field: "$[?(@.name == '$total' && @.in == 'query')]"
|
|
271
|
+
function: truthy
|
|
272
|
+
apiq:OAR024:
|
|
273
|
+
description: "$start must be defined as a query parameter in all operations."
|
|
274
|
+
message: "OAR024: $start must be defined as a query parameter in this operation."
|
|
275
|
+
severity: error
|
|
276
|
+
given: "$.paths[*].get.parameters"
|
|
277
|
+
then:
|
|
278
|
+
field: "$[?(@.name == '$start' && @.in == 'query')]"
|
|
279
|
+
function: truthy
|
|
280
|
+
apiq:OAR025:
|
|
281
|
+
description: "$limit must be defined as a query parameter in all operations."
|
|
282
|
+
message: "OAR025: $limit must be defined as a query parameter in this operation."
|
|
283
|
+
severity: error
|
|
284
|
+
given: "$.paths[*].get.parameters"
|
|
285
|
+
then:
|
|
286
|
+
field: "$[?(@.name == '$limit' && @.in == 'query')]"
|
|
287
|
+
function: truthy
|
|
288
|
+
apiq:OAR026:
|
|
289
|
+
description: "The $total parameter default value should be false."
|
|
290
|
+
message: "OAR026: The $total parameter default value should be false."
|
|
291
|
+
severity: error
|
|
292
|
+
given: "$.paths[*].get.parameters[?(@.name == '$total' && @.in == 'query')]"
|
|
293
|
+
then:
|
|
294
|
+
field: "schema.default"
|
|
295
|
+
function: falsy
|
|
296
|
+
apiq:OAR027:
|
|
297
|
+
description: "Location header is required in responses with code 201 from POST operations."
|
|
298
|
+
message: "OAR027: Location header is required in responses with code 201 from POST operations."
|
|
299
|
+
severity: error
|
|
300
|
+
given: "$.paths.*.post.responses['201']"
|
|
301
|
+
then:
|
|
302
|
+
field: "headers.Location.schema.type"
|
|
303
|
+
function: truthy
|
|
304
|
+
apiq:OAR028:
|
|
305
|
+
description: "$filter must be defined as a query parameter in all operations."
|
|
306
|
+
message: "OAR028: $filter must be defined as a query parameter in this operation."
|
|
307
|
+
severity: warn
|
|
308
|
+
given: "$.paths[*].get.parameters"
|
|
309
|
+
then:
|
|
310
|
+
field: "$[?(@.name == '$filter' && @.in == 'query')]"
|
|
311
|
+
function: truthy
|
|
312
|
+
apiq:OAR029:
|
|
313
|
+
description: "A response not compliant with the standard may cause application issues."
|
|
314
|
+
message: "OAR029: A response not compliant with the standard may cause application issues."
|
|
315
|
+
severity: error
|
|
316
|
+
given: "$.components.schemas.errorResponse"
|
|
317
|
+
then:
|
|
318
|
+
function: schema
|
|
319
|
+
functionOptions:
|
|
320
|
+
schema:
|
|
321
|
+
type: object
|
|
322
|
+
properties:
|
|
323
|
+
properties:
|
|
324
|
+
type: object
|
|
325
|
+
properties:
|
|
326
|
+
error:
|
|
327
|
+
type: object
|
|
328
|
+
properties:
|
|
329
|
+
properties:
|
|
330
|
+
type: object
|
|
331
|
+
properties:
|
|
332
|
+
code:
|
|
333
|
+
type: string
|
|
334
|
+
message:
|
|
335
|
+
type: string
|
|
336
|
+
details:
|
|
337
|
+
type: array
|
|
338
|
+
httpStatus:
|
|
339
|
+
type: integer
|
|
340
|
+
required:
|
|
341
|
+
- code
|
|
342
|
+
- message
|
|
343
|
+
- httpStatus
|
|
344
|
+
required:
|
|
345
|
+
- properties
|
|
346
|
+
apiq:OAR030:
|
|
347
|
+
description: "The path '/status' must be declared."
|
|
348
|
+
message: "OAR030: The path '/status' must be declared."
|
|
349
|
+
severity: error
|
|
350
|
+
given: "$.paths"
|
|
351
|
+
then:
|
|
352
|
+
field: "/status"
|
|
353
|
+
function: truthy
|
|
354
|
+
apiq:OAR031:
|
|
355
|
+
description: The examples can help developers to understand the response data structure and representation."
|
|
356
|
+
message: "OAR031: Examples - Parameters, Request Body and Responses must have an examples defined."
|
|
357
|
+
severity: error
|
|
358
|
+
given:
|
|
359
|
+
- $.paths.*.*.parameters.*
|
|
360
|
+
- $.paths.*.*[responses,requestBody]..content.*
|
|
361
|
+
then:
|
|
362
|
+
field: examples
|
|
363
|
+
function: truthy
|
|
364
|
+
apiq:OAR032:
|
|
365
|
+
description: "Ambiguous path parts not encouraged."
|
|
366
|
+
message: "OAR032: When reading a URL, it must be possible to interpret which resources it is referring to in a concrete way. Ambiguous names in resources must be avoided."
|
|
367
|
+
severity: error
|
|
368
|
+
given: "$.paths.*~"
|
|
369
|
+
then:
|
|
370
|
+
field: "name"
|
|
371
|
+
function: pattern
|
|
372
|
+
functionOptions:
|
|
373
|
+
notMatch: "^.*elements|instances|resources|values|terms|objects|items|elements|instances|resources|values.*$"
|
|
374
|
+
apiq:OAR033:
|
|
375
|
+
description: "Headers 'Accept', 'Content-Type', and 'Authorization' are not allowed."
|
|
376
|
+
message: "OAR033: The header '{{value}}' is not allowed."
|
|
377
|
+
severity: error
|
|
378
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'header')]"
|
|
379
|
+
then:
|
|
380
|
+
field: "name"
|
|
381
|
+
function: pattern
|
|
382
|
+
functionOptions:
|
|
383
|
+
notMatch: "^(Accept|Content-Type|Authorization)$"
|
|
384
|
+
apiq:OAR034:
|
|
385
|
+
description: "A paged response not compliant with the standard may cause application issues."
|
|
386
|
+
message: "OAR034: Paged response schema must be compliant with the standard."
|
|
387
|
+
severity: error
|
|
388
|
+
given: "$.paths[*][*].responses[?(@property != '204' && @property != '304' && @property != 'default')]"
|
|
389
|
+
then:
|
|
390
|
+
field: content
|
|
391
|
+
function: truthy
|
|
392
|
+
apiq:OAR035:
|
|
393
|
+
description: "Response code 401 must be defined for operations with security schemes defined."
|
|
394
|
+
message: "OAR035: Response code 401 must be defined for operations with security schemes defined."
|
|
395
|
+
severity: "error"
|
|
396
|
+
given: "$.paths[*][*].responses"
|
|
397
|
+
then:
|
|
398
|
+
function: truthy
|
|
399
|
+
field: "401"
|
|
400
|
+
message: "Operation must define a 401 Unauthorized response for security schemes."
|
|
401
|
+
apiq:OAR036:
|
|
402
|
+
description: "Cookie use is forbidden as a session mechanism."
|
|
403
|
+
message: "OAR036: Cookie use is forbidden as a session mechanism."
|
|
404
|
+
severity: error
|
|
405
|
+
given:
|
|
406
|
+
- "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'header')]"
|
|
407
|
+
- "$.paths[*][get,post,put,patch,delete].responses[*].headers.*~"
|
|
408
|
+
then:
|
|
409
|
+
field: "name"
|
|
410
|
+
function: pattern
|
|
411
|
+
functionOptions:
|
|
412
|
+
notMatch: "^(Cookie|Set-Cookie)$"
|
|
413
|
+
apiq:OAR037:
|
|
414
|
+
description: "Schema should use well-defined type and format."
|
|
415
|
+
message: "OAR037: String schemas must specify a valid format (date, date-time, password, byte, binary, email, uuid, uri, hostname, ipv4, or ipv6)."
|
|
416
|
+
severity: error
|
|
417
|
+
given: "$..schema..[?(@.type=='string')]"
|
|
418
|
+
then:
|
|
419
|
+
field: format
|
|
420
|
+
function: pattern
|
|
421
|
+
functionOptions:
|
|
422
|
+
match: "^(date|date-time|password|byte|binary|email|uuid|uri|hostname|ipv4|ipv6)$"
|
|
423
|
+
apiq:OAR038:
|
|
424
|
+
description: "Response schema must be compliant with the standard."
|
|
425
|
+
message: "OAR038: Response property is required for status code '{{property}}'."
|
|
426
|
+
severity: error
|
|
427
|
+
given: "$.paths.*.*.responses..content..schema.properties"
|
|
428
|
+
then:
|
|
429
|
+
field: "@key"
|
|
430
|
+
function: pattern
|
|
431
|
+
functionOptions:
|
|
432
|
+
match: "^data|error$"
|
|
433
|
+
apiq:OAR039:
|
|
434
|
+
description: "Response codes must be defined according to the standard depending on the HTTP verb and resource path."
|
|
435
|
+
message: "{{error}}"
|
|
436
|
+
documentationUrl: https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR039.md
|
|
437
|
+
severity: error
|
|
438
|
+
given: "$.paths[*][*]"
|
|
439
|
+
then:
|
|
440
|
+
function: apq-standard-response-codes
|
|
441
|
+
functionOptions:
|
|
442
|
+
resources-exclusions:
|
|
443
|
+
- "get:/status"
|
|
444
|
+
required-codes-by-resources-paths: >
|
|
445
|
+
;get:^/[^/{}]+$:200|206,400,500,503
|
|
446
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+$:200|206,400,500,503,404
|
|
447
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+$:200|206,400,500,503,404
|
|
448
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
449
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
450
|
+
;get:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
451
|
+
;post:^/[^/{}]+$:200|201|202,400,415,500,503
|
|
452
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+$:200|201|202,400,415,500,503,404
|
|
453
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+$:200|201|202,400,415,500,503,404
|
|
454
|
+
;post:^/[^/{}]+/get$:200,404,400,415,500,503
|
|
455
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/get$:200,404,400,415,500,503
|
|
456
|
+
;post:^/[^/{}]+/delete$:200,404,400,415,500,503
|
|
457
|
+
;post:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/delete$:200,404,400,415,500,503
|
|
458
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
459
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
460
|
+
;put:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
461
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
462
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
463
|
+
;delete:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,400,404,500,503
|
|
464
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
465
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
466
|
+
;patch:^/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)/[^/{}]+/(\{[^/{}]+\}|\bme\b)$:200,404,400,415,500,503
|
|
467
|
+
apiq:OAR040:
|
|
468
|
+
description: "A scope name non-compliant with the standard may cause problems at application level."
|
|
469
|
+
message: "OAR040: Scope name must follow the 'apfc_sc_' prefix standard."
|
|
470
|
+
severity: "error"
|
|
471
|
+
given: "$.x-wso2-security.apim.x-wso2-scopes[*].name"
|
|
472
|
+
then:
|
|
473
|
+
function: pattern
|
|
474
|
+
functionOptions:
|
|
475
|
+
match: "^apfc_sc_.*"
|
|
476
|
+
apiq:OAR041:
|
|
477
|
+
description: Available scopes must be defined at the end of the document, at the root level, inside `x-wso2-security`.
|
|
478
|
+
message: "OAR041:Scopes must be defined in `x-wso2-security` at the end of the document, before the paths."
|
|
479
|
+
severity: "error"
|
|
480
|
+
given: "$.paths['/pets'].get"
|
|
481
|
+
then:
|
|
482
|
+
field: "x-aut-type"
|
|
483
|
+
function: truthy
|
|
484
|
+
apiq:OAR044:
|
|
485
|
+
description: "Declared media type should conform to RFC6838 and RFC7231."
|
|
486
|
+
message: "OAR044: Declared media type range should conform to RFC7231."
|
|
487
|
+
severity: "error"
|
|
488
|
+
given: "$.paths.*.*.responses.*.content.*~"
|
|
489
|
+
then:
|
|
490
|
+
field: name
|
|
491
|
+
function: pattern
|
|
492
|
+
functionOptions:
|
|
493
|
+
match: "^application\/[a-zA-Z0-9-_]+$"
|
|
494
|
+
apiq:OAR045:
|
|
495
|
+
description: "Response schema is required for responses with status codes 201 and others that return content."
|
|
496
|
+
message: "OAR045: Response schema is required for status code '{{property}}'."
|
|
497
|
+
severity: error
|
|
498
|
+
given: "$.paths[*][get,post,put,patch,delete].responses[?(@property !== '204')]"
|
|
499
|
+
then:
|
|
500
|
+
field: "content"
|
|
501
|
+
function: truthy
|
|
502
|
+
apiq:OAR046:
|
|
503
|
+
description: "Each operation SHOULD have a tag."
|
|
504
|
+
message: "OAR046: You should categorize the operations of your contract with tags."
|
|
505
|
+
severity: error
|
|
506
|
+
given:
|
|
507
|
+
- "$.paths[*].get"
|
|
508
|
+
- "$.paths[*].post"
|
|
509
|
+
- "$.paths[*].put"
|
|
510
|
+
- "$.paths[*].patch"
|
|
511
|
+
- "$.paths[*].delete"
|
|
512
|
+
then:
|
|
513
|
+
function: schema
|
|
514
|
+
functionOptions:
|
|
515
|
+
schema:
|
|
516
|
+
type: object
|
|
517
|
+
required: ["tags"]
|
|
518
|
+
properties:
|
|
519
|
+
tags:
|
|
520
|
+
type: array
|
|
521
|
+
minItems: 1
|
|
522
|
+
apiq:OAR047:
|
|
523
|
+
description: "Tags required and each tag must have a short description."
|
|
524
|
+
message: "OAR047: Add tags with a short description to each one."
|
|
525
|
+
severity: error
|
|
526
|
+
given: "$"
|
|
527
|
+
then:
|
|
528
|
+
- field: "tags"
|
|
529
|
+
function: schema
|
|
530
|
+
functionOptions:
|
|
531
|
+
schema:
|
|
532
|
+
type: array
|
|
533
|
+
minLength: 1
|
|
534
|
+
items:
|
|
535
|
+
type: object
|
|
536
|
+
required:
|
|
537
|
+
- name
|
|
538
|
+
- description
|
|
539
|
+
properties:
|
|
540
|
+
name:
|
|
541
|
+
type: string
|
|
542
|
+
minLength: 1
|
|
543
|
+
description:
|
|
544
|
+
type: string
|
|
545
|
+
minLength: 1
|
|
546
|
+
apiq:OAR048:
|
|
547
|
+
description: APIs must define at most one body parameter.
|
|
548
|
+
message: "OAR048: An operation can have at most one body parameter"
|
|
549
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR048.md"
|
|
550
|
+
severity: error
|
|
551
|
+
formats:
|
|
552
|
+
- oas2
|
|
553
|
+
given: "$.paths[*][*]"
|
|
554
|
+
then:
|
|
555
|
+
field: parameters
|
|
556
|
+
function: apq-at-most-one-body-parameter
|
|
557
|
+
apiq:OAR049:
|
|
558
|
+
description: "204 No Content MUST NOT return any content."
|
|
559
|
+
message: "OAR049: 204 No Content MUST NOT return any content."
|
|
560
|
+
severity: error
|
|
561
|
+
given: "$.paths[*][get,post,put,patch,delete].responses['204']"
|
|
562
|
+
then:
|
|
563
|
+
field: "content"
|
|
564
|
+
function: falsy
|
|
565
|
+
apiq:OAR050:
|
|
566
|
+
description: "Provide a summary for each operation."
|
|
567
|
+
message: "OAR050: Provide a summary for each operation."
|
|
568
|
+
severity: "error"
|
|
569
|
+
given: "$.paths[*][get,post,put,patch,delete]"
|
|
570
|
+
then:
|
|
571
|
+
field: "summary"
|
|
572
|
+
function: truthy
|
|
573
|
+
apiq:OAR051:
|
|
574
|
+
description: "Provide a different summary and description for each operation."
|
|
575
|
+
message: "OAR051: Provide a different summary and description for each operation."
|
|
576
|
+
severity: "error"
|
|
577
|
+
given: "$.paths[*].*"
|
|
578
|
+
then:
|
|
579
|
+
function: apq-compare-insensitive
|
|
580
|
+
functionOptions:
|
|
581
|
+
property: summary
|
|
582
|
+
equalTo: description
|
|
583
|
+
result: falsy
|
|
584
|
+
apiq:OAR052:
|
|
585
|
+
description: "Numeric schema types must define a format."
|
|
586
|
+
message: "OAR052: Numeric types requires a format"
|
|
587
|
+
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR052.md"
|
|
588
|
+
severity: warn
|
|
589
|
+
given: "$..[?(@.type == 'integer' || @.type == 'number')]"
|
|
590
|
+
then:
|
|
591
|
+
field: format
|
|
592
|
+
function: truthy
|
|
593
|
+
apiq:OAR054:
|
|
594
|
+
description: "Ensure the host matches the specified format"
|
|
595
|
+
message: "OAR054: Hostname must be a subdomain of the organization's domain name."
|
|
596
|
+
severity: error
|
|
597
|
+
given: "$.servers[*]"
|
|
598
|
+
then:
|
|
599
|
+
field: url
|
|
600
|
+
function: pattern
|
|
601
|
+
functionOptions:
|
|
602
|
+
match: ^(http(s)?:\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.apiquality.io\b([-a-zA-Z0-9@:%_\+.~#?&\/=]*)$
|
|
603
|
+
apiq:OAR060:
|
|
604
|
+
description: "All query parameters must be defined as optional."
|
|
605
|
+
message: "OAR060: All query parameter must be optional (required: false)."
|
|
606
|
+
severity: error
|
|
607
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')]"
|
|
608
|
+
then:
|
|
609
|
+
field: "required"
|
|
610
|
+
function: falsy
|
|
611
|
+
apiq:OAR061:
|
|
612
|
+
description: "Ensure get have mandatory response codes"
|
|
613
|
+
message: "OAR061: Ensure get have the mandatory response codes"
|
|
614
|
+
severity: error
|
|
615
|
+
given: "$.paths[?(!(@property === '/status' || @property === '/another'))].get.responses"
|
|
616
|
+
then:
|
|
617
|
+
function: schema
|
|
618
|
+
functionOptions:
|
|
619
|
+
schema:
|
|
620
|
+
oneOf:
|
|
621
|
+
- required: ['200']
|
|
622
|
+
- required: ['202']
|
|
623
|
+
- required: ['206']
|
|
624
|
+
not:
|
|
625
|
+
required: ['201', '204']
|
|
626
|
+
apiq:OAR062:
|
|
627
|
+
description: "Ensure post have mandatory response codes"
|
|
628
|
+
message: "OAR062: Ensure post have the mandatory response codes"
|
|
629
|
+
severity: error
|
|
630
|
+
given: "$.paths[?(!( @property == '/status' || @property == '/another' ))].post.responses"
|
|
631
|
+
then:
|
|
632
|
+
function: schema
|
|
633
|
+
functionOptions:
|
|
634
|
+
schema:
|
|
635
|
+
oneOf:
|
|
636
|
+
- required: ['200']
|
|
637
|
+
- required: ['201']
|
|
638
|
+
- required: ['202']
|
|
639
|
+
- required: ['204']
|
|
640
|
+
- required: ['206']
|
|
641
|
+
apiq:OAR063:
|
|
642
|
+
description: "Ensure put have mandatory response codes"
|
|
643
|
+
message: "OAR063: Ensure put have the mandatory response codes"
|
|
644
|
+
severity: error
|
|
645
|
+
given: "$.paths[?(!( @property == '/status' || @property == '/another' ))].put.responses"
|
|
646
|
+
then:
|
|
647
|
+
function: schema
|
|
648
|
+
functionOptions:
|
|
649
|
+
schema:
|
|
650
|
+
oneOf:
|
|
651
|
+
- required: ['200']
|
|
652
|
+
- required: ['202']
|
|
653
|
+
- required: ['204']
|
|
654
|
+
- required: ['206']
|
|
655
|
+
apiq:OAR064:
|
|
656
|
+
description: "Ensure patch have mandatory response codes"
|
|
657
|
+
message: "OAR064: Ensure patch have the mandatory response codes"
|
|
658
|
+
severity: error
|
|
659
|
+
given: "$.paths[?(!( @property == '/status' || @property == '/another' ))].patch.responses"
|
|
660
|
+
then:
|
|
661
|
+
function: schema
|
|
662
|
+
functionOptions:
|
|
663
|
+
schema:
|
|
664
|
+
oneOf:
|
|
665
|
+
- required: ['200']
|
|
666
|
+
- required: ['202']
|
|
667
|
+
- required: ['204']
|
|
668
|
+
- required: ['206']
|
|
669
|
+
apiq:OAR065:
|
|
670
|
+
description: "Ensure delete have mandatory response codes"
|
|
671
|
+
message: "OAR065: Ensure delete have the mandatory response codes"
|
|
672
|
+
severity: error
|
|
673
|
+
given: "$.paths[?(!( @property == '/status' || @property == '/another' ))].delete.responses"
|
|
674
|
+
then:
|
|
675
|
+
function: schema
|
|
676
|
+
functionOptions:
|
|
677
|
+
schema:
|
|
678
|
+
oneOf:
|
|
679
|
+
- required: ['200']
|
|
680
|
+
- required: ['202']
|
|
681
|
+
- required: ['204']
|
|
682
|
+
apiq:OAR066:
|
|
683
|
+
description: "RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
|
|
684
|
+
message: "OAR066: RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
|
|
685
|
+
severity: "warn"
|
|
686
|
+
given: "$.paths.*.*[responses,requestBody]..content..schema..properties.*~"
|
|
687
|
+
then:
|
|
688
|
+
function: casing
|
|
689
|
+
functionOptions:
|
|
690
|
+
type: snake
|
|
691
|
+
apiq:OAR067:
|
|
692
|
+
description: "RequestBody and Responses schema property names must be compliant with the camelCase naming convention."
|
|
693
|
+
message: "OAR067: RequestBody and Responses schema property names must be compliant with the camelCase naming convention."
|
|
694
|
+
severity: "warn"
|
|
695
|
+
given: "$.paths.*.*[responses,requestBody]..content..schema..properties.*~"
|
|
696
|
+
then:
|
|
697
|
+
function: casing
|
|
698
|
+
functionOptions:
|
|
699
|
+
type: camel
|
|
700
|
+
apiq:OAR068:
|
|
701
|
+
description: "RequestBody and Responses schema property names must be compliant with the PascalCase naming convention."
|
|
702
|
+
message: "OAR068: RequestBody and Responses schema property names must be compliant with the PascalCase naming convention."
|
|
703
|
+
severity: "warn"
|
|
704
|
+
given: "$.paths.*.*[responses,requestBody]..content..schema..properties.*~"
|
|
705
|
+
then:
|
|
706
|
+
function: casing
|
|
707
|
+
functionOptions:
|
|
708
|
+
type: pascal
|
|
709
|
+
apiq:OAR069:
|
|
710
|
+
description: "Any param in PATH or QUERY should have a Bad Request (400) response."
|
|
711
|
+
message: "OAR069: Any param in PATH or QUERY should have a Bad Request (400) response."
|
|
712
|
+
severity: "warn"
|
|
713
|
+
given: "$.paths[*][*].responses"
|
|
714
|
+
then:
|
|
715
|
+
function: truthy
|
|
716
|
+
field: "400"
|
|
717
|
+
message: "Operation must define a 400 Bad Request response for parameters in PATH or QUERY."
|
|
718
|
+
apiq:OAR070:
|
|
719
|
+
description: "Parameters in path should not be numeric."
|
|
720
|
+
message: "OAR070: Parameters in path should not be numeric."
|
|
721
|
+
severity: error
|
|
722
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'path')]"
|
|
723
|
+
then:
|
|
724
|
+
field: "schema.type"
|
|
725
|
+
function: pattern
|
|
726
|
+
functionOptions:
|
|
727
|
+
match: "^(?!integer|number)"
|
|
728
|
+
apiq:OAR071:
|
|
729
|
+
description: "Query parameters 'param1', 'param2', and 'param3' must be defined in the operation."
|
|
730
|
+
message: "OAR071: Query parameters 'param1', 'param2', and 'param3' must be defined."
|
|
731
|
+
severity: error
|
|
732
|
+
given: "$.paths[*].get.parameters"
|
|
733
|
+
then:
|
|
734
|
+
- field: "$[?(@.name == 'param1')]"
|
|
735
|
+
function: truthy
|
|
736
|
+
- field: "$[?(@.name == 'param2')]"
|
|
737
|
+
function: truthy
|
|
738
|
+
- field: "$[?(@.name == 'param3')]"
|
|
739
|
+
function: truthy
|
|
740
|
+
apiq:OAR072:
|
|
741
|
+
description: "Responses with status codes other than 200 must not include 'stacktrace'."
|
|
742
|
+
message: "The response with status code {{property}} must not include 'stacktrace'."
|
|
743
|
+
severity: error
|
|
744
|
+
given: "$.paths[*][get,post,put,patch,delete].responses[?(@property != '200')]"
|
|
745
|
+
then:
|
|
746
|
+
field: "content.application/json.schema.properties"
|
|
747
|
+
function: pattern
|
|
748
|
+
functionOptions:
|
|
749
|
+
notMatch: "stacktrace"
|
|
750
|
+
apiq:OAR073:
|
|
751
|
+
description: "API should include a 429 response to indicate rate limiting, except for the /status path."
|
|
752
|
+
message: "OAR073: API should include a 429 response to indicate rate limiting."
|
|
753
|
+
severity: error
|
|
754
|
+
given: "$.paths[?(@ !== '/status')][*].responses"
|
|
755
|
+
then:
|
|
756
|
+
field: "429"
|
|
757
|
+
function: truthy
|
|
758
|
+
apiq:OAR074:
|
|
759
|
+
description: "Numeric parameters should have minimum, maximum, or format restriction."
|
|
760
|
+
message: "OAR074: Numeric parameter '{{property}}' should have a minimum, maximum, or format restriction."
|
|
761
|
+
severity: error
|
|
762
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.schema.type == 'integer' || @.schema.type == 'number')]"
|
|
763
|
+
then:
|
|
764
|
+
function: schema
|
|
765
|
+
functionOptions:
|
|
766
|
+
schema:
|
|
767
|
+
type: object
|
|
768
|
+
properties:
|
|
769
|
+
schema:
|
|
770
|
+
anyOf:
|
|
771
|
+
- required: ["minimum"]
|
|
772
|
+
- required: ["maximum"]
|
|
773
|
+
- required: ["format"]
|
|
774
|
+
apiq:OAR075:
|
|
775
|
+
description: "String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction."
|
|
776
|
+
message: "OAR075: String parameters should have minLength, maxLength, pattern, or enum restriction."
|
|
777
|
+
severity: error
|
|
778
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.schema.type == 'string' && @.in == 'path')]"
|
|
779
|
+
then:
|
|
780
|
+
function: schema
|
|
781
|
+
functionOptions:
|
|
782
|
+
schema:
|
|
783
|
+
type: object
|
|
784
|
+
properties:
|
|
785
|
+
schema:
|
|
786
|
+
anyOf:
|
|
787
|
+
- required: ["minLength"]
|
|
788
|
+
- required: ["maxLength"]
|
|
789
|
+
- required: ["pattern"]
|
|
790
|
+
- required: ["enum"]
|
|
791
|
+
apiq:OAR076:
|
|
792
|
+
description: "Schema should use well-defined type and format."
|
|
793
|
+
message: "OAR076: Invalid combination: type '{{value.type}}' with format '{{value.format}}'"
|
|
794
|
+
severity: "error"
|
|
795
|
+
given: "$..[?(@.type=='number')]"
|
|
796
|
+
then:
|
|
797
|
+
field: format
|
|
798
|
+
function: enumeration
|
|
799
|
+
functionOptions:
|
|
800
|
+
values:
|
|
801
|
+
- float
|
|
802
|
+
- double
|
|
803
|
+
apiq:OAR077:
|
|
804
|
+
description: "All parameters in query must be snake_case."
|
|
805
|
+
message: "OAR077: All parameters in query must be snake_case."
|
|
806
|
+
severity: warn
|
|
807
|
+
given: "$.paths[*][*].parameters[?(@.in == 'query')]"
|
|
808
|
+
then:
|
|
809
|
+
field: "name"
|
|
810
|
+
function: pattern
|
|
811
|
+
functionOptions:
|
|
812
|
+
match: ^\$?_?[a-z]+(_[a-z]+)*$
|
|
813
|
+
apiq:OAR078:
|
|
814
|
+
description: "All API methods must have security defined."
|
|
815
|
+
message: "OAR078: The operation '{{property}}' must have security defined."
|
|
816
|
+
severity: error
|
|
817
|
+
given: "$.paths[*][get,post,put,patch,delete]"
|
|
818
|
+
then:
|
|
819
|
+
field: "security"
|
|
820
|
+
function: truthy
|
|
821
|
+
apiq:OAR079:
|
|
822
|
+
description: "Operations with path parameters should include a 404 Not Found response."
|
|
823
|
+
message: "OAR079: Path parameter present, but missing 404 Not Found response."
|
|
824
|
+
severity: "warn"
|
|
825
|
+
given: "$.paths[*][*]"
|
|
826
|
+
then:
|
|
827
|
+
field: responses.404
|
|
828
|
+
function: truthy
|
|
829
|
+
apiq:OAR080:
|
|
830
|
+
description: "The security scheme must be among those allowed by the organization and must be complete."
|
|
831
|
+
message: "OAR080: The security scheme '{{property}}' must be among those allowed by the organization and must be complete."
|
|
832
|
+
severity: error
|
|
833
|
+
given: "$.paths[*][get,post,put,patch,delete].security[*]"
|
|
834
|
+
then:
|
|
835
|
+
field: "@key"
|
|
836
|
+
function: pattern
|
|
837
|
+
functionOptions:
|
|
838
|
+
match: "^(apiKey|oauth2)$"
|
|
839
|
+
apiq:OAR081:
|
|
840
|
+
description: "Fields of type password should be string with format password."
|
|
841
|
+
message: "OAR081: Fields of type password should be string with format password."
|
|
842
|
+
severity: error
|
|
843
|
+
given: "$..properties[?(@.type == 'string' && @.format == 'number')]"
|
|
844
|
+
then:
|
|
845
|
+
field: "format"
|
|
846
|
+
function: pattern
|
|
847
|
+
functionOptions:
|
|
848
|
+
match: "^password$"
|
|
849
|
+
apiq:OAR082:
|
|
850
|
+
description: "The string properties must define a byte or binary format."
|
|
851
|
+
message: "OAR082: The string properties must define a 'byte' or 'binary' format."
|
|
852
|
+
severity: error
|
|
853
|
+
given: "$..properties.*"
|
|
854
|
+
then:
|
|
855
|
+
field: format
|
|
856
|
+
function: pattern
|
|
857
|
+
functionOptions:
|
|
858
|
+
match: "^(byte|binary)$"
|
|
859
|
+
apiq:OAR083:
|
|
860
|
+
description: "Certain parameters (e.g., email, password) should not pass through the querystring."
|
|
861
|
+
message: "OAR083: The parameter '{{value}}' should not pass through the querystring."
|
|
862
|
+
severity: error
|
|
863
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')]"
|
|
864
|
+
then:
|
|
865
|
+
field: "name"
|
|
866
|
+
function: pattern
|
|
867
|
+
functionOptions:
|
|
868
|
+
notMatch: "^(email|password)$"
|
|
869
|
+
apiq:OAR084:
|
|
870
|
+
description: "The format password should not pass through the querystring."
|
|
871
|
+
message: "OAR084: The format password should not be used in querystring parameters."
|
|
872
|
+
severity: error
|
|
873
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')]"
|
|
874
|
+
then:
|
|
875
|
+
field: "schema.format"
|
|
876
|
+
function: pattern
|
|
877
|
+
functionOptions:
|
|
878
|
+
notMatch: "^password$"
|
|
879
|
+
apiq:OAR085:
|
|
880
|
+
description: "The OpenAPI version must be one of: 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.1.0."
|
|
881
|
+
message: "OAR085: The OpenAPI version '{{value}}' is not allowed. Allowed versions: 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.1.0."
|
|
882
|
+
severity: warn
|
|
883
|
+
given: "$.openapi"
|
|
884
|
+
then:
|
|
885
|
+
function: pattern
|
|
886
|
+
functionOptions:
|
|
887
|
+
match: "^(2\\.0|3\\.0\\.0|3\\.0\\.1|3\\.0\\.2|3\\.0\\.3|3\\.1\\.0)$"
|
|
888
|
+
apiq:OAR086:
|
|
889
|
+
description: "Descriptions must begin with a capital letter, end with a period, and not be empty."
|
|
890
|
+
message: "OAR086: Descriptions must begin with a capital letter, end with a period, and not be empty."
|
|
891
|
+
severity: "warn"
|
|
892
|
+
given: "$..description"
|
|
893
|
+
then:
|
|
894
|
+
function: pattern
|
|
895
|
+
functionOptions:
|
|
896
|
+
match: "^[A-Z][\\s\\S]*\\.$"
|
|
897
|
+
apiq:OAR087:
|
|
898
|
+
description: "Summaries must begin with a capital letter, end with a period, and not be empty."
|
|
899
|
+
message: "OAR087: Summaries must begin with a capital letter, end with a period, and not be empty."
|
|
900
|
+
severity: "warn"
|
|
901
|
+
given: "$..summary"
|
|
902
|
+
then:
|
|
903
|
+
function: pattern
|
|
904
|
+
functionOptions:
|
|
905
|
+
match: "^[A-Z][\\s\\S]*\\.$"
|
|
906
|
+
apiq:OAR088:
|
|
907
|
+
description: "The $ref of a parameter must end with the suffix Param."
|
|
908
|
+
message: "OAR088: The $ref of a parameter must end with the suffix Param."
|
|
909
|
+
severity: "warn"
|
|
910
|
+
given: "$..parameters[*].$ref"
|
|
911
|
+
then:
|
|
912
|
+
function: pattern
|
|
913
|
+
functionOptions:
|
|
914
|
+
match: "Param$"
|
|
915
|
+
apiq:OAR089:
|
|
916
|
+
description: "The $ref of a request body must end with the suffix Body."
|
|
917
|
+
message: "OAR089: The $ref of a request body must end with the suffix Body."
|
|
918
|
+
severity: "warn"
|
|
919
|
+
given: "$..requestBody.$ref"
|
|
920
|
+
then:
|
|
921
|
+
function: pattern
|
|
922
|
+
functionOptions:
|
|
923
|
+
match: "Body$"
|
|
924
|
+
apiq:OAR090:
|
|
925
|
+
description: "The $ref of a response must end with the suffix Response."
|
|
926
|
+
message: "OAR090: The $ref of a response must end with the suffix Response"
|
|
927
|
+
severity: error
|
|
928
|
+
given: "$.paths['/sample'].get.responses['200'].$ref"
|
|
929
|
+
then:
|
|
930
|
+
function: pattern
|
|
931
|
+
functionOptions:
|
|
932
|
+
match: ".*Response$"
|
|
933
|
+
resolved: false
|
|
934
|
+
apiq:OAR091:
|
|
935
|
+
description: "Parameters must contain only $ref references."
|
|
936
|
+
message: "OAR091: Parameters must contain only $ref references."
|
|
937
|
+
severity: error
|
|
938
|
+
given: "$.paths[*][get,post,put,patch,delete].parameters[*].$ref"
|
|
939
|
+
then:
|
|
940
|
+
function: truthy
|
|
941
|
+
apiq:OAR092:
|
|
942
|
+
description: "RequestBody must contain a $ref."
|
|
943
|
+
message: "OAR092: RequestBody must contain a $ref reference."
|
|
944
|
+
severity: error
|
|
945
|
+
given: "$.paths[*][get,post,put,patch,delete].requestBody[*].$ref"
|
|
946
|
+
then:
|
|
947
|
+
function: truthy
|
|
948
|
+
apiq:OAR093:
|
|
949
|
+
description: "RequestBody must contain only references ($ref)."
|
|
950
|
+
message: "OAR093: RequestBody must contain only references ($ref)."
|
|
951
|
+
severity: error
|
|
952
|
+
given: "$.paths[*][get,post,put,patch,delete].responses[*].$ref"
|
|
953
|
+
then:
|
|
954
|
+
function: truthy
|
|
955
|
+
apiq:OAR094:
|
|
956
|
+
description: "Examples must be used instead of example in the content definition for better tool compatibility."
|
|
957
|
+
message: "OAR094: Examples must be used instead of example in the content definition for better tool compatibility."
|
|
958
|
+
severity: warn
|
|
959
|
+
given: "$..content[*].example"
|
|
960
|
+
then:
|
|
961
|
+
function: falsy
|
|
962
|
+
apiq:OAR096:
|
|
963
|
+
description: "Response code 403 must be defined for operations with security schemes defined."
|
|
964
|
+
message: "OAR096: Response code 403 must be defined for operations with security schemes defined."
|
|
965
|
+
severity: "error"
|
|
966
|
+
given: "$.paths[*][*].responses"
|
|
967
|
+
then:
|
|
968
|
+
function: truthy
|
|
969
|
+
field: "403"
|
|
970
|
+
message: "Operation must define a 403 Unauthorized response for security schemes."
|
|
971
|
+
apiq:OAR097:
|
|
972
|
+
description: "The base path must contain at least two parts."
|
|
973
|
+
message: "OAR097: Path has too few parts."
|
|
974
|
+
severity: "error"
|
|
975
|
+
given: "$.servers[*].url"
|
|
976
|
+
then:
|
|
977
|
+
function: pattern
|
|
978
|
+
functionOptions:
|
|
979
|
+
match: "^https?://[^/]+/[^/]+/[^/]+"
|
|
980
|
+
apiq:OAR098:
|
|
981
|
+
description: "The base path must not contain more than two parts."
|
|
982
|
+
message: "OAR098: Path has too many parts."
|
|
983
|
+
severity: "error"
|
|
984
|
+
given: "$.servers[*].url"
|
|
985
|
+
then:
|
|
986
|
+
function: pattern
|
|
987
|
+
functionOptions:
|
|
988
|
+
notMatch: "^https?://[^/]+/([^/]+/){2,}[^/]+"
|
|
989
|
+
apiq:OAR099:
|
|
990
|
+
description: "API name must start with prefix 'api-'."
|
|
991
|
+
message: "OAR099: API name must start with prefix 'api-'."
|
|
992
|
+
severity: "error"
|
|
993
|
+
given:
|
|
994
|
+
- "$.servers[*].url"
|
|
995
|
+
- "$.paths[*]~"
|
|
996
|
+
- "$.basePath"
|
|
997
|
+
then:
|
|
998
|
+
function: pattern
|
|
999
|
+
functionOptions:
|
|
1000
|
+
match: ".*?/api-[^/]+/v[0-9]+"
|
|
1001
|
+
apiq:OAR100:
|
|
1002
|
+
description: "Last path part must be the API version, indicated with the prefix 'v' and the version number as integer."
|
|
1003
|
+
message: "OAR100: Last path part must be the API version, indicated with the prefix 'v' and the version number as integer."
|
|
1004
|
+
severity: "error"
|
|
1005
|
+
given: "$.servers[*].url"
|
|
1006
|
+
then:
|
|
1007
|
+
function: pattern
|
|
1008
|
+
functionOptions:
|
|
1009
|
+
match: "^https?://[^/]+/.*/v[0-9]+$"
|
|
1010
|
+
apiq:OAR101:
|
|
1011
|
+
description: "The first part of the path should be one of the allowed paths (e.g., '/hello')."
|
|
1012
|
+
message: "OAR101: The first part of the path should be one of the allowed paths."
|
|
1013
|
+
severity: "error"
|
|
1014
|
+
given: "$.servers[*].url"
|
|
1015
|
+
then:
|
|
1016
|
+
function: pattern
|
|
1017
|
+
functionOptions:
|
|
1018
|
+
match: "^https?://[^/]+/hello/.*$"
|
|
1019
|
+
apiq:OAR102:
|
|
1020
|
+
description: "The second part of the path should be one of the allowed values."
|
|
1021
|
+
message: "OAR102: The second part of the path should be one of the allowed values."
|
|
1022
|
+
severity: "error"
|
|
1023
|
+
given: "$.servers[*].url"
|
|
1024
|
+
then:
|
|
1025
|
+
function: pattern
|
|
1026
|
+
functionOptions:
|
|
1027
|
+
match: "^https?://[^/]+/hola/hello$"
|
|
1028
|
+
apiq:OAR103:
|
|
1029
|
+
description: "GET requests are not recommended for resource paths containing 'get' or 'delete', as this may indicate a design flaw."
|
|
1030
|
+
message: "OAR103: GET request should not be used on paths containing 'get' or 'delete'."
|
|
1031
|
+
severity: error
|
|
1032
|
+
given: "$.paths[?(@property.match(/(get|delete)/))].get"
|
|
1033
|
+
then:
|
|
1034
|
+
function: falsy
|
|
1035
|
+
apiq:OAR104:
|
|
1036
|
+
description: "POST requests should not be used on paths ending with 'me' or a templated parameter."
|
|
1037
|
+
message: "OAR104: POST requests should not target paths ending in 'me' or a path parameter like '{id}'."
|
|
1038
|
+
severity: error
|
|
1039
|
+
given: "$.paths[?(/\\/(me|{[^}]+})$/.test(@property))].post"
|
|
1040
|
+
then:
|
|
1041
|
+
function: falsy
|
|
1042
|
+
apiq:OAR105:
|
|
1043
|
+
description: "PUT requests are not recommended for resource paths containing 'get' or 'delete', as this may indicate a design flaw."
|
|
1044
|
+
message: "OAR105: PUT request should not be used on paths containing 'get' or 'delete'."
|
|
1045
|
+
severity: error
|
|
1046
|
+
given: "$.paths[?(@property.match(/(get|delete)/))].put"
|
|
1047
|
+
then:
|
|
1048
|
+
function: falsy
|
|
1049
|
+
apiq:OAR106:
|
|
1050
|
+
description: "PATCH requests are not recommended for resource paths containing 'get' or 'delete', as this may indicate a design flaw."
|
|
1051
|
+
message: "OAR106: PATCH request should not be used on paths containing 'get' or 'delete'."
|
|
1052
|
+
severity: error
|
|
1053
|
+
given: "$.paths[?(@property.match(/(get|delete)/))].patch"
|
|
1054
|
+
then:
|
|
1055
|
+
function: falsy
|
|
1056
|
+
apiq:OAR107:
|
|
1057
|
+
description: "DELETE requests are not recommended for resource paths containing 'get' or 'delete', as this may indicate a design flaw."
|
|
1058
|
+
message: "OAR107: DELETE request should not be used on paths containing 'get' or 'delete'."
|
|
1059
|
+
severity: error
|
|
1060
|
+
given: "$.paths[?(@property.match(/(get|delete)/))].delete"
|
|
1061
|
+
then:
|
|
1062
|
+
function: falsy
|
|
1063
|
+
apiq:OAR108:
|
|
1064
|
+
description: "The schemas should match the provided examples."
|
|
1065
|
+
message: "OAR108: Schema does not match the provided example."
|
|
1066
|
+
severity: "error"
|
|
1067
|
+
given:
|
|
1068
|
+
- "$.paths['/item'].get.responses['200'].content['application/json'].example"
|
|
1069
|
+
- "$.paths['/item'].get.responses['200'].examples['application/json']"
|
|
1070
|
+
then:
|
|
1071
|
+
- function: "schema"
|
|
1072
|
+
functionOptions:
|
|
1073
|
+
schema:
|
|
1074
|
+
type: "object"
|
|
1075
|
+
properties:
|
|
1076
|
+
id:
|
|
1077
|
+
type: "integer"
|
|
1078
|
+
nombre:
|
|
1079
|
+
type: "string"
|
|
1080
|
+
apiq:OAR109:
|
|
1081
|
+
description: "Use default response instead of directly specifying 5XX codes."
|
|
1082
|
+
message: "OAR109: Use default response instead of specifying 5XX codes directly."
|
|
1083
|
+
severity: error
|
|
1084
|
+
given: "$.paths[*][get,post,put,patch,delete].responses"
|
|
1085
|
+
then:
|
|
1086
|
+
field: "$[?(@property.match(/^5[0-9][0-9]$/))]"
|
|
1087
|
+
function: falsy
|
|
1088
|
+
apiq:OAR110:
|
|
1089
|
+
description: "License information cannot be empty."
|
|
1090
|
+
message: "OAR110: License information cannot be empty."
|
|
1091
|
+
severity: "error"
|
|
1092
|
+
given: "$.info"
|
|
1093
|
+
then:
|
|
1094
|
+
field: "license"
|
|
1095
|
+
function: truthy
|
|
1096
|
+
apiq:OAR111:
|
|
1097
|
+
description: "Contact information cannot be empty."
|
|
1098
|
+
message: "OAR111: Contact information cannot be empty."
|
|
1099
|
+
severity: "error"
|
|
1100
|
+
given: "$.info"
|
|
1101
|
+
then:
|
|
1102
|
+
field: "contact"
|
|
1103
|
+
function: truthy
|
|
1104
|
+
apiq:OAR114:
|
|
1105
|
+
description: There are mandatory response headers and others that are not allowed.
|
|
1106
|
+
message: "OAR114: Missing mandatory response x-api-key and x-trace-id headers, or forbidden headers are present."
|
|
1107
|
+
documentationUrl: https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR114.md
|
|
1108
|
+
severity: error
|
|
1109
|
+
given: "$.paths[*][*].responses[*].headers"
|
|
1110
|
+
then:
|
|
1111
|
+
- function: schema
|
|
1112
|
+
functionOptions:
|
|
1113
|
+
schema:
|
|
1114
|
+
type: object
|
|
1115
|
+
required:
|
|
1116
|
+
- x-api-key
|
|
1117
|
+
- x-trace-id
|
|
1118
|
+
propertyNames:
|
|
1119
|
+
pattern: "^(x-api-key|x-trace-id|traceId|dateTime)$"
|