diff --git a/CHANGELOG.md b/CHANGELOG.md index ed5617c8..4cc4ea57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,96 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6.0] - 2026-09-10 + +Recopilado de `1.6.0-beta-1` a `1.6.0-beta-5`. + +### Added + +- OAR060 - QueryParametersOptional - New `path-exclusions` rule property (default `/status`): a comma-separated list of exact, case-sensitive paths the rule must not fire on. +- OAR116 - PathPattern - New rule: every API path must match a configurable regex `pattern` (default `^/`); unanchored match, dynamic message with the configured pattern. +- OAR085 - Accept `3.0.4`, `3.1.1`, `3.1.2` in the default valid-versions. +- OAR022 - OrderbyParameter - New `parameterName` rule property (default `$orderby`), previously hardcoded; now configurable like OAR020/021/028. +- OAR025 - LimitParameter - Same as OAR022: new `parameterName` rule property (default `$limit`), previously hardcoded. + +### Changed + +- Bump `sonar-openapi` core to `1.3.0-beta-2`: parses `3.0.4`/`3.1.1`/`3.1.2`, and a document declaring an unsupported `openapi`/`swagger` version is now analysed instead of being silently skipped. +- `JsonNodeUtils` - `isType`/`getPrimaryType` accept array-form `type` (OpenAPI 3.1). +- OAR082 - Accept array-form `type`; accept `contentEncoding`/`contentMediaType` as byte/binary. +- OAR029 / OAR070 / OAR074 / OAR075 / OAR108 / OAR115 - Accept array-form `type`. +- OAR016 / OAR037 / OAR052 / OAR076 - Accept array-form `type` via `AbstractFormatCheck`. +- OAR075 - StringParameterIntegrityCheck - Default `parameter_integrity` constraints changed from `minLength,maxLength,enum,format` to `minLength,maxLength,pattern,enum`. + +### Fixed + +- OAR002 - Rewrote to validate the full `x-wso2-scopes` definition (null/empty container and missing/null/blank or empty-array/object `name`/`key`/`roles`) via new `apq-wso2-scopes-valid`; also detects an attribute or container written with no value (`roles:`), the `~`/`Null`/`NULL` spellings of null, and anchors map-form scope defects on the scope key. +- OAR044 - MediaTypeCheck - Made the media type regex quantifiers possessive to prevent ReDoS with no change to matching. +- OAR003 - Resolve a `$ref` on `x-wso2-security` and iterate map-form `x-wso2-scopes` (shared `AbstractWso2ScopesCheck`), so referenced and mapping-keyed scopes are detected. +- OAR029 - StandardResponseSchemaCheck - Fixed crash (`NoSuchElementException`) when `rootProperty: "*"` and the schema has no properties; root-node resolution now returns safely instead of throwing. +- OAR108 - SchemaValidatorCheck - Example type detection rewritten to use the YAML token type instead of string pattern-matching, fixing misclassification of quoted numbers; `integer`/`number` and `null` are now treated as compatible with their schema type. +- OAR075 - StringParameterIntegrityCheck - Fixed any-of integrity check that required all configured constraints (`allMatch`) instead of at least one (`anyMatch`). + + + +## [1.6.0-beta-5] - 2026-09-09 + +### Added + +- OAR022 - OrderbyParameter - New `parameterName` rule property (default `$orderby`), previously hardcoded; now configurable like OAR020/021/028. +- OAR025 - LimitParameter - Same as OAR022: new `parameterName` rule property (default `$limit`), previously hardcoded. + +## [1.6.0-beta-4] - 2026-09-04 + +### Fixed + +- OAR029 - StandardResponseSchemaCheck - Fixed crash (`NoSuchElementException`) when `rootProperty: "*"` and the schema has no properties; root-node resolution now returns safely instead of throwing. +- OAR108 - SchemaValidatorCheck - Example type detection rewritten to use the YAML token type instead of string pattern-matching, fixing misclassification of quoted numbers; `integer`/`number` and `null` are now treated as compatible with their schema type. +- OAR075 - StringParameterIntegrityCheck - Fixed any-of integrity check that required all configured constraints (`allMatch`) instead of at least one (`anyMatch`); default constraints changed from `minLength,maxLength,enum,format` to `minLength,maxLength,pattern,enum`. + +## [1.6.0-beta-3] - 2026-09-02 + +### Fixed + +- OAR002 - Detect an attribute or container written with no value (`roles:`), the `~`/`Null`/`NULL` spellings of null, and anchor map-form scope defects on the scope key. +- OAR044 - MediaTypeCheck - Made the media type regex quantifiers possessive to prevent ReDoS with no change to matching. +- OAR003 - Resolve a `$ref` on `x-wso2-security` and iterate map-form `x-wso2-scopes` (shared `AbstractWso2ScopesCheck`), so referenced and mapping-keyed scopes are detected. + +### Added + +- OAR060 - QueryParametersOptional - New `path-exclusions` rule property (default `/status`): a comma-separated list of exact, case-sensitive paths the rule must not fire on. +- OAR116 - PathPattern - New rule: every API path must match a configurable regex `pattern` (default `^/`); unanchored match, dynamic message with the configured pattern. + + +## [1.6.0-beta-2] - 2026-09-01 + +### Changed + +- Bump `sonar-openapi` core to `1.3.0-beta-2`: a document declaring an unsupported `openapi` / `swagger` version is now analysed instead of being silently skipped. + +## [1.6.0-beta-1] - 2026-08-28 + +### Fixed + +- OAR002 - Rewrote to validate the full `x-wso2-scopes` definition (null/empty container and missing/null/blank or empty-array/object `name`/`key`/`roles`) via new `apq-wso2-scopes-valid`. + +### Added + +- OAR085 - Accept `3.0.4`, `3.1.1`, `3.1.2` in the default valid-versions. + +### Changed + +- Bump `sonar-openapi` core to `1.3.0-beta-1` (parses the new versions). +- `JsonNodeUtils` - `isType`/`getPrimaryType` accept array-form `type` (OpenAPI 3.1). +- OAR082 - Accept array-form `type`; accept `contentEncoding`/`contentMediaType` as byte/binary. +- OAR029 - Accept array-form `type`. +- OAR070 - Accept array-form `type`. +- OAR074 - Accept array-form `type`. +- OAR075 - Accept array-form `type`. +- OAR108 - Accept array-form `type`. +- OAR115 - Accept array-form `type`. +- OAR016 / OAR037 / OAR052 / OAR076 - Accept array-form `type` via `AbstractFormatCheck`. + ## [1.5.1] - 2026-08-25 @@ -36,7 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - OAR025 - The shared `apq-collection-query-param-required` function now also validates the parameter type for OAR025, keyed by rule code; when `$limit` is present but its type is not `integer`, a distinct type message is emitted. -## [1.5.1-beta-3] - 2026-08-14 +# [1.5.1-beta-3] - 2026-08-14 ### Changed diff --git a/pom.xml b/pom.xml index de86ba15..dc70ccd2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.5.1 + 1.6.0 sonar-plugin SonarQube OpenAPI Community Rules @@ -64,7 +64,7 @@ 8.7.0.41497 6.7 - 1.2.1 + 1.3.0 1.22.0.848 20231013 4.13.2 diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/RulesLists.java b/src/main/java/apiaddicts/sonar/openapi/checks/RulesLists.java index 8f855576..df069337 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/RulesLists.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/RulesLists.java @@ -52,7 +52,8 @@ public static List> getFormatChecks() { OAR110LicenseInformationCheck.class, OAR111ContactInformationCheck.class, OAR113CustomFieldCheck.class, - OAR115VerifyRequiredFields.class + OAR115VerifyRequiredFields.class, + OAR116PathPatternCheck.class ); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2ScopesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2ScopesCheck.java index e87b71ff..7a0d5899 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2ScopesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2ScopesCheck.java @@ -7,13 +7,17 @@ import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; import apiaddicts.sonar.openapi.checks.BaseCheck; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import java.util.ArrayList; import java.util.List; import java.util.Set; public abstract class AbstractWso2ScopesCheck extends BaseCheck { + private JsonNode scopesKeyNode; + @Override public Set subscribedKinds() { return ImmutableSet.of(OpenApi2Grammar.ROOT, OpenApi3Grammar.ROOT, OpenApi31Grammar.ROOT, OpenApi32Grammar.ROOT); @@ -24,11 +28,25 @@ public void visitNode(JsonNode node) { visitV2NV3Node(node); } + protected JsonNode scopesKeyNode() { + return scopesKeyNode; + } + private void visitV2NV3Node(JsonNode node) { - JsonNode scopesNode = node.get("x-wso2-security").get("apim").get("x-wso2-scopes"); + JsonNode securityNode = node.get("x-wso2-security"); + if (!securityNode.isMissing()) securityNode = JsonNodeUtils.resolve(securityNode); + JsonNode apimNode = securityNode.get("apim"); + JsonNode scopesNode = apimNode.get("x-wso2-scopes"); + scopesKeyNode = JsonNodeUtils.propertyKey(apimNode, "x-wso2-scopes"); visitScopesNode(scopesNode); if (scopesNode.isMissing() || scopesNode.isNull()) return; - List scopes = scopesNode.elements(); + List rawScopes = scopesNode.isObject() + ? new ArrayList<>(scopesNode.propertyMap().values()) + : scopesNode.elements(); + List scopes = new ArrayList<>(rawScopes.size()); + for (JsonNode scope : rawScopes) { + scopes.add(JsonNodeUtils.resolve(scope)); + } visitScopes(scopes); scopes.forEach(this::visitScope); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheck.java index 71d669f0..e588b1e0 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheck.java @@ -1,10 +1,14 @@ package apiaddicts.sonar.openapi.checks.apim.wso2; +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.Token; import org.sonar.check.Rule; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import java.util.List; import java.util.Map; +import java.util.Set; @Rule(key = OAR002ValidWso2ScopesCheck.KEY) public class OAR002ValidWso2ScopesCheck extends AbstractWso2ScopesCheck { @@ -12,12 +16,17 @@ public class OAR002ValidWso2ScopesCheck extends AbstractWso2ScopesCheck { public static final String KEY = "OAR002"; private static final String MESSAGE = "OAR002.error"; private static final String MESSAGE_PROP = "OAR002.error-property"; + private static final Set NULL_SPELLINGS = ImmutableSet.of("~", "Null", "NULL"); private JsonNode scopesNode; @Override protected void visitScopesNode(JsonNode scopesNode) { - if (scopesNode.isNull()) addIssue(KEY, translate(MESSAGE), scopesNode.key()); + if (scopesNode.isNull()) { + addIssue(KEY, translate(MESSAGE), scopesNode.key()); + } else if (scopesNode.isMissing() && scopesKeyNode() != null) { + addIssue(KEY, translate(MESSAGE), scopesKeyNode()); + } this.scopesNode = scopesNode; } @@ -36,10 +45,33 @@ protected void visitScope(JsonNode scope) { private void validateProperty(Map properties, String propertyName, JsonNode scope) { JsonNode property = properties.get(propertyName); - if (!properties.containsKey(propertyName)) { - addIssue(KEY, translate(MESSAGE_PROP, propertyName), scope); - } else if(property.isNull() || property.getTokenValue().trim().equals("")) { + if (property == null) { + addIssue(KEY, translate(MESSAGE_PROP, propertyName), scopeLocation(scope)); + return; + } + if (property.isMissing()) { + JsonNode key = JsonNodeUtils.propertyKey(scope, propertyName); + addIssue(KEY, translate(MESSAGE_PROP, propertyName), key != null ? key : scopeLocation(scope)); + } else if (isEmpty(property)) { addIssue(KEY, translate(MESSAGE_PROP, propertyName), property.key()); } } + + private JsonNode scopeLocation(JsonNode scope) { + JsonNode key = scope.key(); + return key.isMissing() ? scope : key; + } + + private boolean isEmpty(JsonNode property) { + if (isNullScalar(property)) return true; + if (property.isArray()) return property.elements().isEmpty(); + if (property.isObject()) return property.propertyMap().isEmpty(); + return property.getTokenValue().trim().equals(""); + } + + private boolean isNullScalar(JsonNode property) { + if (property.isNull()) return true; + Token token = property.getToken(); + return token != null && NULL_SPELLINGS.contains(token.getOriginalValue()); + } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java index adb15cde..5f54f027 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java @@ -7,6 +7,7 @@ import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; import apiaddicts.sonar.openapi.checks.BaseCheck; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import java.util.Set; @@ -25,7 +26,7 @@ public void visitNode(JsonNode node) { private void visitV2Node(JsonNode node) { JsonNode typeNode = node.get("type"); - String type = typeNode.getTokenValue(); + String type = JsonNodeUtils.getPrimaryType(typeNode); JsonNode formatNode = node.get("format"); if (formatNode.isMissing()) { validate(type, null, typeNode, node); diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java index 39656827..b3446786 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java @@ -45,11 +45,11 @@ public class OAR044MediaTypeCheck extends BaseCheck { private final ExternalRefHandler handleExternalRef = new ExternalRefHandler(); private static final String RESTRICTED_NAME = "[a-zA-Z0-9][a-zA-Z0-9.!#$&^_+\\-]*"; - private static final String OWS = "[ \\t]*"; - private static final String TOKEN = "[a-zA-Z0-9!#$%&'*+\\-.^_`|~]+"; - private static final String QUOTED_STRING = "\"(?:[^\"\\\\]|\\\\.)*\""; + private static final String OWS = "[ \\t]*+"; + private static final String TCHARS = "[a-zA-Z0-9!#$%&'*+\\-.^_`|~]++"; + private static final String QUOTED_STRING = "\"(?:[^\"\\\\]|\\\\.)*+\""; private static final String PARAMETERS = - "(?:" + OWS + ";" + OWS + TOKEN + "=(?:" + TOKEN + "|" + QUOTED_STRING + "))*"; + "(?:" + OWS + ";" + OWS + TCHARS + "=(?:" + TCHARS + "|" + QUOTED_STRING + "))*+"; @VisibleForTesting static final Pattern MIME_TYPE_PATTERN = Pattern.compile( diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR115VerifyRequiredFields.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR115VerifyRequiredFields.java index 235065ec..01ac76e2 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR115VerifyRequiredFields.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR115VerifyRequiredFields.java @@ -2,6 +2,7 @@ import apiaddicts.sonar.openapi.checks.BaseCheck; import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.isExternalRef; +import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.isObjectType; import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.resolve; import com.google.common.collect.ImmutableSet; import com.sonar.sslr.api.AstNodeType; @@ -58,7 +59,7 @@ public void resolveExteralRef(JsonNode node) { public void verifyTypeObject(JsonNode node){ JsonNode typeNode = node.get("type"); - if (typeNode != null && "object".equals(typeNode.getTokenValue())) { + if (typeNode != null && isObjectType(typeNode)) { validateRequiredFields(node); } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheck.java new file mode 100644 index 00000000..c4eb3c98 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheck.java @@ -0,0 +1,53 @@ +package apiaddicts.sonar.openapi.checks.format; + +import java.util.Set; +import java.util.regex.Pattern; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; + +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +@Rule(key = OAR116PathPatternCheck.KEY) +public class OAR116PathPatternCheck extends BaseCheck { + + public static final String KEY = "OAR116"; + private static final String MESSAGE = "OAR116.error"; + private static final String DEFAULT_PATTERN = "^/"; + + @RuleProperty( + key = "pattern", + description = "Regular expression every API path must match.", + defaultValue = DEFAULT_PATTERN + ) + private String patternStr = DEFAULT_PATTERN; + + private Pattern pattern; + + @Override + public Set subscribedKinds() { + return ImmutableSet.of(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, OpenApi32Grammar.PATH); + } + + @Override + protected void visitFile(JsonNode root) { + pattern = Pattern.compile(patternStr != null ? patternStr : DEFAULT_PATTERN); + super.visitFile(root); + } + + @Override + public void visitNode(JsonNode node) { + String path = node.key().getTokenValue(); + if (!pattern.matcher(path).find()) { + addIssue(KEY, translate(MESSAGE, patternStr), node.key()); + } + } +} diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java index 3ed3cd59..3caf3280 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java @@ -4,6 +4,8 @@ import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; +import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.isType; + @Rule(key = OAR070BrokenAccessControlCheck.KEY) public class OAR070BrokenAccessControlCheck extends AbstractParameterCheck { @@ -24,20 +26,18 @@ protected void visitParameterNode(JsonNode node) { JsonNode schemaNode = node.get("schema"); boolean isNumericType = - typeNode != null && - ("integer".equals(typeNode.getTokenValue()) || - "number".equals(typeNode.getTokenValue()) || - "float".equals(typeNode.getTokenValue())); + isType(typeNode, "integer") || + isType(typeNode, "number") || + isType(typeNode, "float"); if (!isNumericType && schemaNode != null) { JsonNode schemaTypeNode = schemaNode.get("type"); isNumericType = - schemaTypeNode != null && - ("integer".equals(schemaTypeNode.getTokenValue()) || - "number".equals(schemaTypeNode.getTokenValue()) || - "float".equals(schemaTypeNode.getTokenValue())); + isType(schemaTypeNode, "integer") || + isType(schemaTypeNode, "number") || + isType(schemaTypeNode, "float"); typeNode = schemaTypeNode; } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java index b3426978..a927ce6f 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java @@ -10,6 +10,13 @@ public class OAR022OrderbyParameterCheck extends AbstractQueryParameterCheck { private static final String MESSAGE = "OAR022.error"; private static final String PARAM_NAME = "$orderby"; + @RuleProperty( + key = "parameterName", + description = "Name of the query parameter to be checked", + defaultValue = PARAM_NAME + ) + private String parameterNameOverride = PARAM_NAME; + @RuleProperty( key = "paths", description = "List of explicit paths to include/exclude from this rule separated by comma", @@ -33,6 +40,11 @@ public OAR022OrderbyParameterCheck() { ); } + @Override + protected String getParameterName() { + return parameterNameOverride; + } + @Override protected String getPathsStr() { return pathsStr; diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java index 865ac31d..3b55eced 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java @@ -12,6 +12,13 @@ public class OAR025LimitParameterCheck extends AbstractQueryParameterCheck { private static final String PARAM_NAME = "$limit"; private static final String EXPECTED_TYPE = "integer"; + @RuleProperty( + key = "parameterName", + description = "Name of the query parameter to be checked", + defaultValue = PARAM_NAME + ) + private String parameterNameOverride = PARAM_NAME; + @RuleProperty( key = "paths", description = "List of explicit paths to include/exclude from this rule separated by comma", @@ -35,6 +42,11 @@ public OAR025LimitParameterCheck() { ); } + @Override + protected String getParameterName() { + return parameterNameOverride; + } + @Override protected String getPathsStr() { return pathsStr; diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java index f53c3d0c..9ebf8904 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java @@ -1,13 +1,59 @@ package apiaddicts.sonar.openapi.checks.parameters; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +import com.sonar.sslr.api.AstNode; + +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; + @Rule(key = OAR060QueryParametersOptionalCheck.KEY) public class OAR060QueryParametersOptionalCheck extends AbstractParameterCheck { public static final String KEY = "OAR060"; private static final String MESSAGE = "OAR060.error"; + private static final String DEFAULT_EXCLUSION = "/status"; + private static final String PATHS = "paths"; + private static final String PARAMETERS = "parameters"; + private static final String REF = "$ref"; + private static final int MAX_REF_DEPTH = 10; + + @RuleProperty( + key = "path-exclusions", + description = "List of explicit paths to exclude from this rule.", + defaultValue = DEFAULT_EXCLUSION + ) + private String exclusionStr = DEFAULT_EXCLUSION; + + private Set exclusion = Collections.emptySet(); + + private Map> refUsages = Collections.emptyMap(); + + @Override + protected void visitFile(JsonNode root) { + exclusion = (exclusionStr == null || exclusionStr.trim().isEmpty()) + ? Collections.emptySet() + : Arrays.stream(exclusionStr.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toSet()); + refUsages = collectRefUsages(root); + super.visitFile(root); + } @Override protected void visitParameterNode(JsonNode node) { @@ -16,6 +62,10 @@ protected void visitParameterNode(JsonNode node) { if (inNode != null && "query".equals(inNode.getTokenValue())) { + if (isExcludedPath(node)) { + return; + } + JsonNode requiredNode = node.get("required"); if (requiredNode != null && "true".equals(requiredNode.getTokenValue())) { @@ -23,4 +73,66 @@ protected void visitParameterNode(JsonNode node) { } } } -} \ No newline at end of file + + private boolean isExcludedPath(JsonNode node) { + AstNode pathNode = node.getFirstAncestor( + OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, OpenApi32Grammar.PATH); + if (pathNode != null) { + return exclusion.contains(((JsonNode) pathNode).key().getTokenValue()); + } + Set usages = refUsages.get(node.getPointer()); + return usages != null && exclusion.containsAll(usages); + } + + private Map> collectRefUsages(JsonNode root) { + JsonNode paths = root.get(PATHS); + if (paths.isMissing() || !paths.isObject()) { + return Collections.emptyMap(); + } + Map> usages = new HashMap<>(); + for (JsonNode pathNode : paths.properties()) { + JsonNode pathKey = pathNode.key(); + if (pathKey.isMissing()) { + continue; + } + String path = pathKey.getTokenValue(); + JsonNode pathItem = resolveLocalRef(pathNode); + collectParameterRefUsages(pathItem.get(PARAMETERS), path, usages); + for (JsonNode operationNode : pathItem.properties()) { + if (JsonNodeUtils.isOperation(operationNode)) { + collectParameterRefUsages(operationNode.get(PARAMETERS), path, usages); + } + } + } + return usages; + } + + private void collectParameterRefUsages(JsonNode parametersNode, String path, Map> usages) { + if (parametersNode.isMissing() || !parametersNode.isArray()) { + return; + } + for (JsonNode parameterNode : parametersNode.elements()) { + JsonNode current = parameterNode; + for (int depth = 0; depth < MAX_REF_DEPTH && current.isRef(); depth++) { + JsonNode resolved = resolveLocalRef(current); + if (resolved == current) { + break; + } + usages.computeIfAbsent(resolved.getPointer(), k -> new HashSet<>()).add(path); + current = resolved; + } + } + } + + private static JsonNode resolveLocalRef(JsonNode node) { + if (!node.isRef()) { + return node; + } + String ref = node.get(REF).getTokenValue(); + if (ref == null || !ref.startsWith("#")) { + return node; + } + JsonNode resolved = node.resolve(); + return resolved.isMissing() ? node : resolved; + } +} diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheck.java index e61aee17..e9bf1a8e 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheck.java @@ -16,6 +16,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -128,19 +129,11 @@ private void visitSchemaNode(JsonNode responseNode, String statusCode) { Map properties = getAllProperties(schemaNode); if (rootProperty != null) { - if (rootProperty.equals("*")) { - rootProperty = properties.entrySet().iterator().next().getKey(); - } + Optional rootNode = resolveRootNode(properties, schemaNode); + if (!rootNode.isPresent()) return; - validateProperty(properties, rootProperty, "object", schemaNode.key()).ifPresent(node -> { - Map allProp = getAllProperties(node); - if (allProp.isEmpty()) { - addIssue(KEY, translate("OAR029.error-required-one-property", rootProperty), handleExternalRef.getTrueNode(node.key())); - } - }); - - schemaNode = properties.get(rootProperty); - properties = getAllProperties(properties.get(rootProperty)); + schemaNode = rootNode.get(); + properties = getAllProperties(schemaNode); } if (successCode) { @@ -151,6 +144,23 @@ private void visitSchemaNode(JsonNode responseNode, String statusCode) { validateRootProperties(requiredAlways, properties, schemaNode); } + private Optional resolveRootNode(Map properties, JsonNode schemaNode) { + String currentRootProperty = rootProperty; + if ("*".equals(currentRootProperty)) { + if (properties.isEmpty()) return Optional.empty(); + currentRootProperty = properties.entrySet().iterator().next().getKey(); + } + + final String resolvedRootProperty = currentRootProperty; + Optional rootNode = validateProperty(properties, resolvedRootProperty, "object", schemaNode.key()); + rootNode.ifPresent(node -> { + if (getAllProperties(node).isEmpty()) { + addIssue(KEY, translate("OAR029.error-required-one-property", resolvedRootProperty), handleExternalRef.getTrueNode(node.key())); + } + }); + return rootNode; + } + private void validateRootProperties(JSONArray requiredPropsArray, Map properties, JsonNode parentNode) { if (requiredPropsArray == null || requiredPropsArray.isEmpty()) return; @@ -173,7 +183,7 @@ private void validateDataProperty(String name, JSONObject schema, Map { - boolean isArray = "array".equals(parent.get("type").getTokenValue()); + boolean isArray = isArrayType(parent.get("type")); if (getAllProperties(node).isEmpty() && !isArray) { addIssue(KEY, translate("OAR029.error-required-one-property", name), handleExternalRef.getTrueNode(node.key())); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheck.java index 8153a58c..e0787983 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheck.java @@ -2,8 +2,11 @@ import com.google.common.collect.ImmutableSet; import com.sonar.sslr.api.AstNodeType; +import com.sonar.sslr.api.TokenType; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.snakeyaml.parser.Tokens; import org.sonar.check.Rule; import apiaddicts.sonar.openapi.checks.BaseCheck; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; @@ -19,6 +22,7 @@ public class OAR108SchemaValidatorCheck extends BaseCheck { public static final String KEY = "OAR108"; private static final String MESSAGE = "OAR108.error"; + private static final String TYPE_NULL = "null"; @Override public Set subscribedKinds() { @@ -61,12 +65,19 @@ private void validateTypes(JsonNode schema, JsonNode example, boolean isSwagger) schemaTypes.forEach((keyName, expectedType) -> { String actualType = exampleTypes.getOrDefault(keyName, "unknown"); - if (!expectedType.equals(actualType)) { + if (!isCompatible(expectedType, actualType)) { addIssue(KEY, translate(MESSAGE), example.key()); } }); } + private boolean isCompatible(String expectedType, String actualType) { + if (expectedType == null) return true; + if (TYPE_NULL.equals(actualType)) return true; + if (expectedType.equals(actualType)) return true; + return "number".equals(expectedType) && "integer".equals(actualType); + } + private Map extractSchemaTypes(JsonNode schemaNode) { Map schemaTypes = new HashMap<>(); @@ -75,7 +86,7 @@ private Map extractSchemaTypes(JsonNode schemaNode) { for (Map.Entry entry : propertiesNode.propertyMap().entrySet()) { String propertyName = entry.getKey(); JsonNode propertyTypeNode = entry.getValue().get("type"); - String propertyType = propertyTypeNode != null ? propertyTypeNode.stringValue() : null; + String propertyType = JsonNodeUtils.getPrimaryType(propertyTypeNode); schemaTypes.put(propertyName, propertyType); } } @@ -116,22 +127,28 @@ private Map extractExampleTypesSwagger2(JsonNode examplesNode) { } private String determineExampleType(JsonNode node) { - String value = node.stringValue().trim(); + if (node.isObject()) { + return "object"; + } + if (node.isArray()) { + return "array"; + } + if (node.isNull()) { + return TYPE_NULL; + } - if (value.matches("-?\\d+\\.\\d+")) { - return "number"; - } else if (value.matches("-?\\d+")) { + TokenType tokenType = node.getToken().getType(); + if (tokenType == Tokens.INTEGER) { return "integer"; } - - if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + if (tokenType == Tokens.FLOAT) { + return "number"; + } + if (tokenType == Tokens.TRUE || tokenType == Tokens.FALSE) { return "boolean"; } - - if (node.isObject()) { - return "object"; - } else if (node.isArray()) { - return "array"; + if (tokenType == Tokens.NULL) { + return TYPE_NULL; } return "string"; diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java index 0eb84e65..ca1d63b3 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java @@ -3,6 +3,8 @@ import org.sonar.check.Rule; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.isType; + @Rule(key = OAR074NumericParameterIntegrityCheck.KEY) public class OAR074NumericParameterIntegrityCheck extends AbstractTypedParameterIntegrityCheck { @@ -15,9 +17,7 @@ public OAR074NumericParameterIntegrityCheck() { @Override protected boolean isTargetType(JsonNode typeNode) { - if(typeNode == null || typeNode.isMissing()) return false; - String t = typeNode.getTokenValue(); - return "integer".equals(t) || "number".equals(t) || "float".equals(t); + return isType(typeNode, "integer") || isType(typeNode, "number") || isType(typeNode, "float"); } @Override diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java index 5e2a3b8c..9a554366 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java @@ -7,12 +7,14 @@ import java.util.Set; import java.util.stream.Collectors; +import static apiaddicts.sonar.openapi.utils.JsonNodeUtils.isStringType; + @Rule(key = OAR075StringParameterIntegrityCheck.KEY) public class OAR075StringParameterIntegrityCheck extends AbstractTypedParameterIntegrityCheck { public static final String KEY = "OAR075"; private static final String MESSAGE = "OAR075.error"; - private static final String DEFAULT = "minLength,maxLength,enum,format"; + private static final String DEFAULT = "minLength,maxLength,pattern,enum"; @RuleProperty( key = "parameter_integrity", @@ -27,7 +29,7 @@ public OAR075StringParameterIntegrityCheck() { @Override protected boolean isTargetType(JsonNode typeNode) { - return typeNode != null && !typeNode.isMissing() && "string".equals(typeNode.getTokenValue()); + return isStringType(typeNode); } @Override @@ -36,7 +38,7 @@ protected void validateTypedNode(JsonNode node,JsonNode typeNode) { .map(String::trim) .collect(Collectors.toSet()); - boolean ok = checks.stream().allMatch(k->{ + boolean ok = checks.stream().anyMatch(k->{ JsonNode n = node.get(k); return n != null && !n.isMissing(); }); diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java index 2f619e19..4411d026 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java @@ -13,6 +13,8 @@ import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import apiaddicts.sonar.openapi.utils.JsonNodeUtils; + import java.util.Arrays; import java.util.List; import java.util.Set; @@ -57,11 +59,13 @@ private void visitV2Node(JsonNode node) { if (fieldNode == null || fieldNode.isMissing()) continue; JsonNode typeNode = fieldNode.get("type"); - String type = typeNode.isMissing() ? null : typeNode.getTokenValue(); - if ("string".equals(type)) { + if (JsonNodeUtils.isStringType(typeNode)) { String format = fieldNode.get("format").getTokenValue(); - if (!"binary".equals(format) && !"byte".equals(format)) { + boolean hasBinaryFormat = "binary".equals(format) || "byte".equals(format); + boolean hasContentEncoding = !fieldNode.get("contentEncoding").isMissing() + || !fieldNode.get("contentMediaType").isMissing(); + if (!hasBinaryFormat && !hasContentEncoding) { addIssue(KEY, translate(MESSAGE, fieldsApply), typeNode.key()); } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java index 4ba0c578..bd0614f7 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java @@ -20,7 +20,7 @@ public class OAR085OpenAPIVersionCheck extends BaseCheck { public static final String KEY = "OAR085"; private static final String MESSAGE = "OAR085.error"; - private static final String DEFAULT_VALID_VERSIONS = "2.0,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.2.0"; + private static final String DEFAULT_VALID_VERSIONS = "2.0,3.0.0,3.0.1,3.0.2,3.0.3,3.0.4,3.1.0,3.1.1,3.1.2,3.2.0"; @RuleProperty( key = "valid-versions", diff --git a/src/main/java/apiaddicts/sonar/openapi/utils/JsonNodeUtils.java b/src/main/java/apiaddicts/sonar/openapi/utils/JsonNodeUtils.java index 1763698f..4380a62e 100644 --- a/src/main/java/apiaddicts/sonar/openapi/utils/JsonNodeUtils.java +++ b/src/main/java/apiaddicts/sonar/openapi/utils/JsonNodeUtils.java @@ -7,6 +7,7 @@ import org.apiaddicts.apitools.dosonarapi.openapi.OpenApiConfiguration; import org.apiaddicts.apitools.dosonarapi.openapi.parser.OpenApiParser; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlGrammar; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.YamlParser; import java.net.HttpURLConnection; @@ -17,6 +18,7 @@ import java.io.InputStreamReader; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import com.sonar.sslr.api.AstNodeType; @@ -45,17 +47,21 @@ private JsonNodeUtils() { private static String lastFetchedContent = ""; public static JsonNode resolve(JsonNode original) { - - if (original.isRef()) { - String ref = original.get("$ref").getTokenValue(); + JsonNode current = original; + Set visitedRefs = new HashSet<>(); + while (current.isRef()) { + String ref = current.get("$ref").getTokenValue(); if (ref.startsWith("#")) { - return original.resolve(); + if (!visitedRefs.add(ref)) { + return current; + } + current = current.resolve(); } else { JsonNode resolved = resolveExternalRef(ref); - return resolved != null ? resolved : original; + return resolved != null ? resolved : current; } } - return original; + return current; } public static boolean isExternalRef (JsonNode original){ @@ -148,6 +154,16 @@ public static String getLastFetchedContent() { return lastFetchedContent; } + public static JsonNode propertyKey(JsonNode object, String propertyName) { + if (object == null || !object.isObject()) return null; + JsonNode found = null; + for (JsonNode property : object.getJsonChildren(YamlGrammar.BLOCK_PROPERTY, YamlGrammar.FLOW_PROPERTY)) { + JsonNode key = property.key(); + if (!key.isMissing() && propertyName.equals(key.getTokenValue())) found = key; + } + return found; + } + public static JsonNode getType(JsonNode schema) { return schema.get(TYPE); } @@ -184,8 +200,26 @@ public static boolean isBooleanType(JsonNode schemaNode) { return isType(schemaNode, TYPE_BOOLEAN); } + public static String getPrimaryType(JsonNode typeNode) { + if (typeNode == null || typeNode.isMissing()) return null; + if (typeNode.isArray()) { + for (JsonNode element : typeNode.elements()) { + String value = element.getTokenValue(); + if (value != null && !"null".equals(value)) return value; + } + return null; + } + return typeNode.getTokenValue(); + } + public static boolean isType(JsonNode type, String name) { - return TYPE_ANY.equals(name) || name.equals(type.getTokenValue()); + if (TYPE_ANY.equals(name)) return true; + if (type == null || type.isMissing()) return false; + if (name.equals(type.getTokenValue())) return true; + for (JsonNode element : type.elements()) { + if (name.equals(element.getTokenValue())) return true; + } + return false; } public static boolean isOperation(JsonNode node) { diff --git a/src/main/resources/messages/errors.properties b/src/main/resources/messages/errors.properties index 22094345..c659580f 100644 --- a/src/main/resources/messages/errors.properties +++ b/src/main/resources/messages/errors.properties @@ -121,6 +121,7 @@ OAR110.error=License information cannot be empty OAR111.error=Contact information cannot be empty OAR113.error=Field or extension {0} must be at the assigned location OAR115.error=This value does not exist, it must be defined in the schema properties +OAR116.error=Path does not match the required pattern: {0} generic.section=Section {0} is mandatory generic.consume=Should indicate the default request media type generic.produce=Should indicate the default response media type diff --git a/src/main/resources/messages/errors_es.properties b/src/main/resources/messages/errors_es.properties index 9204c4a3..51ce126b 100644 --- a/src/main/resources/messages/errors_es.properties +++ b/src/main/resources/messages/errors_es.properties @@ -121,6 +121,7 @@ OAR110.error=La información de licencia no puede estar vacía OAR111.error=La información de contacto no puede estar vacía OAR113.error=El campo o extensión {0} debe estar en la ubicación asignada. OAR115.error=Este valor no existe, debe estár definido en las propiedades del esquema +OAR116.error=La ruta no cumple con el patrón requerido: {0} generic.section=La sección {0} es obligatoria generic.consume=Debe indicar el tipo de medio de solicitud predeterminado generic.produce=Debe indicar el tipo de medio de respuesta predeterminado diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.html new file mode 100644 index 00000000..acbd04d3 --- /dev/null +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.html @@ -0,0 +1,53 @@ +

Todas las rutas de la API deben cumplir con la expresión regular configurada. Por defecto el patrón solo exige que cada ruta empiece con "/" (^/); el cliente puede sobrescribirlo con una expresión más estricta.

+

Solución no conforme (OpenAPI 2)

+
+swagger: "2.0"
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/}}
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Solución conforme (OpenAPI 2)

+
+swagger: "2.0"
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Solución no conforme (OpenAPI 3)

+
+openapi: 3.0.0
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/}}
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Solución conforme (OpenAPI 3)

+
+openapi: 3.0.0
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+
diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.json b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.json new file mode 100644 index 00000000..56ebac61 --- /dev/null +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR116.json @@ -0,0 +1,13 @@ +{ + "title": "OAR116 - PathPattern - Todas las rutas de la API deben cumplir con la expresión regular configurada", + "type": "BUG", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [ + "format" + ], + "defaultSeverity": "MAJOR" +} diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR060.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR060.html index 1edc6032..e2049c5b 100644 --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR060.html +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR060.html @@ -1,4 +1,8 @@

Todos los parámetros en query deben definirse como opcionales

+

Parámetros configurables:

+
    +
  • path-exclusions — lista separada por comas de rutas exactas (sensibles a mayúsculas) en las que esta regla no debe aplicarse (por defecto: /status). La coincidencia es exacta: /status excluye /status, pero no /status/health ni /statuses.
  • +

Ejemplo de código no compatible (OpenAPI 2)

   swagger: "2.0"
diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.html
new file mode 100644
index 00000000..4f0b960e
--- /dev/null
+++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.html
@@ -0,0 +1,53 @@
+

Every API path must match the configured regular expression. By default the pattern only requires each path to start with "/" (^/); clients can override it with a stricter expression.

+

Noncompliant Solution (OpenAPI 2)

+
+swagger: "2.0"
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/}}
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Compliant Solution (OpenAPI 2)

+
+swagger: "2.0"
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Noncompliant Solution (OpenAPI 3)

+
+openapi: 3.0.0
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/}}
+    get:
+      responses:
+        "200":
+          description: Ok
+
+

Compliant Solution (OpenAPI 3)

+
+openapi: 3.0.0
+info:
+  title: Sample API
+  version: "1.0.0"
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+
diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.json b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.json new file mode 100644 index 00000000..c68e1305 --- /dev/null +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR116.json @@ -0,0 +1,13 @@ +{ + "title": "OAR116 - PathPattern - All API paths must match the configured regular expression", + "type": "BUG", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [ + "format" + ], + "defaultSeverity": "MAJOR" +} diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR060.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR060.html index 514df0ab..227a47aa 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR060.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR060.html @@ -1,4 +1,8 @@

All parameters in query must be defined as optional

+

Configurable parameters:

+
    +
  • path-exclusions — comma-separated list of exact, case-sensitive paths this rule must not fire on (default: /status). Matching is exact: /status excludes /status but neither /status/health nor /statuses.
  • +

Noncompliant Code Example (OpenAPI 2)

   swagger: "2.0"
diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/security/OAR075.json b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/security/OAR075.json
index 760ee104..5e814abe 100644
--- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/security/OAR075.json
+++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/security/OAR075.json
@@ -1,5 +1,5 @@
 {
-    "title": "OAR075 - StringParameterIntegrityCheck - String parameters should have minLength, maxLength, pattern (regular expression),format or enum restriction",
+    "title": "OAR075 - StringParameterIntegrityCheck - String parameters should have minLength, maxLength, pattern (regular expression) or enum restriction",
     "type": "VULNERABILITY",
     "status": "ready",
     "remediation": {
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheckTest.java
index dbdc0cb9..a6c6d8ac 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR002ValidWso2ScopesCheckTest.java
@@ -108,6 +108,57 @@ public void verifyInV32WithoutSecurity() {
         verifyV32("without-security");
     }
 
+    @Test
+    public void verifyInV2WithEmptyValueScopes() {
+        verifyV2("with-empty-value-scopes.yaml");
+    }
+    @Test
+    public void verifyInV3WithEmptyValueScopes() {
+        verifyV3("with-empty-value-scopes.yaml");
+    }
+    @Test
+    public void verifyInV31WithEmptyValueScopes() {
+        verifyV31("with-empty-value-scopes.yaml");
+    }
+    @Test
+    public void verifyInV32WithEmptyValueScopes() {
+        verifyV32("with-empty-value-scopes.yaml");
+    }
+
+    @Test
+    public void verifyInV2WithEmptyValueContainer() {
+        verifyV2("with-empty-value-container.yaml");
+    }
+    @Test
+    public void verifyInV3WithEmptyValueContainer() {
+        verifyV3("with-empty-value-container.yaml");
+    }
+    @Test
+    public void verifyInV31WithEmptyValueContainer() {
+        verifyV31("with-empty-value-container.yaml");
+    }
+    @Test
+    public void verifyInV32WithEmptyValueContainer() {
+        verifyV32("with-empty-value-container.yaml");
+    }
+
+    @Test
+    public void verifyInV2WithScopesAsMap() {
+        verifyV2("with-scopes-as-map.yaml");
+    }
+    @Test
+    public void verifyInV3WithScopesAsMap() {
+        verifyV3("with-scopes-as-map.yaml");
+    }
+    @Test
+    public void verifyInV31WithScopesAsMap() {
+        verifyV31("with-scopes-as-map.yaml");
+    }
+    @Test
+    public void verifyInV32WithScopesAsMap() {
+        verifyV32("with-scopes-as-map.yaml");
+    }
+
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR002 - ValidWso2Scopes - WSO2 scope definition is wrong", RuleType.VULNERABILITY, Severity.BLOCKER, tags("api-manager", "vulnerability", "wso2"));
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR003DefinedWso2ScopesDescriptionCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR003DefinedWso2ScopesDescriptionCheckTest.java
index 2cbf3ffb..4685dcf7 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR003DefinedWso2ScopesDescriptionCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR003DefinedWso2ScopesDescriptionCheckTest.java
@@ -63,6 +63,32 @@ public void verifyInV32WithoutScopeDescription() {
     public void verifyInV3WithNullScopeDescription() {
         verifyV3("with-null-description");
     }
+
+    @Test
+    public void verifyInV3WithScopesAsMap() {
+        verifyV3("fail-scopes-as-map");
+    }
+    @Test
+    public void verifyInV31WithScopesAsMap() {
+        verifyV31("fail-scopes-as-map");
+    }
+    @Test
+    public void verifyInV32WithScopesAsMap() {
+        verifyV32("fail-scopes-as-map");
+    }
+
+    @Test
+    public void verifyInV3WithRefSecurity() {
+        verifyV3("fail-ref-security");
+    }
+    @Test
+    public void verifyInV31WithRefSecurity() {
+        verifyV31("fail-ref-security");
+    }
+    @Test
+    public void verifyInV32WithRefSecurity() {
+        verifyV32("fail-ref-security");
+    }
     @Test
     public void verifyInV31WithNullScopeDescription() {
         verifyV31("with-null-description");
@@ -72,6 +98,49 @@ public void verifyInV32WithNullScopeDescription() {
         verifyV32("with-null-description");
     }
 
+    @Test
+    public void verifyInV2WithScopesAsMap() {
+        verifyV2("fail-scopes-as-map");
+    }
+    @Test
+    public void verifyInV2WithRefSecurity() {
+        verifyV2("fail-ref-security");
+    }
+
+    @Test
+    public void verifyInV2WithChainedRefSecurity() {
+        verifyV2("fail-chained-ref-security");
+    }
+    @Test
+    public void verifyInV3WithChainedRefSecurity() {
+        verifyV3("fail-chained-ref-security");
+    }
+    @Test
+    public void verifyInV31WithChainedRefSecurity() {
+        verifyV31("fail-chained-ref-security");
+    }
+    @Test
+    public void verifyInV32WithChainedRefSecurity() {
+        verifyV32("fail-chained-ref-security");
+    }
+
+    @Test
+    public void verifyInV2WithRefScopeDescription() {
+        verifyV2("ref-scope-with-description");
+    }
+    @Test
+    public void verifyInV3WithRefScopeDescription() {
+        verifyV3("ref-scope-with-description");
+    }
+    @Test
+    public void verifyInV31WithRefScopeDescription() {
+        verifyV31("ref-scope-with-description");
+    }
+    @Test
+    public void verifyInV32WithRefScopeDescription() {
+        verifyV32("ref-scope-with-description");
+    }
+
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR003 - DefinedWso2ScopesDescription - WSO2 scope description is recommended", RuleType.VULNERABILITY, Severity.BLOCKER, tags("api-manager", "vulnerability", "wso2"));
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheckTest.java
index ae71be53..5a70f94a 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheckTest.java
@@ -6,6 +6,8 @@
 import org.sonar.api.rules.RuleType;
 import apiaddicts.sonar.openapi.BaseCheckTest;
 
+import static org.junit.Assert.assertFalse;
+
 public class OAR044MediaTypeCheckTest extends BaseCheckTest {
 
     @Before
@@ -36,6 +38,13 @@ public void verifyInV32() {
         verifyV32("media-type");
     }
 
+    @Test(timeout = 2000)
+    public void verifyNoBacktrackingOnLongInput() {
+        String malicious = "application/json;x=\"" + "a".repeat(500_000);
+        assertFalse(OAR044MediaTypeCheck.MEDIA_RANGE_PATTERN.matcher(malicious).matches());
+        assertFalse(OAR044MediaTypeCheck.MIME_TYPE_PATTERN.matcher(malicious).matches());
+    }
+
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR044 - MediaType - Media types SHOULD conform to the RFC.", RuleType.BUG, Severity.BLOCKER, tags("format"));
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheckTest.java
new file mode 100644
index 00000000..9ef1caf7
--- /dev/null
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR116PathPatternCheckTest.java
@@ -0,0 +1,83 @@
+package apiaddicts.sonar.openapi.checks.format;
+
+import java.lang.reflect.Field;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.rule.RuleParamType;
+import apiaddicts.sonar.openapi.BaseCheckTest;
+
+public class OAR116PathPatternCheckTest extends BaseCheckTest {
+
+    @Before
+    public void init() {
+        ruleName = "OAR116";
+        check = new OAR116PathPatternCheck();
+        v2Path = getV2Path("format");
+        v3Path = getV3Path("format");
+        v31Path = getV31Path("format");
+        v32Path = getV32Path("format");
+    }
+
+    @Test
+    public void verifyValidV2() {
+        verifyV2("valid");
+    }
+    @Test
+    public void verifyValidV3() {
+        verifyV3("valid");
+    }
+    @Test
+    public void verifyValidV31() {
+        verifyV31("valid");
+    }
+    @Test
+    public void verifyValidV32() {
+        verifyV32("valid");
+    }
+
+    @Test
+    public void verifyInvalidV2() throws Exception {
+        setPattern("^/v[0-9]+");
+        verifyV2("invalid.yaml");
+    }
+    @Test
+    public void verifyInvalidV3() throws Exception {
+        setPattern("^/v[0-9]+");
+        verifyV3("invalid.yaml");
+    }
+    @Test
+    public void verifyInvalidV31() throws Exception {
+        setPattern("^/v[0-9]+");
+        verifyV31("invalid.yaml");
+    }
+    @Test
+    public void verifyInvalidV32() throws Exception {
+        setPattern("^/v[0-9]+");
+        verifyV32("invalid.yaml");
+    }
+    @Test
+    public void verifyNullPatternFallsBackToDefault() throws Exception {
+        setPattern(null);
+        verifyV3("valid");
+    }
+
+    private void setPattern(String pattern) throws Exception {
+        Field field = OAR116PathPatternCheck.class.getDeclaredField("patternStr");
+        field.setAccessible(true);
+        field.set(check, pattern);
+    }
+
+    @Override
+    public void verifyParameters() {
+        assertNumberOfParameters(1);
+        assertParameterProperties("pattern", "^/", RuleParamType.STRING);
+    }
+
+    @Override
+    public void verifyRule() {
+        assertRuleProperties("OAR116 - PathPattern - All API paths must match the configured regular expression", RuleType.BUG, Severity.MAJOR, tags("format"));
+    }
+}
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
index 6f9e281e..8938c709 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
@@ -105,7 +105,8 @@ public void verifyRule() {
 
     @Override
     public void verifyParameters() {
-        assertNumberOfParameters(2);
+        assertNumberOfParameters(3);
+        assertParameterProperties("parameterName", "$orderby", RuleParamType.STRING);
         assertParameterProperties("paths", "/examples", RuleParamType.STRING);
         assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
     }
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
index a044b481..6b90dbef 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
@@ -183,7 +183,8 @@ public void verifyRule() {
 
     @Override
     public void verifyParameters() {
-        assertNumberOfParameters(2);
+        assertNumberOfParameters(3);
+        assertParameterProperties("parameterName", "$limit", RuleParamType.STRING);
         assertParameterProperties("paths", "/examples", RuleParamType.STRING);
         assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
     }
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheckTest.java
index e9058dbb..6b9363ff 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheckTest.java
@@ -1,9 +1,12 @@
 package apiaddicts.sonar.openapi.checks.parameters;
 
+import java.lang.reflect.Field;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.rule.RuleParamType;
 import apiaddicts.sonar.openapi.BaseCheckTest;
 
 public class OAR060QueryParametersOptionalCheckTest extends BaseCheckTest{
@@ -50,9 +53,96 @@ public void verifyInV31requiredTrue() {
     public void verifyInV32requiredTrue() {
         verifyV32("required-true");
     }
+    @Test
+    public void verifyInV2ExcludedPath() {
+        verifyV2("excluded-path");
+    }
+    @Test
+    public void verifyInV3ExcludedPath() {
+        verifyV3("excluded-path");
+    }
+    @Test
+    public void verifyInV31ExcludedPath() {
+        verifyV31("excluded-path");
+    }
+    @Test
+    public void verifyInV32ExcludedPath() {
+        verifyV32("excluded-path");
+    }
+    @Test
+    public void verifyNonQueryParamsIgnored() {
+        verifyV3("non-query.yaml");
+    }
+    @Test
+    public void verifyPathLevelQueryParam() {
+        verifyV3("path-level.yaml");
+    }
+    @Test
+    public void verifyComponentsQueryParam() {
+        verifyV3("components-query.yaml");
+    }
+    @Test
+    public void verifySubpathIsNotExcluded() {
+        verifyV3("subpath.yaml");
+    }
+    @Test
+    public void verifyEmptyExclusionsFlagsEveryPath() throws Exception {
+        setExclusions("");
+        verifyV3("no-exclusions.yaml");
+    }
+    @Test
+    public void verifyNullExclusionsFlagsEveryPath() throws Exception {
+        setExclusions(null);
+        verifyV3("no-exclusions.yaml");
+    }
+    @Test
+    public void verifyRefParamAndQueryWithoutRequiredIgnored() {
+        verifyV3("edge-params.yaml");
+    }
+    @Test
+    public void verifyV3SharedParamUsedOnlyByExcludedPath() {
+        verifyV3("ref-excluded-path.yaml");
+    }
+    @Test
+    public void verifyV31SharedParamUsedOnlyByExcludedPath() {
+        verifyV31("ref-excluded-path.yaml");
+    }
+    @Test
+    public void verifyV32SharedParamUsedOnlyByExcludedPath() {
+        verifyV32("ref-excluded-path.yaml");
+    }
+    @Test
+    public void verifyV2SharedParamUsedOnlyByExcludedPath() {
+        verifyV2("ref-excluded-path.yaml");
+    }
+    @Test
+    public void verifyV3SharedParamUsedByExcludedAndNonExcludedPaths() {
+        verifyV3("ref-mixed-paths.yaml");
+    }
+    @Test
+    public void verifyV3SharedParamNotExcludedWithoutExclusions() throws Exception {
+        setExclusions("");
+        verifyV3("ref-no-exclusions.yaml");
+    }
+    @Test
+    public void verifyV3ExternalRefParamIsNotResolved() {
+        verifyV3("ref-external.yaml");
+    }
+
+    private void setExclusions(String value) throws Exception {
+        Field field = OAR060QueryParametersOptionalCheck.class.getDeclaredField("exclusionStr");
+        field.setAccessible(true);
+        field.set(check, value);
+    }
+
+    @Override
+    public void verifyParameters() {
+        assertNumberOfParameters(1);
+        assertParameterProperties("path-exclusions", "/status", RuleParamType.STRING);
+    }
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR060 - QueryParametersOptional - All parameters in query must be defined as optional", RuleType.BUG, Severity.CRITICAL, tags("parameters"));
     }
-    
+
 }
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheckTest.java
index 66063382..8921e0f4 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR108SchemaValidatorCheckTest.java
@@ -54,6 +54,16 @@ public void verifyInV32Invalid() {
         verifyV32("invalid");
     }
 
+    @Test
+    public void verifyInV2QuotedNumber() {
+        verifyV2("quoted-number.yaml");
+    }
+
+    @Test
+    public void verifyInV31ArrayType() {
+        verifyV31("array-type.yaml");
+    }
+
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR108 - SchemaValidator - Schema does not match the provided example", RuleType.BUG, Severity.MAJOR, tags("schemas"));
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheckTest.java
index 35d3d780..f9682140 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheckTest.java
@@ -46,6 +46,16 @@ public void verifyInV32withRestrictions() {
     public void verifyInV3noRestrictions() {
         verifyV3("no-restrictions");
     }
+
+    @Test
+    public void verifyInV3singleRestriction() {
+        verifyV3("single-restriction.yaml");
+    }
+
+    @Test
+    public void verifyInV31arrayType() {
+        verifyV31("array-type.yaml");
+    }
     @Test
     public void verifyInV31noRestrictions() {
         verifyV31("no-restrictions");
@@ -58,11 +68,11 @@ public void verifyInV32noRestrictions() {
     @Override
     public void verifyParameters() {
         assertNumberOfParameters(1);
-        assertParameterProperties("parameter_integrity", "minLength,maxLength,enum,format", RuleParamType.STRING);
+        assertParameterProperties("parameter_integrity", "minLength,maxLength,pattern,enum", RuleParamType.STRING);
     }
 
     @Override
     public void verifyRule() {
-        assertRuleProperties("OAR075 - StringParameterIntegrityCheck - String parameters should have minLength, maxLength, pattern (regular expression),format or enum restriction", RuleType.VULNERABILITY, Severity.MAJOR, tags("safety"));
+        assertRuleProperties("OAR075 - StringParameterIntegrityCheck - String parameters should have minLength, maxLength, pattern (regular expression) or enum restriction", RuleType.VULNERABILITY, Severity.MAJOR, tags("safety"));
     }
 }
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheckTest.java
index 8efe8794..72a09a2e 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheckTest.java
@@ -35,6 +35,11 @@ public void verifyvalidV32() {
         verifyV32("valid-format");
     }
 
+    @Test
+    public void verifyArrayFormTypeV31() {
+        verifyV31("array-type-format.yaml");
+    }
+
     @Override
     public void verifyRule() {
         assertRuleProperties("OAR082 - BinaryOrByte - The string properties of the specified parameters must define a byte or binary format.", RuleType.VULNERABILITY, Severity.MAJOR, tags("safety"));
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheckTest.java
index 3d09c539..1968df46 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheckTest.java
@@ -50,6 +50,21 @@ public void verifyInV32() {
         verifyV32("valid-openapi-version");
     }
 
+    @Test
+    public void verifyValidOpenApiVersion304InV3() {
+        verifyV3("valid-openapi-version-304.yaml");
+    }
+
+    @Test
+    public void verifyValidOpenApiVersion311InV31() {
+        verifyV31("valid-openapi-version-311.yaml");
+    }
+
+    @Test
+    public void verifyValidOpenApiVersion312InV31() {
+        verifyV31("valid-openapi-version-312.yaml");
+    }
+
     @Test
     public void verifyInvalidOpenApiVersionInV3() {
         verifyV3("invalid-openapi-version");
@@ -137,6 +152,6 @@ public void verifyRule() {
     @Override
     public void verifyParameters() {
         assertNumberOfParameters(1);
-        assertParameterProperties("valid-versions", "2.0,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.2.0", RuleParamType.STRING);
+        assertParameterProperties("valid-versions", "2.0,3.0.0,3.0.1,3.0.2,3.0.3,3.0.4,3.1.0,3.1.1,3.1.2,3.2.0", RuleParamType.STRING);
     }
 }
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-container.yaml b/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-container.yaml
new file mode 100644
index 00000000..5f4067ea
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-container.yaml
@@ -0,0 +1,10 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes: # Noncompliant {{OAR002: WSO2 scopes definition is wrong}}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-scopes.yaml b/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-scopes.yaml
new file mode 100644
index 00000000..7fe1d99b
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR002/with-empty-value-scopes.yaml
@@ -0,0 +1,38 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      - name: read
+        key: read
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: write
+        key: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_WRITE
+      - name: # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view
+        roles: ROLE_VIEW
+      - name: tilde
+        key: tilde
+        roles: ~ # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: capitalised
+        key: capitalised
+        roles: Null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: uppercase
+        key: uppercase
+        roles: NULL # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: quoted-null
+        key: quoted-null
+        roles: "null"
+      - name: quoted-tilde
+        key: quoted-tilde
+        roles: "~"
+      - name: duplicated
+        key: duplicated
+        roles: ROLE_FIRST
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes-as-map.yaml b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes-as-map.yaml
new file mode 100644
index 00000000..6db0f78e
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes-as-map.yaml
@@ -0,0 +1,21 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        name: read
+        roles: ROLE_READ
+      write:
+        name: write
+        key: write
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      manage:
+        name: manage
+        key: manage
+        roles: ROLE_MANAGE
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.json b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.json
index c03da0ac..0e04b217 100644
--- a/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.json
+++ b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.json
@@ -30,6 +30,22 @@
         "key" : "view2",
         "roles" : "ROLE_VIEW_2",
         "name" : null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      }, {
+        "name" : "read3",
+        "key" : "read3",
+        "roles" : [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : "read4",
+        "key" : "read4",
+        "roles" : {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : [], # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        "key" : "view3",
+        "roles" : "ROLE_VIEW_3"
+      }, {
+        "name" : "view4",
+        "key" : {}, # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        "roles" : "ROLE_VIEW_4"
       } ]
     }
   }
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.yaml b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.yaml
index f574f034..4e436bc8 100644
--- a/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.yaml
+++ b/src/test/resources/checks/v2/apim/wso2/OAR002/with-scopes.yaml
@@ -22,4 +22,16 @@ x-wso2-security:
         roles: null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
       - key: view2
         roles: ROLE_VIEW_2
-        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
\ No newline at end of file
+        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      - name: read3
+        key: read3
+        roles: [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: read4
+        key: read4
+        roles: {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: [] # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view3
+        roles: ROLE_VIEW_3
+      - name: view4
+        key: {} # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_VIEW_4
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.json b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.json
new file mode 100644
index 00000000..ac1b0c48
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.json
@@ -0,0 +1,32 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/securityAlias"
+  },
+  "x-wso2-definitions" : {
+    "securityAlias" : {
+      "$ref" : "#/x-wso2-definitions/security"
+    },
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.yaml b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.yaml
new file mode 100644
index 00000000..132eb7ad
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-chained-ref-security.yaml
@@ -0,0 +1,22 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/securityAlias"
+x-wso2-definitions:
+  securityAlias:
+    $ref: "#/x-wso2-definitions/security"
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.json b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.json
new file mode 100644
index 00000000..e219f118
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.json
@@ -0,0 +1,29 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/security"
+  },
+  "x-wso2-definitions" : {
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.yaml b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.yaml
new file mode 100644
index 00000000..cbbd6d45
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-ref-security.yaml
@@ -0,0 +1,20 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/security"
+x-wso2-definitions:
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.json b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.json
new file mode 100644
index 00000000..b4b5f581
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.json
@@ -0,0 +1,27 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.yaml b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.yaml
new file mode 100644
index 00000000..e01463b2
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/fail-scopes-as-map.yaml
@@ -0,0 +1,19 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+        key: write
+        roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.json b/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.json
new file mode 100644
index 00000000..47b61bd6
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.json
@@ -0,0 +1,33 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : {
+          "$ref" : "#/x-wso2-definitions/writeScope"
+        }
+      }
+    }
+  },
+  "x-wso2-definitions" : {
+    "writeScope" : {
+      "name" : "write",
+      "key" : "write",
+      "roles" : "ROLE_WRITE",
+      "description" : "Allows users to write the catalogue"
+    }
+  }
+}
diff --git a/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.yaml b/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.yaml
new file mode 100644
index 00000000..3de00aef
--- /dev/null
+++ b/src/test/resources/checks/v2/apim/wso2/OAR003/ref-scope-with-description.yaml
@@ -0,0 +1,23 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        $ref: "#/x-wso2-definitions/writeScope"
+x-wso2-definitions:
+  writeScope:
+    name: write
+    key: write
+    roles: ROLE_WRITE
+    description: Allows users to write the catalogue
diff --git a/src/test/resources/checks/v2/format/OAR116/invalid.yaml b/src/test/resources/checks/v2/format/OAR116/invalid.yaml
new file mode 100644
index 00000000..1c7023d3
--- /dev/null
+++ b/src/test/resources/checks/v2/format/OAR116/invalid.yaml
@@ -0,0 +1,15 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /v1/pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/v[0-9]+}}
+    get:
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v2/format/OAR116/valid.json b/src/test/resources/checks/v2/format/OAR116/valid.json
new file mode 100644
index 00000000..f8df8270
--- /dev/null
+++ b/src/test/resources/checks/v2/format/OAR116/valid.json
@@ -0,0 +1,33 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Sample API"
+  },
+  "paths" : {
+    "/pets" : {
+      "get" : {
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    },
+    "/pets/{id}" : {
+      "get" : {
+        "parameters" : [ {
+          "in" : "path",
+          "name" : "id",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v2/format/OAR116/valid.yaml b/src/test/resources/checks/v2/format/OAR116/valid.yaml
new file mode 100644
index 00000000..1026fc76
--- /dev/null
+++ b/src/test/resources/checks/v2/format/OAR116/valid.yaml
@@ -0,0 +1,20 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          type: string
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v2/parameters/OAR060/excluded-path.json b/src/test/resources/checks/v2/parameters/OAR060/excluded-path.json
new file mode 100644
index 00000000..ea1bff5e
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR060/excluded-path.json
@@ -0,0 +1,35 @@
+{
+    "swagger" : "2.0",
+    "info" : {
+      "version" : "1.0.0",
+      "title" : "Swagger Petstore"
+    },
+    "paths" : {
+      "/status" : {
+        "get" : {
+          "parameters" : [ {
+            "in" : "query",
+            "name" : "other",
+            "type" : "array",
+            "required" : true,
+            "items" : {
+              "type" : "string"
+            }
+          }, {
+            "in" : "query",
+            "name" : "$filter",
+            "type" : "array",
+            "required" : true,
+            "items" : {
+              "type" : "string"
+            }
+          } ],
+          "responses" : {
+            "206" : {
+              "description" : "Ok"
+            }
+          }
+        }
+      }
+    }
+  }
diff --git a/src/test/resources/checks/v2/parameters/OAR060/excluded-path.yaml b/src/test/resources/checks/v2/parameters/OAR060/excluded-path.yaml
new file mode 100644
index 00000000..0a40c64d
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR060/excluded-path.yaml
@@ -0,0 +1,23 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /status:
+    get:
+      parameters:
+        - in: query
+          name: other
+          type: array
+          required: true
+          items:
+            type: string
+        - in: query
+          name: $filter
+          type: array
+          required: true
+          items:
+            type: string
+      responses:
+        206:
+          description: Ok
diff --git a/src/test/resources/checks/v2/parameters/OAR060/ref-excluded-path.yaml b/src/test/resources/checks/v2/parameters/OAR060/ref-excluded-path.yaml
new file mode 100644
index 00000000..33913eaa
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR060/ref-excluded-path.yaml
@@ -0,0 +1,35 @@
+swagger: "2.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared param used only by an excluded path
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/parameters/SharedRefOnly'
+      responses:
+        200:
+          description: Ok
+  /pets:
+    get:
+      parameters:
+        - $ref: '#/parameters/SharedByPets'
+      responses:
+        200:
+          description: Ok
+parameters:
+  SharedRefOnly:
+    in: query
+    name: q
+    type: string
+    required: true
+  SharedByPets:
+    in: query
+    name: p
+    type: string
+    required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+  NeverReferenced:
+    in: query
+    name: unused
+    type: string
+    required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
diff --git a/src/test/resources/checks/v2/parameters/OAR060/required-true.json b/src/test/resources/checks/v2/parameters/OAR060/required-true.json
index 7873ddd1..32746f81 100644
--- a/src/test/resources/checks/v2/parameters/OAR060/required-true.json
+++ b/src/test/resources/checks/v2/parameters/OAR060/required-true.json
@@ -5,7 +5,7 @@
       "title" : "Swagger Petstore"
     },
     "paths" : {
-      "/status" : {
+      "/pets" : {
         "get" : {
           "parameters" : [ {
             "in" : "query",
diff --git a/src/test/resources/checks/v2/parameters/OAR060/required-true.yaml b/src/test/resources/checks/v2/parameters/OAR060/required-true.yaml
index 3072d144..5f7a4462 100644
--- a/src/test/resources/checks/v2/parameters/OAR060/required-true.yaml
+++ b/src/test/resources/checks/v2/parameters/OAR060/required-true.yaml
@@ -3,7 +3,7 @@ info:
   version: 1.0.0
   title: Swagger Petstore
 paths:
-  /status:
+  /pets:
     get:
       parameters:
         - in: query
diff --git a/src/test/resources/checks/v2/schemas/OAR108/quoted-number.yaml b/src/test/resources/checks/v2/schemas/OAR108/quoted-number.yaml
new file mode 100644
index 00000000..bf0acb83
--- /dev/null
+++ b/src/test/resources/checks/v2/schemas/OAR108/quoted-number.yaml
@@ -0,0 +1,66 @@
+swagger: '2.0'
+info:
+  title: Ejemplo de API
+  version: 1.0.0
+paths:
+  /item:
+    get:
+      responses:
+        200:
+          description: OK
+          schema:
+            type: object
+            properties:
+              id:
+                type: integer
+              nombre:
+                type: string
+            required:
+              - id
+              - nombre
+          examples: # Noncompliant {{OAR108: Schema does not match the provided example}}
+            application/json:
+              id: "123"
+              nombre: "Ejemplo"
+  /nulos:
+    get:
+      responses:
+        200:
+          description: OK
+          schema:
+            type: object
+            properties:
+              id:
+                type: integer
+              nombre:
+                type: string
+          examples:
+            application/json:
+              id:
+              nombre: null
+  /enteros:
+    get:
+      responses:
+        200:
+          description: OK
+          schema:
+            type: object
+            properties:
+              total:
+                type: number
+          examples:
+            application/json:
+              total: 5
+  /sin-type:
+    get:
+      responses:
+        200:
+          description: OK
+          schema:
+            type: object
+            properties:
+              sinType:
+                description: sin type
+          examples:
+            application/json:
+              sinType: "algo"
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-container.yaml b/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-container.yaml
new file mode 100644
index 00000000..d7fe8cbf
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-container.yaml
@@ -0,0 +1,10 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes: # Noncompliant {{OAR002: WSO2 scopes definition is wrong}}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-scopes.yaml b/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-scopes.yaml
new file mode 100644
index 00000000..9fc21704
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR002/with-empty-value-scopes.yaml
@@ -0,0 +1,38 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      - name: read
+        key: read
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: write
+        key: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_WRITE
+      - name: # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view
+        roles: ROLE_VIEW
+      - name: tilde
+        key: tilde
+        roles: ~ # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: capitalised
+        key: capitalised
+        roles: Null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: uppercase
+        key: uppercase
+        roles: NULL # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: quoted-null
+        key: quoted-null
+        roles: "null"
+      - name: quoted-tilde
+        key: quoted-tilde
+        roles: "~"
+      - name: duplicated
+        key: duplicated
+        roles: ROLE_FIRST
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes-as-map.yaml b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes-as-map.yaml
new file mode 100644
index 00000000..3d2cf7af
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes-as-map.yaml
@@ -0,0 +1,21 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        name: read
+        roles: ROLE_READ
+      write:
+        name: write
+        key: write
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      manage:
+        name: manage
+        key: manage
+        roles: ROLE_MANAGE
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.json b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.json
index 79789ef4..3683ae6d 100644
--- a/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.json
+++ b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.json
@@ -30,6 +30,22 @@
         "key" : "view2",
         "roles" : "ROLE_VIEW_2",
         "name" : null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      }, {
+        "name" : "read3",
+        "key" : "read3",
+        "roles" : [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : "read4",
+        "key" : "read4",
+        "roles" : {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : [], # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        "key" : "view3",
+        "roles" : "ROLE_VIEW_3"
+      }, {
+        "name" : "view4",
+        "key" : {}, # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        "roles" : "ROLE_VIEW_4"
       } ]
     }
   }
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.yaml b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.yaml
index 4c8994cb..29c82171 100644
--- a/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.yaml
+++ b/src/test/resources/checks/v3/apim/wso2/OAR002/with-scopes.yaml
@@ -22,4 +22,16 @@ x-wso2-security:
         roles: null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
       - key: view2
         roles: ROLE_VIEW_2
-        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
\ No newline at end of file
+        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      - name: read3
+        key: read3
+        roles: [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: read4
+        key: read4
+        roles: {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: [] # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view3
+        roles: ROLE_VIEW_3
+      - name: view4
+        key: {} # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_VIEW_4
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.json b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.json
new file mode 100644
index 00000000..95e577dd
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.json
@@ -0,0 +1,32 @@
+{
+  "openapi" : "3.0.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/securityAlias"
+  },
+  "x-wso2-definitions" : {
+    "securityAlias" : {
+      "$ref" : "#/x-wso2-definitions/security"
+    },
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.yaml b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.yaml
new file mode 100644
index 00000000..d54bf5d8
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-chained-ref-security.yaml
@@ -0,0 +1,22 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/securityAlias"
+x-wso2-definitions:
+  securityAlias:
+    $ref: "#/x-wso2-definitions/security"
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.json b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.json
new file mode 100644
index 00000000..2f8441e3
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.json
@@ -0,0 +1,29 @@
+{
+  "openapi" : "3.0.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/security"
+  },
+  "x-wso2-definitions" : {
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.yaml b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.yaml
new file mode 100644
index 00000000..4cd86003
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-ref-security.yaml
@@ -0,0 +1,67 @@
+# OAR003 — the whole x-wso2-security block behind a $ref. JsonNode.get() never resolves references,
+# so before the fix the scope list looked absent to Sonar and it reported nothing, while Spectral
+# lints the resolved document. After the fix Sonar resolves the reference and reports the scope.
+openapi: "3.0.3"
+info:
+  title: Pet Store WSO2 scopes behind a reference
+  description: The same catalogue API with its WSO2 security block factored out behind a $ref.
+  version: 1.0.0
+  contact:
+    name: APIQuality
+    email: support@apiquality.io
+    url: https://apiquality.io
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0.html
+servers:
+  - url: https://api.example.com/petstore/v1
+    description: Production
+tags:
+  - name: pets
+    description: Everything about the pets in the catalogue
+paths:
+  /pets:
+    get:
+      tags:
+        - pets
+      operationId: listPets
+      summary: List pets
+      description: Returns every pet in the catalogue.
+      responses:
+        '200':
+          description: The list of pets
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/Pet"
+components:
+  schemas:
+    Pet:
+      type: object
+      description: One pet in the catalogue.
+      required:
+        - id
+        - name
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: The pet's identifier.
+        name:
+          type: string
+          description: The pet's name.
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/security"
+x-wso2-definitions:
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.json b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.json
new file mode 100644
index 00000000..94bd3753
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.json
@@ -0,0 +1,27 @@
+{
+  "openapi" : "3.0.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.yaml b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.yaml
new file mode 100644
index 00000000..0c66f6f7
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/fail-scopes-as-map.yaml
@@ -0,0 +1,66 @@
+# OAR003 — x-wso2-scopes written as a mapping keyed by scope name instead of the array WSO2 expects.
+# A mapping, so ObjectNode.elements() yields nothing and OAR003 visited no scope on the Sonar side
+# before the fix, while Spectral's [*] iterates the mapping's values.
+openapi: "3.0.3"
+info:
+  title: Pet Store WSO2 scopes as a mapping
+  description: The same catalogue API with its WSO2 scopes written as a mapping instead of an array.
+  version: 1.0.0
+  contact:
+    name: APIQuality
+    email: support@apiquality.io
+    url: https://apiquality.io
+  license:
+    name: Apache 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0.html
+servers:
+  - url: https://api.example.com/petstore/v1
+    description: Production
+tags:
+  - name: pets
+    description: Everything about the pets in the catalogue
+paths:
+  /pets:
+    get:
+      tags:
+        - pets
+      operationId: listPets
+      summary: List pets
+      description: Returns every pet in the catalogue.
+      responses:
+        '200':
+          description: The list of pets
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/Pet"
+components:
+  schemas:
+    Pet:
+      type: object
+      description: One pet in the catalogue.
+      required:
+        - id
+        - name
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: The pet's identifier.
+        name:
+          type: string
+          description: The pet's name.
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+        key: write
+        roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.json b/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.json
new file mode 100644
index 00000000..a7922e2b
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.json
@@ -0,0 +1,33 @@
+{
+  "openapi" : "3.0.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : {
+          "$ref" : "#/x-wso2-definitions/writeScope"
+        }
+      }
+    }
+  },
+  "x-wso2-definitions" : {
+    "writeScope" : {
+      "name" : "write",
+      "key" : "write",
+      "roles" : "ROLE_WRITE",
+      "description" : "Allows users to write the catalogue"
+    }
+  }
+}
diff --git a/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.yaml b/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.yaml
new file mode 100644
index 00000000..6dfbbe4f
--- /dev/null
+++ b/src/test/resources/checks/v3/apim/wso2/OAR003/ref-scope-with-description.yaml
@@ -0,0 +1,23 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        $ref: "#/x-wso2-definitions/writeScope"
+x-wso2-definitions:
+  writeScope:
+    name: write
+    key: write
+    roles: ROLE_WRITE
+    description: Allows users to write the catalogue
diff --git a/src/test/resources/checks/v3/format/OAR116/invalid.yaml b/src/test/resources/checks/v3/format/OAR116/invalid.yaml
new file mode 100644
index 00000000..8b65f479
--- /dev/null
+++ b/src/test/resources/checks/v3/format/OAR116/invalid.yaml
@@ -0,0 +1,15 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /v1/pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/v[0-9]+}}
+    get:
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v3/format/OAR116/valid.json b/src/test/resources/checks/v3/format/OAR116/valid.json
new file mode 100644
index 00000000..8e79badd
--- /dev/null
+++ b/src/test/resources/checks/v3/format/OAR116/valid.json
@@ -0,0 +1,35 @@
+{
+  "openapi" : "3.0.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Sample API"
+  },
+  "paths" : {
+    "/pets" : {
+      "get" : {
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    },
+    "/pets/{id}" : {
+      "get" : {
+        "parameters" : [ {
+          "in" : "path",
+          "name" : "id",
+          "required" : true,
+          "schema" : {
+            "type" : "string"
+          }
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v3/format/OAR116/valid.yaml b/src/test/resources/checks/v3/format/OAR116/valid.yaml
new file mode 100644
index 00000000..d5504dfc
--- /dev/null
+++ b/src/test/resources/checks/v3/format/OAR116/valid.yaml
@@ -0,0 +1,21 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: string
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v3/parameters/OAR060/components-query.yaml b/src/test/resources/checks/v3/parameters/OAR060/components-query.yaml
new file mode 100644
index 00000000..92d52d01
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/components-query.yaml
@@ -0,0 +1,18 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 components query param
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SharedQuery:
+      in: query
+      name: q
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v3/parameters/OAR060/edge-params.yaml b/src/test/resources/checks/v3/parameters/OAR060/edge-params.yaml
new file mode 100644
index 00000000..16b93b65
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/edge-params.yaml
@@ -0,0 +1,24 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 edge params
+paths:
+  /pets:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SomeRef'
+        - in: query
+          name: noRequiredFlag
+          schema:
+            type: string
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SomeRef:
+      in: header
+      name: X-Ref
+      required: false
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v3/parameters/OAR060/excluded-path.json b/src/test/resources/checks/v3/parameters/OAR060/excluded-path.json
new file mode 100644
index 00000000..0cbe884b
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/excluded-path.json
@@ -0,0 +1,39 @@
+{
+    "openapi" : "3.0.0",
+    "info" : {
+      "version" : "1.0.0",
+      "title" : "Swagger Petstore"
+    },
+    "paths" : {
+      "/status" : {
+        "get" : {
+          "parameters" : [ {
+            "in" : "query",
+            "name" : "other",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          }, {
+            "in" : "query",
+            "name" : "$filter",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          } ],
+          "responses" : {
+            "206" : {
+              "description" : "Ok"
+            }
+          }
+        }
+      }
+    }
+  }
diff --git a/src/test/resources/checks/v3/parameters/OAR060/excluded-path.yaml b/src/test/resources/checks/v3/parameters/OAR060/excluded-path.yaml
new file mode 100644
index 00000000..124f66f9
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/excluded-path.yaml
@@ -0,0 +1,25 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /status:
+    get:
+      parameters:
+        - in: query
+          name: other
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+        - in: query
+          name: $filter
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+      responses:
+        206:
+          description: Ok
diff --git a/src/test/resources/checks/v3/parameters/OAR060/no-exclusions.yaml b/src/test/resources/checks/v3/parameters/OAR060/no-exclusions.yaml
new file mode 100644
index 00000000..c75c3921
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/no-exclusions.yaml
@@ -0,0 +1,16 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 empty exclusions
+paths:
+  /status:
+    get:
+      parameters:
+        - in: query
+          name: verbose
+          required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+          schema:
+            type: boolean
+      responses:
+        "200":
+          description: OK
diff --git a/src/test/resources/checks/v3/parameters/OAR060/non-query.yaml b/src/test/resources/checks/v3/parameters/OAR060/non-query.yaml
new file mode 100644
index 00000000..7d873771
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/non-query.yaml
@@ -0,0 +1,26 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 non-query params
+paths:
+  /pets/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: string
+        - in: header
+          name: X-Trace
+          required: true
+          schema:
+            type: string
+        - in: cookie
+          name: session
+          required: true
+          schema:
+            type: string
+      responses:
+        "200":
+          description: OK
diff --git a/src/test/resources/checks/v3/parameters/OAR060/path-level.yaml b/src/test/resources/checks/v3/parameters/OAR060/path-level.yaml
new file mode 100644
index 00000000..20b5be86
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/path-level.yaml
@@ -0,0 +1,16 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 path-level params
+paths:
+  /pets:
+    parameters:
+      - in: query
+        name: shared
+        required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+        schema:
+          type: string
+    get:
+      responses:
+        "200":
+          description: OK
diff --git a/src/test/resources/checks/v3/parameters/OAR060/ref-excluded-path.yaml b/src/test/resources/checks/v3/parameters/OAR060/ref-excluded-path.yaml
new file mode 100644
index 00000000..1e51ba5a
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/ref-excluded-path.yaml
@@ -0,0 +1,34 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared param used only by an excluded path
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedRefOnly'
+      responses:
+        "200":
+          description: OK
+    parameters:
+      - $ref: '#/components/parameters/SharedPathLevel'
+components:
+  parameters:
+    SharedRefOnly:
+      in: query
+      name: q
+      required: true
+      schema:
+        type: string
+    SharedPathLevel:
+      in: query
+      name: p
+      required: true
+      schema:
+        type: string
+    NeverReferenced:
+      in: query
+      name: unused
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v3/parameters/OAR060/ref-external.yaml b/src/test/resources/checks/v3/parameters/OAR060/ref-external.yaml
new file mode 100644
index 00000000..e750d557
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/ref-external.yaml
@@ -0,0 +1,24 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 external parameter ref
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: './common.yaml#/components/parameters/External'
+      responses:
+        "200":
+          description: OK
+  /pets:
+    get:
+      parameters:
+        - $ref: './common.yaml#/components/parameters/External'
+        - in: query
+          name: inline
+          required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+          schema:
+            type: string
+      responses:
+        "200":
+          description: OK
diff --git a/src/test/resources/checks/v3/parameters/OAR060/ref-mixed-paths.yaml b/src/test/resources/checks/v3/parameters/OAR060/ref-mixed-paths.yaml
new file mode 100644
index 00000000..91890337
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/ref-mixed-paths.yaml
@@ -0,0 +1,36 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared params across excluded and non-excluded paths
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedByBoth'
+      responses:
+        "200":
+          description: OK
+  /pets:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedByBoth'
+        - $ref: '#/components/parameters/ChainedAlias'
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SharedByBoth:
+      in: query
+      name: q
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
+    ChainedAlias:
+      $ref: '#/components/parameters/ChainTarget'
+    ChainTarget:
+      in: query
+      name: c
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v3/parameters/OAR060/ref-no-exclusions.yaml b/src/test/resources/checks/v3/parameters/OAR060/ref-no-exclusions.yaml
new file mode 100644
index 00000000..61cbc298
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/ref-no-exclusions.yaml
@@ -0,0 +1,20 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared param with no exclusions configured
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedRefOnly'
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SharedRefOnly:
+      in: query
+      name: q
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v3/parameters/OAR060/required-true.json b/src/test/resources/checks/v3/parameters/OAR060/required-true.json
index c57cdd06..ffb18531 100644
--- a/src/test/resources/checks/v3/parameters/OAR060/required-true.json
+++ b/src/test/resources/checks/v3/parameters/OAR060/required-true.json
@@ -5,7 +5,7 @@
       "title" : "Swagger Petstore"
     },
     "paths" : {
-      "/status" : {
+      "/pets" : {
         "get" : {
           "parameters" : [ {
             "in" : "query",
diff --git a/src/test/resources/checks/v3/parameters/OAR060/required-true.yaml b/src/test/resources/checks/v3/parameters/OAR060/required-true.yaml
index 00a697fb..f7579b07 100644
--- a/src/test/resources/checks/v3/parameters/OAR060/required-true.yaml
+++ b/src/test/resources/checks/v3/parameters/OAR060/required-true.yaml
@@ -3,7 +3,7 @@ info:
   version: 1.0.0
   title: Swagger Petstore
 paths:
-  /status:
+  /pets:
     get:
       parameters:
         - in: query
diff --git a/src/test/resources/checks/v3/parameters/OAR060/subpath.yaml b/src/test/resources/checks/v3/parameters/OAR060/subpath.yaml
new file mode 100644
index 00000000..61f0af84
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR060/subpath.yaml
@@ -0,0 +1,16 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: OAR060 subpath not excluded
+paths:
+  /status/health:
+    get:
+      parameters:
+        - in: query
+          name: verbose
+          required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+          schema:
+            type: boolean
+      responses:
+        "200":
+          description: OK
diff --git a/src/test/resources/checks/v3/security/OAR075/single-restriction.yaml b/src/test/resources/checks/v3/security/OAR075/single-restriction.yaml
new file mode 100644
index 00000000..24c83a81
--- /dev/null
+++ b/src/test/resources/checks/v3/security/OAR075/single-restriction.yaml
@@ -0,0 +1,36 @@
+openapi: 3.0.0
+info:
+  title: OAR075 - una sola restriccion basta
+  version: 1.0.0
+paths:
+  /users:
+    get:
+      parameters:
+        - name: soloMinLength
+          in: query
+          schema:
+            type: string
+            minLength: 1
+        - name: soloMaxLength
+          in: query
+          schema:
+            type: string
+            maxLength: 10
+        - name: soloPattern
+          in: query
+          schema:
+            type: string
+            pattern: '^[a-z]+$'
+        - name: soloEnum
+          in: query
+          schema:
+            type: string
+            enum: ['admin', 'user']
+        - name: soloFormat
+          in: query
+          schema:
+            type: string  # Noncompliant {{OAR075: String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction that are defined in the properties}}
+            format: date
+      responses:
+        '200':
+          description: ok
diff --git a/src/test/resources/checks/v3/security/OAR085/valid-openapi-version-304.yaml b/src/test/resources/checks/v3/security/OAR085/valid-openapi-version-304.yaml
new file mode 100644
index 00000000..4fec745f
--- /dev/null
+++ b/src/test/resources/checks/v3/security/OAR085/valid-openapi-version-304.yaml
@@ -0,0 +1,6 @@
+openapi: 3.0.4
+info:
+  title: Sample API
+  description: This is a sample API.
+  version: 1.0.0
+paths: {}
diff --git a/src/test/resources/checks/v31/apim/OAR002/with-empty-value-container.yaml b/src/test/resources/checks/v31/apim/OAR002/with-empty-value-container.yaml
new file mode 100644
index 00000000..850d6426
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR002/with-empty-value-container.yaml
@@ -0,0 +1,10 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes: # Noncompliant {{OAR002: WSO2 scopes definition is wrong}}
diff --git a/src/test/resources/checks/v31/apim/OAR002/with-empty-value-scopes.yaml b/src/test/resources/checks/v31/apim/OAR002/with-empty-value-scopes.yaml
new file mode 100644
index 00000000..03732920
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR002/with-empty-value-scopes.yaml
@@ -0,0 +1,38 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      - name: read
+        key: read
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: write
+        key: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_WRITE
+      - name: # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view
+        roles: ROLE_VIEW
+      - name: tilde
+        key: tilde
+        roles: ~ # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: capitalised
+        key: capitalised
+        roles: Null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: uppercase
+        key: uppercase
+        roles: NULL # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: quoted-null
+        key: quoted-null
+        roles: "null"
+      - name: quoted-tilde
+        key: quoted-tilde
+        roles: "~"
+      - name: duplicated
+        key: duplicated
+        roles: ROLE_FIRST
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
diff --git a/src/test/resources/checks/v31/apim/OAR002/with-scopes-as-map.yaml b/src/test/resources/checks/v31/apim/OAR002/with-scopes-as-map.yaml
new file mode 100644
index 00000000..5aad43ce
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR002/with-scopes-as-map.yaml
@@ -0,0 +1,21 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        name: read
+        roles: ROLE_READ
+      write:
+        name: write
+        key: write
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      manage:
+        name: manage
+        key: manage
+        roles: ROLE_MANAGE
diff --git a/src/test/resources/checks/v31/apim/OAR002/with-scopes.json b/src/test/resources/checks/v31/apim/OAR002/with-scopes.json
index 807c5ce0..2794caaf 100644
--- a/src/test/resources/checks/v31/apim/OAR002/with-scopes.json
+++ b/src/test/resources/checks/v31/apim/OAR002/with-scopes.json
@@ -30,6 +30,22 @@
         "key" : "view2",
         "roles" : "ROLE_VIEW_2",
         "name" : null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      }, {
+        "name" : "read3",
+        "key" : "read3",
+        "roles" : [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : "read4",
+        "key" : "read4",
+        "roles" : {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : [], # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        "key" : "view3",
+        "roles" : "ROLE_VIEW_3"
+      }, {
+        "name" : "view4",
+        "key" : {}, # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        "roles" : "ROLE_VIEW_4"
       } ]
     }
   }
diff --git a/src/test/resources/checks/v31/apim/OAR002/with-scopes.yaml b/src/test/resources/checks/v31/apim/OAR002/with-scopes.yaml
index 3c78c8ee..34c40976 100644
--- a/src/test/resources/checks/v31/apim/OAR002/with-scopes.yaml
+++ b/src/test/resources/checks/v31/apim/OAR002/with-scopes.yaml
@@ -22,4 +22,16 @@ x-wso2-security:
         roles: null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
       - key: view2
         roles: ROLE_VIEW_2
-        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
\ No newline at end of file
+        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      - name: read3
+        key: read3
+        roles: [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: read4
+        key: read4
+        roles: {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: [] # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view3
+        roles: ROLE_VIEW_3
+      - name: view4
+        key: {} # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_VIEW_4
\ No newline at end of file
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.json b/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.json
new file mode 100644
index 00000000..57c857c7
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.json
@@ -0,0 +1,32 @@
+{
+  "openapi" : "3.1.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/securityAlias"
+  },
+  "x-wso2-definitions" : {
+    "securityAlias" : {
+      "$ref" : "#/x-wso2-definitions/security"
+    },
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.yaml b/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.yaml
new file mode 100644
index 00000000..0e83ac74
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-chained-ref-security.yaml
@@ -0,0 +1,22 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/securityAlias"
+x-wso2-definitions:
+  securityAlias:
+    $ref: "#/x-wso2-definitions/security"
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.json b/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.json
new file mode 100644
index 00000000..c86d0759
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.json
@@ -0,0 +1,29 @@
+{
+  "openapi" : "3.1.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/security"
+  },
+  "x-wso2-definitions" : {
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.yaml b/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.yaml
new file mode 100644
index 00000000..cf221e8d
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-ref-security.yaml
@@ -0,0 +1,20 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/security"
+x-wso2-definitions:
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.json b/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.json
new file mode 100644
index 00000000..a7d21d3e
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.json
@@ -0,0 +1,27 @@
+{
+  "openapi" : "3.1.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.yaml b/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.yaml
new file mode 100644
index 00000000..6063131b
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/fail-scopes-as-map.yaml
@@ -0,0 +1,19 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+        key: write
+        roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.json b/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.json
new file mode 100644
index 00000000..a957aa90
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.json
@@ -0,0 +1,33 @@
+{
+  "openapi" : "3.1.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : {
+          "$ref" : "#/x-wso2-definitions/writeScope"
+        }
+      }
+    }
+  },
+  "x-wso2-definitions" : {
+    "writeScope" : {
+      "name" : "write",
+      "key" : "write",
+      "roles" : "ROLE_WRITE",
+      "description" : "Allows users to write the catalogue"
+    }
+  }
+}
diff --git a/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.yaml b/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.yaml
new file mode 100644
index 00000000..3525a799
--- /dev/null
+++ b/src/test/resources/checks/v31/apim/OAR003/ref-scope-with-description.yaml
@@ -0,0 +1,23 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        $ref: "#/x-wso2-definitions/writeScope"
+x-wso2-definitions:
+  writeScope:
+    name: write
+    key: write
+    roles: ROLE_WRITE
+    description: Allows users to write the catalogue
diff --git a/src/test/resources/checks/v31/format/OAR116/invalid.yaml b/src/test/resources/checks/v31/format/OAR116/invalid.yaml
new file mode 100644
index 00000000..7289b912
--- /dev/null
+++ b/src/test/resources/checks/v31/format/OAR116/invalid.yaml
@@ -0,0 +1,15 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /v1/pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/v[0-9]+}}
+    get:
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v31/format/OAR116/valid.json b/src/test/resources/checks/v31/format/OAR116/valid.json
new file mode 100644
index 00000000..c7df1855
--- /dev/null
+++ b/src/test/resources/checks/v31/format/OAR116/valid.json
@@ -0,0 +1,35 @@
+{
+  "openapi" : "3.1.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Sample API"
+  },
+  "paths" : {
+    "/pets" : {
+      "get" : {
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    },
+    "/pets/{id}" : {
+      "get" : {
+        "parameters" : [ {
+          "in" : "path",
+          "name" : "id",
+          "required" : true,
+          "schema" : {
+            "type" : "string"
+          }
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v31/format/OAR116/valid.yaml b/src/test/resources/checks/v31/format/OAR116/valid.yaml
new file mode 100644
index 00000000..b098e837
--- /dev/null
+++ b/src/test/resources/checks/v31/format/OAR116/valid.yaml
@@ -0,0 +1,21 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: string
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v31/parameters/OAR060/excluded-path.json b/src/test/resources/checks/v31/parameters/OAR060/excluded-path.json
new file mode 100644
index 00000000..736eaa28
--- /dev/null
+++ b/src/test/resources/checks/v31/parameters/OAR060/excluded-path.json
@@ -0,0 +1,39 @@
+{
+    "openapi" : "3.1.0",
+    "info" : {
+      "version" : "1.0.0",
+      "title" : "Swagger Petstore"
+    },
+    "paths" : {
+      "/status" : {
+        "get" : {
+          "parameters" : [ {
+            "in" : "query",
+            "name" : "other",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          }, {
+            "in" : "query",
+            "name" : "$filter",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          } ],
+          "responses" : {
+            "206" : {
+              "description" : "Ok"
+            }
+          }
+        }
+      }
+    }
+  }
diff --git a/src/test/resources/checks/v31/parameters/OAR060/excluded-path.yaml b/src/test/resources/checks/v31/parameters/OAR060/excluded-path.yaml
new file mode 100644
index 00000000..b988b0a9
--- /dev/null
+++ b/src/test/resources/checks/v31/parameters/OAR060/excluded-path.yaml
@@ -0,0 +1,25 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /status:
+    get:
+      parameters:
+        - in: query
+          name: other
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+        - in: query
+          name: $filter
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+      responses:
+        206:
+          description: Ok
diff --git a/src/test/resources/checks/v31/parameters/OAR060/ref-excluded-path.yaml b/src/test/resources/checks/v31/parameters/OAR060/ref-excluded-path.yaml
new file mode 100644
index 00000000..e00a371b
--- /dev/null
+++ b/src/test/resources/checks/v31/parameters/OAR060/ref-excluded-path.yaml
@@ -0,0 +1,33 @@
+openapi: "3.1.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared param used only by an excluded path
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedRefOnly'
+      responses:
+        "200":
+          description: OK
+  /pets:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedByPets'
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SharedRefOnly:
+      in: query
+      name: q
+      required: true
+      schema:
+        type: string
+    SharedByPets:
+      in: query
+      name: p
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v31/parameters/OAR060/required-true.json b/src/test/resources/checks/v31/parameters/OAR060/required-true.json
index d9ffefcf..b01ebf5d 100644
--- a/src/test/resources/checks/v31/parameters/OAR060/required-true.json
+++ b/src/test/resources/checks/v31/parameters/OAR060/required-true.json
@@ -5,7 +5,7 @@
       "title" : "Swagger Petstore"
     },
     "paths" : {
-      "/status" : {
+      "/pets" : {
         "get" : {
           "parameters" : [ {
             "in" : "query",
diff --git a/src/test/resources/checks/v31/parameters/OAR060/required-true.yaml b/src/test/resources/checks/v31/parameters/OAR060/required-true.yaml
index 95969ebd..e973fa15 100644
--- a/src/test/resources/checks/v31/parameters/OAR060/required-true.yaml
+++ b/src/test/resources/checks/v31/parameters/OAR060/required-true.yaml
@@ -3,7 +3,7 @@ info:
   version: 1.0.0
   title: Swagger Petstore
 paths:
-  /status:
+  /pets:
     get:
       parameters:
         - in: query
diff --git a/src/test/resources/checks/v31/schemas/OAR108/array-type.yaml b/src/test/resources/checks/v31/schemas/OAR108/array-type.yaml
new file mode 100644
index 00000000..e6a5f1d8
--- /dev/null
+++ b/src/test/resources/checks/v31/schemas/OAR108/array-type.yaml
@@ -0,0 +1,56 @@
+openapi: 3.1.1
+info:
+  title: OAR108 - forma-array de type (JSON Schema 2020-12)
+  version: 1.0.0
+paths:
+  /mismatch:
+    get:
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  nullAlFinal:
+                    type: ["integer", "null"]
+                  nullDelante:
+                    type: ["null", "integer"]
+              example: # Noncompliant {{OAR108: Schema does not match the provided example}}
+                nullAlFinal: "mal"
+                nullDelante: "mal"
+  /match:
+    get:
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  nullAlFinal:
+                    type: ["integer", "null"]
+                  nullDelante:
+                    type: ["null", "integer"]
+                  cadena:
+                    type: ["string", "null"]
+              example:
+                nullAlFinal: 1
+                nullDelante: 2
+                cadena: "ok"
+  /nulos:
+    get:
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  id:
+                    type: ["integer", "null"]
+              example:
+                id:
diff --git a/src/test/resources/checks/v31/security/OAR075/array-type.yaml b/src/test/resources/checks/v31/security/OAR075/array-type.yaml
new file mode 100644
index 00000000..d86ee880
--- /dev/null
+++ b/src/test/resources/checks/v31/security/OAR075/array-type.yaml
@@ -0,0 +1,45 @@
+openapi: 3.1.1
+info:
+  title: OAR075 - forma-array de type (JSON Schema 2020-12)
+  version: 1.0.0
+paths:
+  /users:
+    get:
+      parameters:
+        - name: nullAlFinalSinRestricciones
+          in: query
+          schema:
+            type: ["string", "null"]  # Noncompliant {{OAR075: String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction that are defined in the properties}}
+        - name: nullDelanteSinRestricciones
+          in: query
+          schema:
+            type: ["null", "string"]  # Noncompliant {{OAR075: String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction that are defined in the properties}}
+        - name: arrayDeUnElemento
+          in: query
+          schema:
+            type: ["string"]  # Noncompliant {{OAR075: String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction that are defined in the properties}}
+        - name: nullAlFinalConMinLength
+          in: query
+          schema:
+            type: ["string", "null"]
+            minLength: 1
+        - name: nullDelanteConEnum
+          in: query
+          schema:
+            type: ["null", "string"]
+            enum: ['a', 'b']
+        - name: soloNull
+          in: query
+          schema:
+            type: ["null"]
+        - name: nullEscalar
+          in: query
+          schema:
+            type: "null"
+        - name: numerico
+          in: query
+          schema:
+            type: ["integer", "null"]
+      responses:
+        '200':
+          description: ok
diff --git a/src/test/resources/checks/v31/security/OAR082/array-type-format.yaml b/src/test/resources/checks/v31/security/OAR082/array-type-format.yaml
new file mode 100644
index 00000000..10f83e4a
--- /dev/null
+++ b/src/test/resources/checks/v31/security/OAR082/array-type-format.yaml
@@ -0,0 +1,23 @@
+openapi: "3.1.1"
+info:
+  version: "1.0.0"
+  title: "Swagger Petstore"
+paths:
+  /invoices:
+    get:
+      responses:
+        '200':
+          description: A invoice.
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  product:
+                    type: ["string", "null"] # Noncompliant {{OAR082: The string properties among product,line,price must define a byte or binary format}}
+                  line:
+                    type: ["string", "null"]
+                    contentEncoding: base64
+                  price:
+                    type: string
+                    format: binary
diff --git a/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-311.yaml b/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-311.yaml
new file mode 100644
index 00000000..99a531e9
--- /dev/null
+++ b/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-311.yaml
@@ -0,0 +1,6 @@
+openapi: "3.1.1"
+info:
+  title: Sample API
+  description: This is a sample API.
+  version: 1.0.0
+paths: {}
diff --git a/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-312.yaml b/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-312.yaml
new file mode 100644
index 00000000..038eebbf
--- /dev/null
+++ b/src/test/resources/checks/v31/security/OAR085/valid-openapi-version-312.yaml
@@ -0,0 +1,6 @@
+openapi: "3.1.2"
+info:
+  title: Sample API
+  description: This is a sample API.
+  version: 1.0.0
+paths: {}
diff --git a/src/test/resources/checks/v32/apim/OAR002/with-empty-value-container.yaml b/src/test/resources/checks/v32/apim/OAR002/with-empty-value-container.yaml
new file mode 100644
index 00000000..f22a310f
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR002/with-empty-value-container.yaml
@@ -0,0 +1,10 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes: # Noncompliant {{OAR002: WSO2 scopes definition is wrong}}
diff --git a/src/test/resources/checks/v32/apim/OAR002/with-empty-value-scopes.yaml b/src/test/resources/checks/v32/apim/OAR002/with-empty-value-scopes.yaml
new file mode 100644
index 00000000..0275ee99
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR002/with-empty-value-scopes.yaml
@@ -0,0 +1,38 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      - name: read
+        key: read
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: write
+        key: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_WRITE
+      - name: # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view
+        roles: ROLE_VIEW
+      - name: tilde
+        key: tilde
+        roles: ~ # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: capitalised
+        key: capitalised
+        roles: Null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: uppercase
+        key: uppercase
+        roles: NULL # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: quoted-null
+        key: quoted-null
+        roles: "null"
+      - name: quoted-tilde
+        key: quoted-tilde
+        roles: "~"
+      - name: duplicated
+        key: duplicated
+        roles: ROLE_FIRST
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
diff --git a/src/test/resources/checks/v32/apim/OAR002/with-scopes-as-map.yaml b/src/test/resources/checks/v32/apim/OAR002/with-scopes-as-map.yaml
new file mode 100644
index 00000000..ac70f381
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR002/with-scopes-as-map.yaml
@@ -0,0 +1,21 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read: # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        name: read
+        roles: ROLE_READ
+      write:
+        name: write
+        key: write
+        roles: # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      manage:
+        name: manage
+        key: manage
+        roles: ROLE_MANAGE
diff --git a/src/test/resources/checks/v32/apim/OAR002/with-scopes.json b/src/test/resources/checks/v32/apim/OAR002/with-scopes.json
index b5574cef..789a6e48 100644
--- a/src/test/resources/checks/v32/apim/OAR002/with-scopes.json
+++ b/src/test/resources/checks/v32/apim/OAR002/with-scopes.json
@@ -30,6 +30,22 @@
         "key" : "view2",
         "roles" : "ROLE_VIEW_2",
         "name" : null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      }, {
+        "name" : "read3",
+        "key" : "read3",
+        "roles" : [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : "read4",
+        "key" : "read4",
+        "roles" : {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      }, {
+        "name" : [], # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        "key" : "view3",
+        "roles" : "ROLE_VIEW_3"
+      }, {
+        "name" : "view4",
+        "key" : {}, # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        "roles" : "ROLE_VIEW_4"
       } ]
     }
   }
diff --git a/src/test/resources/checks/v32/apim/OAR002/with-scopes.yaml b/src/test/resources/checks/v32/apim/OAR002/with-scopes.yaml
index c853ee55..62787cc4 100644
--- a/src/test/resources/checks/v32/apim/OAR002/with-scopes.yaml
+++ b/src/test/resources/checks/v32/apim/OAR002/with-scopes.yaml
@@ -22,4 +22,16 @@ x-wso2-security:
         roles: null # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
       - key: view2
         roles: ROLE_VIEW_2
-        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
\ No newline at end of file
+        name: null # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+      - name: read3
+        key: read3
+        roles: [] # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: read4
+        key: read4
+        roles: {} # Noncompliant {{OAR002: WSO2 scope 'roles' is required}}
+      - name: [] # Noncompliant {{OAR002: WSO2 scope 'name' is required}}
+        key: view3
+        roles: ROLE_VIEW_3
+      - name: view4
+        key: {} # Noncompliant {{OAR002: WSO2 scope 'key' is required}}
+        roles: ROLE_VIEW_4
\ No newline at end of file
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.json b/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.json
new file mode 100644
index 00000000..59284764
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.json
@@ -0,0 +1,32 @@
+{
+  "openapi" : "3.2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/securityAlias"
+  },
+  "x-wso2-definitions" : {
+    "securityAlias" : {
+      "$ref" : "#/x-wso2-definitions/security"
+    },
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.yaml b/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.yaml
new file mode 100644
index 00000000..02656857
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-chained-ref-security.yaml
@@ -0,0 +1,22 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/securityAlias"
+x-wso2-definitions:
+  securityAlias:
+    $ref: "#/x-wso2-definitions/security"
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.json b/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.json
new file mode 100644
index 00000000..e3149baf
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.json
@@ -0,0 +1,29 @@
+{
+  "openapi" : "3.2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "$ref" : "#/x-wso2-definitions/security"
+  },
+  "x-wso2-definitions" : {
+    "security" : {
+      "apim" : {
+        "x-wso2-scopes" : [ {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        }, { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        } ]
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.yaml b/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.yaml
new file mode 100644
index 00000000..7475799b
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-ref-security.yaml
@@ -0,0 +1,20 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  $ref: "#/x-wso2-definitions/security"
+x-wso2-definitions:
+  security:
+    apim:
+      x-wso2-scopes:
+        - name: read
+          key: read
+          roles: ROLE_READ
+          description: Allows users to read the catalogue
+        - name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          key: write
+          roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.json b/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.json
new file mode 100644
index 00000000..cf7cf14c
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.json
@@ -0,0 +1,27 @@
+{
+  "openapi" : "3.2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : { # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+          "name" : "write",
+          "key" : "write",
+          "roles" : "ROLE_WRITE"
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.yaml b/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.yaml
new file mode 100644
index 00000000..5419e731
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/fail-scopes-as-map.yaml
@@ -0,0 +1,19 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        name: write # Noncompliant {{OAR003: WSO2 scope 'description' is recommended}}
+        key: write
+        roles: ROLE_WRITE
diff --git a/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.json b/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.json
new file mode 100644
index 00000000..e9353150
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.json
@@ -0,0 +1,33 @@
+{
+  "openapi" : "3.2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Swagger Petstore"
+  },
+  "paths" : {
+    "/pets" : { }
+  },
+  "x-wso2-security" : {
+    "apim" : {
+      "x-wso2-scopes" : {
+        "read" : {
+          "name" : "read",
+          "key" : "read",
+          "roles" : "ROLE_READ",
+          "description" : "Allows users to read the catalogue"
+        },
+        "write" : {
+          "$ref" : "#/x-wso2-definitions/writeScope"
+        }
+      }
+    }
+  },
+  "x-wso2-definitions" : {
+    "writeScope" : {
+      "name" : "write",
+      "key" : "write",
+      "roles" : "ROLE_WRITE",
+      "description" : "Allows users to write the catalogue"
+    }
+  }
+}
diff --git a/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.yaml b/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.yaml
new file mode 100644
index 00000000..eae34366
--- /dev/null
+++ b/src/test/resources/checks/v32/apim/OAR003/ref-scope-with-description.yaml
@@ -0,0 +1,23 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /pets: {}
+
+x-wso2-security:
+  apim:
+    x-wso2-scopes:
+      read:
+        name: read
+        key: read
+        roles: ROLE_READ
+        description: Allows users to read the catalogue
+      write:
+        $ref: "#/x-wso2-definitions/writeScope"
+x-wso2-definitions:
+  writeScope:
+    name: write
+    key: write
+    roles: ROLE_WRITE
+    description: Allows users to write the catalogue
diff --git a/src/test/resources/checks/v32/format/OAR116/invalid.yaml b/src/test/resources/checks/v32/format/OAR116/invalid.yaml
new file mode 100644
index 00000000..1d638f4e
--- /dev/null
+++ b/src/test/resources/checks/v32/format/OAR116/invalid.yaml
@@ -0,0 +1,15 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /v1/pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets: # Noncompliant {{OAR116: Path does not match the required pattern: ^/v[0-9]+}}
+    get:
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v32/format/OAR116/valid.json b/src/test/resources/checks/v32/format/OAR116/valid.json
new file mode 100644
index 00000000..3501b947
--- /dev/null
+++ b/src/test/resources/checks/v32/format/OAR116/valid.json
@@ -0,0 +1,35 @@
+{
+  "openapi" : "3.2.0",
+  "info" : {
+    "version" : "1.0.0",
+    "title" : "Sample API"
+  },
+  "paths" : {
+    "/pets" : {
+      "get" : {
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    },
+    "/pets/{id}" : {
+      "get" : {
+        "parameters" : [ {
+          "in" : "path",
+          "name" : "id",
+          "required" : true,
+          "schema" : {
+            "type" : "string"
+          }
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Ok"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/checks/v32/format/OAR116/valid.yaml b/src/test/resources/checks/v32/format/OAR116/valid.yaml
new file mode 100644
index 00000000..9ef63523
--- /dev/null
+++ b/src/test/resources/checks/v32/format/OAR116/valid.yaml
@@ -0,0 +1,21 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Sample API
+paths:
+  /pets:
+    get:
+      responses:
+        "200":
+          description: Ok
+  /pets/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: string
+      responses:
+        "200":
+          description: Ok
diff --git a/src/test/resources/checks/v32/parameters/OAR060/excluded-path.json b/src/test/resources/checks/v32/parameters/OAR060/excluded-path.json
new file mode 100644
index 00000000..7c3d226c
--- /dev/null
+++ b/src/test/resources/checks/v32/parameters/OAR060/excluded-path.json
@@ -0,0 +1,39 @@
+{
+    "openapi" : "3.2.0",
+    "info" : {
+      "version" : "1.0.0",
+      "title" : "Swagger Petstore"
+    },
+    "paths" : {
+      "/status" : {
+        "get" : {
+          "parameters" : [ {
+            "in" : "query",
+            "name" : "other",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          }, {
+            "in" : "query",
+            "name" : "$filter",
+            "required": true,
+            "schema": {
+              "type" : "array",
+              "items" : {
+                "type" : "string"
+              }
+            }
+          } ],
+          "responses" : {
+            "206" : {
+              "description" : "Ok"
+            }
+          }
+        }
+      }
+    }
+  }
diff --git a/src/test/resources/checks/v32/parameters/OAR060/excluded-path.yaml b/src/test/resources/checks/v32/parameters/OAR060/excluded-path.yaml
new file mode 100644
index 00000000..662fc575
--- /dev/null
+++ b/src/test/resources/checks/v32/parameters/OAR060/excluded-path.yaml
@@ -0,0 +1,25 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: Swagger Petstore
+paths:
+  /status:
+    get:
+      parameters:
+        - in: query
+          name: other
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+        - in: query
+          name: $filter
+          required: true
+          schema:
+            type: array
+            items:
+              type: string
+      responses:
+        206:
+          description: Ok
diff --git a/src/test/resources/checks/v32/parameters/OAR060/ref-excluded-path.yaml b/src/test/resources/checks/v32/parameters/OAR060/ref-excluded-path.yaml
new file mode 100644
index 00000000..95085868
--- /dev/null
+++ b/src/test/resources/checks/v32/parameters/OAR060/ref-excluded-path.yaml
@@ -0,0 +1,33 @@
+openapi: "3.2.0"
+info:
+  version: 1.0.0
+  title: OAR060 shared param used only by an excluded path
+paths:
+  /status:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedRefOnly'
+      responses:
+        "200":
+          description: OK
+  /pets:
+    get:
+      parameters:
+        - $ref: '#/components/parameters/SharedByPets'
+      responses:
+        "200":
+          description: OK
+components:
+  parameters:
+    SharedRefOnly:
+      in: query
+      name: q
+      required: true
+      schema:
+        type: string
+    SharedByPets:
+      in: query
+      name: p
+      required: true # Noncompliant {{OAR060: All parameters in query must be defined as optional}}
+      schema:
+        type: string
diff --git a/src/test/resources/checks/v32/parameters/OAR060/required-true.json b/src/test/resources/checks/v32/parameters/OAR060/required-true.json
index cf62bf43..36dc495a 100644
--- a/src/test/resources/checks/v32/parameters/OAR060/required-true.json
+++ b/src/test/resources/checks/v32/parameters/OAR060/required-true.json
@@ -5,7 +5,7 @@
       "title" : "Swagger Petstore"
     },
     "paths" : {
-      "/status" : {
+      "/pets" : {
         "get" : {
           "parameters" : [ {
             "in" : "query",
diff --git a/src/test/resources/checks/v32/parameters/OAR060/required-true.yaml b/src/test/resources/checks/v32/parameters/OAR060/required-true.yaml
index b6fbafbf..3588cdd2 100644
--- a/src/test/resources/checks/v32/parameters/OAR060/required-true.yaml
+++ b/src/test/resources/checks/v32/parameters/OAR060/required-true.yaml
@@ -3,7 +3,7 @@ info:
   version: 1.0.0
   title: Swagger Petstore
 paths:
-  /status:
+  /pets:
     get:
       parameters:
         - in: query