From 8162df7a91226a62ca75701a8ec9e2f7a353456c Mon Sep 17 00:00:00 2001 From: Rafael Goterris Date: Mon, 15 Jun 2026 23:05:55 +0200 Subject: [PATCH 01/10] feat: OAR031 independent per-level examples coverage Validate response, request body, parameter and property examples as four independent levels, each toggleable via @RuleProperty (validateResponse, validateRequestBody, validateParameter, validateProperty; all on by default). The response/request-body/parameter levels now require a media-type or schema-root example (non-recursive); per-property examples no longer satisfy them. Aligns OAR031 with the Spectral ruleset (identical findings). Refs #117 Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 11 +++ .../checks/examples/OAR031ExamplesCheck.java | 77 ++++++++++++++++--- .../rules/openapi/examples/OAR031.html | 2 + .../examples/OAR031ExamplesCheckTest.java | 10 +++ .../v2/examples/OAR031/externalref.yaml | 4 +- .../OAR031/nested-properties-examples.yaml | 2 +- .../checks/v2/examples/OAR031/valid.yaml | 15 +++- .../v3/examples/OAR031/externalref.yaml | 4 +- .../OAR031/nested-properties-examples.yaml | 2 +- .../v31/examples/OAR031/externalref.yaml | 4 +- .../OAR031/nested-properties-examples.yaml | 2 +- .../v32/examples/OAR031/externalref.yaml | 4 +- .../OAR031/nested-properties-examples.yaml | 2 +- 13 files changed, 116 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2908a60c..c28d7cc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ 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). +## [Unreleased] + +### Added + +- OAR031 - ExamplesCheck - Per-level configuration via rule parameters `validate-response`, `validate-request-body`, `validate-parameter` and `validate-property` (all `true` by default); each level can be disabled independently. + +### Changed + +- OAR031 - ExamplesCheck - Examples are now validated as four **independent** levels (response, request body, parameter, property). The response/request-body/parameter levels require an example declared at the media-type or schema **root** (non-recursive); examples nested inside schema properties no longer satisfy them. Aligns OAR031 with the Spectral ruleset (identical findings on the same document) and is stricter than before, so existing specs may surface new findings. + + ## [1.4.1] - 2026-06-04 ### Added diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheck.java index e63b1aed..d174a7f0 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheck.java @@ -15,6 +15,7 @@ import org.apiaddicts.apitools.dosonarapi.api.v32.OpenApi32Grammar; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR031ExamplesCheck.KEY) public class OAR031ExamplesCheck extends BaseCheck { @@ -27,6 +28,39 @@ public class OAR031ExamplesCheck extends BaseCheck { private static final String ITEMS = "items"; private static final String ERROR_RESPONSE = "OAR031.error-response"; + private static final String ERROR_REQUEST = "OAR031.error-request"; + + @RuleProperty( + key = "validateResponse", + description = "Validate that responses declare a body-level example", + defaultValue = "true", + type = "BOOLEAN" + ) + private boolean validateResponse = true; + + @RuleProperty( + key = "validateRequestBody", + description = "Validate that request bodies declare a body-level example", + defaultValue = "true", + type = "BOOLEAN" + ) + private boolean validateRequestBody = true; + + @RuleProperty( + key = "validateParameter", + description = "Validate that parameters declare an example", + defaultValue = "true", + type = "BOOLEAN" + ) + private boolean validateParameter = true; + + @RuleProperty( + key = "validateProperty", + description = "Validate that each schema property declares an example", + defaultValue = "true", + type = "BOOLEAN" + ) + private boolean validateProperty = true; private final ExternalRefHandler handleExternalRef = new ExternalRefHandler(); @@ -67,11 +101,13 @@ private void visitParameterNode(JsonNode node) { JsonNode schema = resolved.get(SCHEMA); + // Parameter level: the parameter itself, or its schema's ROOT, must declare an + // example. Examples buried inside schema properties do NOT satisfy this level. boolean hasExample = !resolved.get(EXAMPLE).isMissing() || !resolved.get(EXAMPLES).isMissing() - || (!schema.isMissing() && isSchemaCovered(schema)); + || schemaHasRootExample(schema); - if (!hasExample) { + if (validateParameter && !hasExample) { addIssue(KEY, translate("OAR031.error-parameter"), handleExternalRef.getTrueNode(node)); } }); @@ -89,10 +125,11 @@ private void visitV2Node(JsonNode node) { private void visitResponseV2Node(JsonNode node) { handleExternalRef.resolve(node, resolved -> { JsonNode schemaNode = resolved.get(SCHEMA); + // Response level: a response-level examples map or the schema ROOT example. boolean hasExample = !resolved.get(EXAMPLES).isMissing() - || (!schemaNode.isMissing() && isSchemaCovered(schemaNode)); + || schemaHasRootExample(schemaNode); - if (!hasExample) { + if (validateResponse && !hasExample) { addIssue(KEY, translate(ERROR_RESPONSE), handleExternalRef.getTrueNode(node.key())); } }); @@ -117,26 +154,42 @@ private void processResponses(JsonNode node, java.util.function.Consumer + !resolved.get(EXAMPLE).isMissing() || !resolved.get(EXAMPLES).isMissing()); + } + + // Recursive coverage, used only by the property-level walk. private boolean isSchemaCovered(JsonNode schemaNode) { if (schemaNode.isMissing()) return false; @@ -160,6 +213,8 @@ private boolean isSchemaCovered(JsonNode schemaNode) { } private void visitSchemaNode(JsonNode node) { + if (!validateProperty) return; + JsonNode parentNode = (JsonNode) node.getParent().getParent(); if (parentNode.getType().equals(OpenApi3Grammar.PARAMETER) || parentNode.getType().equals(OpenApi31Grammar.PARAMETER) || parentNode.getType().equals(OpenApi32Grammar.PARAMETER)) { @@ -185,6 +240,8 @@ private void visitSchemaNode(JsonNode node) { } private void visitPathNode(JsonNode node) { + if (!validateProperty) return; + node.properties().stream() .filter(prop -> isOperation(prop)) .map(JsonNode::value) @@ -223,4 +280,4 @@ private void visitSchemaNode2(JsonNode responseNode) { }); }); } -} \ No newline at end of file +} diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/examples/OAR031.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/examples/OAR031.html index 78956b46..c20f54bc 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/examples/OAR031.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/examples/OAR031.html @@ -1,4 +1,6 @@

The examples can help developers to understand the response data structure and representation.

+

Examples are validated as four independent levels: response, request body, parameter and property. The response, request-body and parameter levels require an example declared at the media-type or schema root; examples nested inside individual properties do not satisfy them. 204 responses are excluded.

+

Each level can be enabled or disabled independently with the rule parameters validate-response, validate-request-body, validate-parameter and validate-property (all enabled by default).

Noncompliant Code Example (OpenAPI 2)

 swagger: "2.0"
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheckTest.java
index 5cea16f8..4f7f624d 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/examples/OAR031ExamplesCheckTest.java
@@ -4,6 +4,7 @@
 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 OAR031ExamplesCheckTest extends BaseCheckTest {
@@ -112,4 +113,13 @@ public void verifyRule() {
         assertRuleProperties("OAR031 - Examples - Responses, Request Body, Parameters and Properties must have an example defined", RuleType.BUG, Severity.MAJOR, tags("examples"));
     }
 
+    @Override
+    public void verifyParameters() {
+        assertNumberOfParameters(4);
+        assertParameterProperties("validateResponse", "true", RuleParamType.BOOLEAN);
+        assertParameterProperties("validateRequestBody", "true", RuleParamType.BOOLEAN);
+        assertParameterProperties("validateParameter", "true", RuleParamType.BOOLEAN);
+        assertParameterProperties("validateProperty", "true", RuleParamType.BOOLEAN);
+    }
+
 }
diff --git a/src/test/resources/checks/v2/examples/OAR031/externalref.yaml b/src/test/resources/checks/v2/examples/OAR031/externalref.yaml
index 8b2b3bc2..18b488c1 100644
--- a/src/test/resources/checks/v2/examples/OAR031/externalref.yaml
+++ b/src/test/resources/checks/v2/examples/OAR031/externalref.yaml
@@ -10,7 +10,7 @@ paths:
   /users:
     get:
       responses:
-        200:
+        200: # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: OK
           schema:
             type: array
@@ -27,7 +27,7 @@ paths:
           required: true
           type: string
       responses:
-        200:
+        200: # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A single user
           schema:
             $ref: '#/definitions/User'
diff --git a/src/test/resources/checks/v2/examples/OAR031/nested-properties-examples.yaml b/src/test/resources/checks/v2/examples/OAR031/nested-properties-examples.yaml
index 13ca46e6..6b0b4816 100644
--- a/src/test/resources/checks/v2/examples/OAR031/nested-properties-examples.yaml
+++ b/src/test/resources/checks/v2/examples/OAR031/nested-properties-examples.yaml
@@ -6,7 +6,7 @@ paths:
   /profile:
     put:
       parameters:
-        - name: body
+        - name: body # Noncompliant {{OAR031: Parameters must have one or more examples defined}}
           in: body
           required: true
           schema:
diff --git a/src/test/resources/checks/v2/examples/OAR031/valid.yaml b/src/test/resources/checks/v2/examples/OAR031/valid.yaml
index abb84e89..299650c1 100644
--- a/src/test/resources/checks/v2/examples/OAR031/valid.yaml
+++ b/src/test/resources/checks/v2/examples/OAR031/valid.yaml
@@ -12,6 +12,12 @@ paths:
           description: Pet list
           schema:
             $ref: '#/definitions/pets'
+          examples:
+            application/json:
+              size: 1
+              pets:
+                - name: Snow
+                  type: dog
         default:
           $ref: "#/responses/server_error_response"
   /pets/{id}:
@@ -23,6 +29,10 @@ paths:
           description: One pet
           schema:
             $ref: "#/definitions/pet"
+          examples:
+            application/json:
+              name: Snow
+              type: dog
         default:
           $ref: "#/responses/server_error_response"
 
@@ -63,4 +73,7 @@ responses:
       properties:
         error:
           type: string
-          example: "Server error"
\ No newline at end of file
+          example: "Server error"
+    examples:
+      application/json:
+        error: "Server error"
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/examples/OAR031/externalref.yaml b/src/test/resources/checks/v3/examples/OAR031/externalref.yaml
index 05e57ddb..e747a397 100644
--- a/src/test/resources/checks/v3/examples/OAR031/externalref.yaml
+++ b/src/test/resources/checks/v3/examples/OAR031/externalref.yaml
@@ -14,7 +14,7 @@ paths:
       summary: Get all users
       description: Returns a list of users.
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A JSON array of user objects
           content:
             application/json:
@@ -40,7 +40,7 @@ paths:
               name: Puppy
               type: dog
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A single user object
           content:
             application/json:
diff --git a/src/test/resources/checks/v3/examples/OAR031/nested-properties-examples.yaml b/src/test/resources/checks/v3/examples/OAR031/nested-properties-examples.yaml
index d80344b0..9a67dda1 100644
--- a/src/test/resources/checks/v3/examples/OAR031/nested-properties-examples.yaml
+++ b/src/test/resources/checks/v3/examples/OAR031/nested-properties-examples.yaml
@@ -6,7 +6,7 @@ paths:
   /profile:
     put:
       summary: Update user profile
-      requestBody:
+      requestBody: # Noncompliant {{OAR031: Request body must have one or more examples defined}}
         required: true
         content:
           application/json:
diff --git a/src/test/resources/checks/v31/examples/OAR031/externalref.yaml b/src/test/resources/checks/v31/examples/OAR031/externalref.yaml
index 21cee27e..4e6e96e7 100644
--- a/src/test/resources/checks/v31/examples/OAR031/externalref.yaml
+++ b/src/test/resources/checks/v31/examples/OAR031/externalref.yaml
@@ -14,7 +14,7 @@ paths:
       summary: Get all users
       description: Returns a list of users.
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A JSON array of user objects
           content:
             application/json:
@@ -40,7 +40,7 @@ paths:
               name: Puppy
               type: dog
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A single user object
           content:
             application/json:
diff --git a/src/test/resources/checks/v31/examples/OAR031/nested-properties-examples.yaml b/src/test/resources/checks/v31/examples/OAR031/nested-properties-examples.yaml
index bf96e975..c48f0f18 100644
--- a/src/test/resources/checks/v31/examples/OAR031/nested-properties-examples.yaml
+++ b/src/test/resources/checks/v31/examples/OAR031/nested-properties-examples.yaml
@@ -6,7 +6,7 @@ paths:
   /profile:
     put:
       summary: Update user profile
-      requestBody:
+      requestBody: # Noncompliant {{OAR031: Request body must have one or more examples defined}}
         required: true
         content:
           application/json:
diff --git a/src/test/resources/checks/v32/examples/OAR031/externalref.yaml b/src/test/resources/checks/v32/examples/OAR031/externalref.yaml
index 126feb8a..2c326a8b 100644
--- a/src/test/resources/checks/v32/examples/OAR031/externalref.yaml
+++ b/src/test/resources/checks/v32/examples/OAR031/externalref.yaml
@@ -14,7 +14,7 @@ paths:
       summary: Get all users
       description: Returns a list of users.
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A JSON array of user objects
           content:
             application/json:
@@ -40,7 +40,7 @@ paths:
               name: Puppy
               type: dog
       responses:
-        '200':
+        '200': # Noncompliant {{OAR031: Responses must have one or more examples defined}}
           description: A single user object
           content:
             application/json:
diff --git a/src/test/resources/checks/v32/examples/OAR031/nested-properties-examples.yaml b/src/test/resources/checks/v32/examples/OAR031/nested-properties-examples.yaml
index 34233555..e93c96a9 100644
--- a/src/test/resources/checks/v32/examples/OAR031/nested-properties-examples.yaml
+++ b/src/test/resources/checks/v32/examples/OAR031/nested-properties-examples.yaml
@@ -6,7 +6,7 @@ paths:
   /profile:
     put:
       summary: Update user profile
-      requestBody:
+      requestBody: # Noncompliant {{OAR031: Request body must have one or more examples defined}}
         required: true
         content:
           application/json:

From 8268153d5dfec951f214e60f1a3ae549f6f7cbf6 Mon Sep 17 00:00:00 2001
From: Rafael Goterris 
Date: Mon, 15 Jun 2026 23:25:09 +0200
Subject: [PATCH 02/10] chore(release): 1.5.0-beta-1

Co-Authored-By: Claude Opus 4.8 (1M context) 
---
 CHANGELOG.md | 2 +-
 pom.xml      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c28d7cc9..ce246b1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ 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).
 
-## [Unreleased]
+## [1.5.0-beta-1] - 2026-06-15
 
 ### Added
 
diff --git a/pom.xml b/pom.xml
index 264be0bf..a08e523f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
   4.0.0
   org.apiaddicts.apitools.dosonarapi
   sonaropenapi-rules-community
-  1.4.1
+  1.5.0-beta-1
   sonar-plugin
 
   SonarQube OpenAPI Community Rules

From ee3ea9207b1119bf2396500326f690398c2aa156 Mon Sep 17 00:00:00 2001
From: Melsy Huamani 
Date: Tue, 23 Jun 2026 20:48:18 -0500
Subject: [PATCH 03/10] fix: exclude delete in oar017 rule, and update tests
 and docs for oar020, oar021, oar022, oar025

---
 CHANGELOG.md                                  | 13 ++++++++++
 .../operations/OAR017ResourcePathCheck.java   |  2 +-
 .../rules/openapi/operations/OAR017.html      |  2 +-
 .../rules/openapi/parameters/OAR022.html      |  1 +
 .../rules/openapi/parameters/OAR025.html      |  1 +
 .../rules/openapi/resources/OAR017.html       |  2 +-
 .../rules/openapi/operations/OAR017.html      |  2 +-
 .../rules/openapi/parameters/OAR022.html      |  1 +
 .../rules/openapi/parameters/OAR025.html      |  1 +
 .../rules/openapi/resources/OAR017.html       |  2 +-
 .../OAR017ResourcePathCheckTest.java          |  2 +-
 .../OAR020ExpandParameterCheckTest.java       |  2 +-
 .../OAR021ExcludeParameterCheckTest.java      |  2 +-
 .../OAR022OrderbyParameterCheckTest.java      | 17 +++++++++++++
 .../OAR025LimitParameterCheckTest.java        | 17 +++++++++++++
 .../checks/v2/operations/OAR017/plain.yaml    |  5 ++++
 .../v2/parameters/OAR022/single-resource.json | 23 +++++++++++++++++
 .../v2/parameters/OAR022/single-resource.yaml | 14 +++++++++++
 .../v2/parameters/OAR025/single-resource.json | 23 +++++++++++++++++
 .../v2/parameters/OAR025/single-resource.yaml | 14 +++++++++++
 .../checks/v2/resources/OAR017/plain.yaml     |  6 +++++
 .../checks/v3/operations/OAR017/plain.yaml    |  5 ++++
 .../v3/parameters/OAR022/single-resource.json | 25 +++++++++++++++++++
 .../v3/parameters/OAR022/single-resource.yaml | 15 +++++++++++
 .../v3/parameters/OAR025/single-resource.json | 25 +++++++++++++++++++
 .../v3/parameters/OAR025/single-resource.yaml | 15 +++++++++++
 .../checks/v3/resources/OAR017/plain.yaml     |  5 ++++
 .../checks/v31/operations/OAR017/plain.yaml   |  5 ++++
 .../parameters/OAR022/single-resource.json    | 25 +++++++++++++++++++
 .../parameters/OAR022/single-resource.yaml    | 15 +++++++++++
 .../parameters/OAR025/single-resource.json    | 25 +++++++++++++++++++
 .../parameters/OAR025/single-resource.yaml    | 15 +++++++++++
 .../checks/v31/resources/OAR017/plain.yaml    |  6 +++++
 .../checks/v32/operations/OAR017/plain.yaml   |  5 ++++
 .../parameters/OAR022/single-resource.json    | 25 +++++++++++++++++++
 .../parameters/OAR022/single-resource.yaml    | 15 +++++++++++
 .../parameters/OAR025/single-resource.json    | 25 +++++++++++++++++++
 .../parameters/OAR025/single-resource.yaml    | 15 +++++++++++
 .../checks/v32/resources/OAR017/plain.yaml    |  6 +++++
 39 files changed, 416 insertions(+), 8 deletions(-)
 create mode 100644 src/test/resources/checks/v2/parameters/OAR022/single-resource.json
 create mode 100644 src/test/resources/checks/v2/parameters/OAR022/single-resource.yaml
 create mode 100644 src/test/resources/checks/v2/parameters/OAR025/single-resource.json
 create mode 100644 src/test/resources/checks/v2/parameters/OAR025/single-resource.yaml
 create mode 100644 src/test/resources/checks/v3/parameters/OAR022/single-resource.json
 create mode 100644 src/test/resources/checks/v3/parameters/OAR022/single-resource.yaml
 create mode 100644 src/test/resources/checks/v3/parameters/OAR025/single-resource.json
 create mode 100644 src/test/resources/checks/v3/parameters/OAR025/single-resource.yaml
 create mode 100644 src/test/resources/checks/v31/parameters/OAR022/single-resource.json
 create mode 100644 src/test/resources/checks/v31/parameters/OAR022/single-resource.yaml
 create mode 100644 src/test/resources/checks/v31/parameters/OAR025/single-resource.json
 create mode 100644 src/test/resources/checks/v31/parameters/OAR025/single-resource.yaml
 create mode 100644 src/test/resources/checks/v32/parameters/OAR022/single-resource.json
 create mode 100644 src/test/resources/checks/v32/parameters/OAR022/single-resource.yaml
 create mode 100644 src/test/resources/checks/v32/parameters/OAR025/single-resource.json
 create mode 100644 src/test/resources/checks/v32/parameters/OAR025/single-resource.yaml

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce246b1f..19de2249 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,19 @@ 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.5.0-beta-2] - 2026-06-23
+
+### Fixed
+
+- OAR017 - ResourcePathCheck - Added `delete` to the `exclude_patterns` default (now `get,me,search,delete`); paths ending with `/delete` (e.g. `/orders/delete`, `/orders/{orderId}/delete`) are now treated as pseudo-parameters and no longer trigger the alternation rule.
+- OAR020 - ExpandParameterCheck - Fixed `verifyInV2PathEndingWithParam` test method that was incorrectly calling `verifyV3("with-param")` instead of `verifyV2("with-param")`; the Swagger 2.0 `with-param` test fixtures are now correctly exercised in v2 mode.
+- OAR021 - ExcludeParameterCheck - Fixed `verifyInV2PathEndingWithParam` test, now correctly calls `verifyV2("with-param")`.
+
+### Added
+
+- OAR022 - OrderbyParameterCheck - Added `single-resource` test cases (v2, v3, v31, v32) verifying that paths ending with a path parameter (e.g. `/examples/{id}`) are correctly excluded by `applyToParameterizedPaths = false`.
+- OAR025 - LimitParameterCheck - Added `single-resource` test cases (v2, v3, v31, v32) verifying that paths ending with a path parameter (e.g. `/examples/{id}`) are correctly excluded by `applyToParameterizedPaths = false`.
+
 ## [1.5.0-beta-1] - 2026-06-15
 
 ### Added
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheck.java
index 41796f8e..28518cda 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheck.java
@@ -20,7 +20,7 @@ public class OAR017ResourcePathCheck extends BaseCheck {
 
 	public static final String KEY = "OAR017";
 	private static final String MESSAGE = "OAR017.error";
-	public static final String EXCLUDE_PATTERNS = "get,me,search";
+	public static final String EXCLUDE_PATTERNS = "get,me,search,delete";
 
 	@RuleProperty(
 			key = "exclude_patterns",
diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/operations/OAR017.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/operations/OAR017.html
index 668d7d4e..aef0567a 100644
--- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/operations/OAR017.html
+++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/operations/OAR017.html
@@ -4,7 +4,7 @@
   
  • Aparecen dos segmentos consecutivos del mismo tipo: dos segmentos estáticos (p.ej. /a/b) o dos parámetros de ruta (p.ej. /{a}/{b}).
  • El path empieza por un parámetro de ruta (p.ej. /{id}/items).
  • -

    Configurable: exclude_patterns (por defecto: get,me,search) — segmentos tratados como pseudo-parámetros que no rompen la regla de alternación.

    +

    Configurable: exclude_patterns (por defecto: get,me,search,delete) — segmentos tratados como pseudo-parámetros que no rompen la regla de alternación.

    Ejemplo de código no compatible (OpenAPI 2)

     swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR022.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR022.html
    index 93c17629..f0b86367 100644
    --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR022.html
    +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR022.html
    @@ -1,4 +1,5 @@
     

    El parámetro elegido debe definirse en esta operación. Por defecto, $orderby

    +

    Esta regla se aplica únicamente a endpoints GET de colección. Los paths que terminan con un parámetro de ruta (p. ej. /examples/{id}) se consideran endpoints de recurso único y quedan excluidos automáticamente.

    Ejemplo de código no compatible (OpenAPI 2)

       swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR025.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR025.html
    index 235d4f29..84e5d1bc 100644
    --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR025.html
    +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/parameters/OAR025.html
    @@ -1,4 +1,5 @@
     

    El parámetro elegido debe definirse en esta operación. Por defecto, $limit

    +

    Esta regla se aplica únicamente a endpoints GET de colección. Los paths que terminan con un parámetro de ruta (p. ej. /examples/{id}) se consideran endpoints de recurso único y quedan excluidos automáticamente.

    Ejemplo de código no compatible (OpenAPI 2)

       swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/resources/OAR017.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/resources/OAR017.html
    index b8177471..b3884acb 100644
    --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/resources/OAR017.html
    +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/resources/OAR017.html
    @@ -4,7 +4,7 @@
       
  • Aparecen dos segmentos consecutivos del mismo tipo: dos segmentos estáticos (p.ej. /a/b) o dos parámetros de ruta (p.ej. /{a}/{b}).
  • El path empieza por un parámetro de ruta (p.ej. /{id}/items).
  • -

    Configurable: exclude_patterns (por defecto: get,me,search) — segmentos tratados como pseudo-parámetros que no rompen la regla de alternación.

    +

    Configurable: exclude_patterns (por defecto: get,me,search,delete) — segmentos tratados como pseudo-parámetros que no rompen la regla de alternación.

    Ejemplo de código no compatible (OpenAPI 2)

    JSON

    diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/operations/OAR017.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/operations/OAR017.html
    index 22bce347..f6fe52d2 100644
    --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/operations/OAR017.html
    +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/operations/OAR017.html
    @@ -4,7 +4,7 @@
     
  • Two consecutive path segments of the same type appear: two static segments (e.g. /a/b) or two path parameters (e.g. /{a}/{b}).
  • The path starts with a path parameter (e.g. /{id}/items).
  • -

    Configurable: exclude_patterns (default: get,me,search) — segments treated as pseudo-parameters that do not break the alternation rule.

    +

    Configurable: exclude_patterns (default: get,me,search,delete) — segments treated as pseudo-parameters that do not break the alternation rule.

    Noncompliant Code Example (OpenAPI 2)

     swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR022.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR022.html
    index 5917921e..2d816f82 100644
    --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR022.html
    +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR022.html
    @@ -1,4 +1,5 @@
     

    The chosen parameter must be defined in this operation. By default, $orderby

    +

    This rule applies only to collection GET endpoints. Paths ending with a path parameter (e.g. /examples/{id}) are treated as single-resource endpoints and are automatically excluded.

    Noncompliant Code Example (OpenAPI 2)

       swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR025.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR025.html
    index d896113f..40d0873f 100644
    --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR025.html
    +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/parameters/OAR025.html
    @@ -1,4 +1,5 @@
     

    The chosen parameter must be defined in this operation. By default, $limit

    +

    This rule applies only to collection GET endpoints. Paths ending with a path parameter (e.g. /examples/{id}) are treated as single-resource endpoints and are automatically excluded.

    Noncompliant Code Example (OpenAPI 2)

       swagger: "2.0"
    diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/resources/OAR017.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/resources/OAR017.html
    index b33a7d6b..378a93fe 100644
    --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/resources/OAR017.html
    +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/resources/OAR017.html
    @@ -4,7 +4,7 @@
     
  • Two consecutive path segments of the same type appear: two static segments (e.g. /a/b) or two path parameters (e.g. /{a}/{b}).
  • The path starts with a path parameter (e.g. /{id}/items).
  • -

    Configurable: exclude_patterns (default: get,me,search) — segments treated as pseudo-parameters that do not break the alternation rule.

    +

    Configurable: exclude_patterns (default: get,me,search,delete) — segments treated as pseudo-parameters that do not break the alternation rule.

    Noncompliant Code Example (OpenAPI 2)

    JSON

    diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheckTest.java
    index 88614748..54d9b77c 100644
    --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheckTest.java
    +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR017ResourcePathCheckTest.java
    @@ -69,7 +69,7 @@ public void verifyRule() {
         @Override
         public void verifyParameters() {
             assertNumberOfParameters(1);
    -        assertParameterProperties("exclude_patterns", "get,me,search", RuleParamType.STRING);
    +        assertParameterProperties("exclude_patterns", "get,me,search,delete", RuleParamType.STRING);
         }
     
     }
    \ No newline at end of file
    diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
    index ceea3c8f..ab2f92a4 100644
    --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
    +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
    @@ -41,7 +41,7 @@ public void verifyInV2WithRef() {
     
         @Test
         public void verifyInV2PathEndingWithParam() {
    -        verifyV3("with-param");
    +        verifyV2("with-param");
         }
     
         @Test
    diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
    index 6cce39f9..b758f663 100644
    --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
    +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
    @@ -41,7 +41,7 @@ public void verifyInV2WithRef() {
     
         @Test
         public void verifyInV2PathEndingWithParam() {
    -        verifyV3("with-param");
    +        verifyV2("with-param");
         }
     
         @Test
    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 e7fccdb3..89c741be 100644
    --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
    +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
    @@ -72,6 +72,23 @@ public void verifyInV32Without() {
             verifyV32("plain-without");
         }
     
    +    @Test
    +    public void verifyInV2SingleResource() {
    +        verifyV2("single-resource");
    +    }
    +    @Test
    +    public void verifyInV3SingleResource() {
    +        verifyV3("single-resource");
    +    }
    +    @Test
    +    public void verifyInV31SingleResource() {
    +        verifyV31("single-resource");
    +    }
    +    @Test
    +    public void verifyInV32SingleResource() {
    +        verifyV32("single-resource");
    +    }
    +
         @Override
         public void verifyRule() {
             assertRuleProperties("OAR022 - OrderbyParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MINOR, tags("parameters"));
    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 e6562b9d..463dfe8c 100644
    --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
    +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
    @@ -72,6 +72,23 @@ public void verifyInV32Without() {
             verifyV32("plain-without");
         }
     
    +    @Test
    +    public void verifyInV2SingleResource() {
    +        verifyV2("single-resource");
    +    }
    +    @Test
    +    public void verifyInV3SingleResource() {
    +        verifyV3("single-resource");
    +    }
    +    @Test
    +    public void verifyInV31SingleResource() {
    +        verifyV31("single-resource");
    +    }
    +    @Test
    +    public void verifyInV32SingleResource() {
    +        verifyV32("single-resource");
    +    }
    +
         @Override
         public void verifyRule() {
             assertRuleProperties("OAR025 - LimitParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MAJOR, tags("parameters"));
    diff --git a/src/test/resources/checks/v2/operations/OAR017/plain.yaml b/src/test/resources/checks/v2/operations/OAR017/plain.yaml
    index 81d61480..249e07ea 100644
    --- a/src/test/resources/checks/v2/operations/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v2/operations/OAR017/plain.yaml
    @@ -43,6 +43,11 @@ paths:
           responses:
             200:
               description: "Ok"
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: "Ok"
       /one/{two}/{three}: # Noncompliant {{OAR017: Resource path should alternate static and parametrized parts}}
         get:
           responses:
    diff --git a/src/test/resources/checks/v2/parameters/OAR022/single-resource.json b/src/test/resources/checks/v2/parameters/OAR022/single-resource.json
    new file mode 100644
    index 00000000..3357f145
    --- /dev/null
    +++ b/src/test/resources/checks/v2/parameters/OAR022/single-resource.json
    @@ -0,0 +1,23 @@
    +{
    +  "swagger" : "2.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "type" : "string"
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v2/parameters/OAR022/single-resource.yaml b/src/test/resources/checks/v2/parameters/OAR022/single-resource.yaml
    new file mode 100644
    index 00000000..ae452772
    --- /dev/null
    +++ b/src/test/resources/checks/v2/parameters/OAR022/single-resource.yaml
    @@ -0,0 +1,14 @@
    +swagger: "2.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v2/parameters/OAR025/single-resource.json b/src/test/resources/checks/v2/parameters/OAR025/single-resource.json
    new file mode 100644
    index 00000000..3357f145
    --- /dev/null
    +++ b/src/test/resources/checks/v2/parameters/OAR025/single-resource.json
    @@ -0,0 +1,23 @@
    +{
    +  "swagger" : "2.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "type" : "string"
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v2/parameters/OAR025/single-resource.yaml b/src/test/resources/checks/v2/parameters/OAR025/single-resource.yaml
    new file mode 100644
    index 00000000..ae452772
    --- /dev/null
    +++ b/src/test/resources/checks/v2/parameters/OAR025/single-resource.yaml
    @@ -0,0 +1,14 @@
    +swagger: "2.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v2/resources/OAR017/plain.yaml b/src/test/resources/checks/v2/resources/OAR017/plain.yaml
    index 2a16af5a..08562325 100644
    --- a/src/test/resources/checks/v2/resources/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v2/resources/OAR017/plain.yaml
    @@ -61,3 +61,9 @@ paths:
           responses:
             200:
               description: Ok
    +
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v3/operations/OAR017/plain.yaml b/src/test/resources/checks/v3/operations/OAR017/plain.yaml
    index d902e89e..298bd9f9 100644
    --- a/src/test/resources/checks/v3/operations/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v3/operations/OAR017/plain.yaml
    @@ -43,3 +43,8 @@ paths:
           responses:
             200:
               description: Ok
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v3/parameters/OAR022/single-resource.json b/src/test/resources/checks/v3/parameters/OAR022/single-resource.json
    new file mode 100644
    index 00000000..54255ba9
    --- /dev/null
    +++ b/src/test/resources/checks/v3/parameters/OAR022/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.0.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v3/parameters/OAR022/single-resource.yaml b/src/test/resources/checks/v3/parameters/OAR022/single-resource.yaml
    new file mode 100644
    index 00000000..e76282c7
    --- /dev/null
    +++ b/src/test/resources/checks/v3/parameters/OAR022/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.0.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v3/parameters/OAR025/single-resource.json b/src/test/resources/checks/v3/parameters/OAR025/single-resource.json
    new file mode 100644
    index 00000000..54255ba9
    --- /dev/null
    +++ b/src/test/resources/checks/v3/parameters/OAR025/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.0.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v3/parameters/OAR025/single-resource.yaml b/src/test/resources/checks/v3/parameters/OAR025/single-resource.yaml
    new file mode 100644
    index 00000000..e76282c7
    --- /dev/null
    +++ b/src/test/resources/checks/v3/parameters/OAR025/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.0.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v3/resources/OAR017/plain.yaml b/src/test/resources/checks/v3/resources/OAR017/plain.yaml
    index f872fa73..189c4d50 100644
    --- a/src/test/resources/checks/v3/resources/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v3/resources/OAR017/plain.yaml
    @@ -56,3 +56,8 @@ paths:
           responses:
             200:
               description: Ok
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v31/operations/OAR017/plain.yaml b/src/test/resources/checks/v31/operations/OAR017/plain.yaml
    index 72bb577e..9ad95cf0 100644
    --- a/src/test/resources/checks/v31/operations/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v31/operations/OAR017/plain.yaml
    @@ -43,3 +43,8 @@ paths:
           responses:
             200:
               description: Ok
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v31/parameters/OAR022/single-resource.json b/src/test/resources/checks/v31/parameters/OAR022/single-resource.json
    new file mode 100644
    index 00000000..68f78709
    --- /dev/null
    +++ b/src/test/resources/checks/v31/parameters/OAR022/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.1.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v31/parameters/OAR022/single-resource.yaml b/src/test/resources/checks/v31/parameters/OAR022/single-resource.yaml
    new file mode 100644
    index 00000000..6e692751
    --- /dev/null
    +++ b/src/test/resources/checks/v31/parameters/OAR022/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.1.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v31/parameters/OAR025/single-resource.json b/src/test/resources/checks/v31/parameters/OAR025/single-resource.json
    new file mode 100644
    index 00000000..68f78709
    --- /dev/null
    +++ b/src/test/resources/checks/v31/parameters/OAR025/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.1.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v31/parameters/OAR025/single-resource.yaml b/src/test/resources/checks/v31/parameters/OAR025/single-resource.yaml
    new file mode 100644
    index 00000000..6e692751
    --- /dev/null
    +++ b/src/test/resources/checks/v31/parameters/OAR025/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.1.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v31/resources/OAR017/plain.yaml b/src/test/resources/checks/v31/resources/OAR017/plain.yaml
    index 87dee561..674c3d0c 100644
    --- a/src/test/resources/checks/v31/resources/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v31/resources/OAR017/plain.yaml
    @@ -56,3 +56,9 @@ paths:
           responses:
             200:
               description: Ok
    +
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v32/operations/OAR017/plain.yaml b/src/test/resources/checks/v32/operations/OAR017/plain.yaml
    index a623de5e..8c115f7b 100644
    --- a/src/test/resources/checks/v32/operations/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v32/operations/OAR017/plain.yaml
    @@ -43,3 +43,8 @@ paths:
           responses:
             200:
               description: Ok
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    diff --git a/src/test/resources/checks/v32/parameters/OAR022/single-resource.json b/src/test/resources/checks/v32/parameters/OAR022/single-resource.json
    new file mode 100644
    index 00000000..d08f568f
    --- /dev/null
    +++ b/src/test/resources/checks/v32/parameters/OAR022/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.2.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v32/parameters/OAR022/single-resource.yaml b/src/test/resources/checks/v32/parameters/OAR022/single-resource.yaml
    new file mode 100644
    index 00000000..b2a320f5
    --- /dev/null
    +++ b/src/test/resources/checks/v32/parameters/OAR022/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.2.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v32/parameters/OAR025/single-resource.json b/src/test/resources/checks/v32/parameters/OAR025/single-resource.json
    new file mode 100644
    index 00000000..d08f568f
    --- /dev/null
    +++ b/src/test/resources/checks/v32/parameters/OAR025/single-resource.json
    @@ -0,0 +1,25 @@
    +{
    +  "openapi" : "3.2.0",
    +  "info" : {
    +    "version" : "1.0.0",
    +    "title" : "Swagger Petstore"
    +  },
    +  "paths" : {
    +    "/examples/{id}" : {
    +      "get" : {
    +        "parameters" : [ {
    +          "in" : "query",
    +          "name" : "other",
    +          "schema" : {
    +            "type" : "string"
    +          }
    +        } ],
    +        "responses" : {
    +          "206" : {
    +            "description" : "Ok"
    +          }
    +        }
    +      }
    +    }
    +  }
    +}
    diff --git a/src/test/resources/checks/v32/parameters/OAR025/single-resource.yaml b/src/test/resources/checks/v32/parameters/OAR025/single-resource.yaml
    new file mode 100644
    index 00000000..b2a320f5
    --- /dev/null
    +++ b/src/test/resources/checks/v32/parameters/OAR025/single-resource.yaml
    @@ -0,0 +1,15 @@
    +openapi: "3.2.0"
    +info:
    +  version: 1.0.0
    +  title: Swagger Petstore
    +paths:
    +  /examples/{id}:
    +    get:
    +      parameters:
    +        - in: query
    +          name: other
    +          schema:
    +            type: string
    +      responses:
    +        206:
    +          description: Ok
    diff --git a/src/test/resources/checks/v32/resources/OAR017/plain.yaml b/src/test/resources/checks/v32/resources/OAR017/plain.yaml
    index d3e714b9..baa25013 100644
    --- a/src/test/resources/checks/v32/resources/OAR017/plain.yaml
    +++ b/src/test/resources/checks/v32/resources/OAR017/plain.yaml
    @@ -56,3 +56,9 @@ paths:
           responses:
             200:
               description: Ok
    +
    +  /one/delete:
    +    post:
    +      responses:
    +        200:
    +          description: Ok
    
    From d33c36981e365d78df51b9839d1de8c5368fb8a9 Mon Sep 17 00:00:00 2001
    From: Melsy Huamani 
    Date: Wed, 24 Jun 2026 11:55:04 -0500
    Subject: [PATCH 04/10] feat: oar037 dont fire with absent format, and add
     media types for oar044 rule
    
    ---
     CHANGELOG.md                                  |   7 +-
     pom.xml                                       |   2 +-
     .../format/OAR037StringFormatCheck.java       |   2 +-
     .../checks/format/OAR044MediaTypeCheck.java   |   2 +-
     .../es/openapi/rules/openapi/core/OAR044.html | 110 +++++++++++-------
     .../openapi/rules/openapi/format/OAR037.html  |  14 +--
     .../openapi/rules/openapi/format/OAR044.html  |  18 ++-
     .../openapi/rules/openapi/core/OAR044.html    | 110 +++++++++++-------
     .../openapi/rules/openapi/format/OAR037.html  |  14 +--
     .../openapi/rules/openapi/format/OAR044.html  |  18 ++-
     .../format/OAR037StringFormatCheckTest.java   |   5 +
     .../checks/v2/core/OAR044/media-type.json     |   2 +
     .../checks/v2/core/OAR044/media-type.yaml     |  12 +-
     .../checks/v2/format/OAR037/plain.json        |   2 +-
     .../checks/v2/format/OAR037/plain.yaml        |   2 +-
     .../checks/v2/format/OAR044/media-type.json   |   2 +
     .../checks/v2/format/OAR044/media-type.yaml   |  12 +-
     .../checks/v3/core/OAR044/media-type.json     |  20 +++-
     .../checks/v3/core/OAR044/media-type.yaml     |  19 ++-
     .../checks/v3/format/OAR037/complete.json     |   2 +-
     .../checks/v3/format/OAR037/complete.yaml     |   2 +-
     .../checks/v3/format/OAR037/no-format.json    |  36 ++++++
     .../checks/v3/format/OAR037/no-format.yaml    |  21 ++++
     .../checks/v3/format/OAR044/media-type.json   |  18 ++-
     .../checks/v3/format/OAR044/media-type.yaml   |  17 ++-
     .../checks/v31/core/OAR044/media-type.json    |  20 +++-
     .../checks/v31/core/OAR044/media-type.yaml    |  19 ++-
     .../checks/v31/format/OAR037/complete.json    |   2 +-
     .../checks/v31/format/OAR037/complete.yaml    |   2 +-
     .../checks/v31/format/OAR044/media-type.json  |  18 ++-
     .../checks/v31/format/OAR044/media-type.yaml  |  17 ++-
     .../checks/v32/core/OAR044/media-type.json    |  20 +++-
     .../checks/v32/core/OAR044/media-type.yaml    |  19 ++-
     .../checks/v32/format/OAR037/complete.json    |   2 +-
     .../checks/v32/format/OAR037/complete.yaml    |   2 +-
     .../checks/v32/format/OAR044/media-type.json  |  18 ++-
     .../checks/v32/format/OAR044/media-type.yaml  |  17 ++-
     37 files changed, 494 insertions(+), 131 deletions(-)
     create mode 100644 src/test/resources/checks/v3/format/OAR037/no-format.json
     create mode 100644 src/test/resources/checks/v3/format/OAR037/no-format.yaml
    
    diff --git a/CHANGELOG.md b/CHANGELOG.md
    index 19de2249..1cd94274 100644
    --- a/CHANGELOG.md
    +++ b/CHANGELOG.md
    @@ -5,13 +5,18 @@ 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.5.0-beta-2] - 2026-06-23
    +## [1.5.0-beta-2] - 2026-06-24
    +
    +### Changed
    +
    +- OAR037 - StringFormatCheck - Rule no longer fires when a string schema omits the `format` field entirely; it only fires when `format` is present but not a recognized value.
     
     ### Fixed
     
     - OAR017 - ResourcePathCheck - Added `delete` to the `exclude_patterns` default (now `get,me,search,delete`); paths ending with `/delete` (e.g. `/orders/delete`, `/orders/{orderId}/delete`) are now treated as pseudo-parameters and no longer trigger the alternation rule.
     - OAR020 - ExpandParameterCheck - Fixed `verifyInV2PathEndingWithParam` test method that was incorrectly calling `verifyV3("with-param")` instead of `verifyV2("with-param")`; the Swagger 2.0 `with-param` test fixtures are now correctly exercised in v2 mode.
     - OAR021 - ExcludeParameterCheck - Fixed `verifyInV2PathEndingWithParam` test, now correctly calls `verifyV2("with-param")`.
    +- OAR044 - MediaTypeCheck - Fixed `MEDIA_RANGE_PATTERN` to allow `*/*` (full wildcard) as a valid OAP3 media range; the type component now accepts `*` in addition to RFC 6838 type names. Added test coverage for vendor-specific types (`application/vnd.ms-excel`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`, `application/ld+json`, `application/vnd.github+json`).
     
     ### Added
     
    diff --git a/pom.xml b/pom.xml
    index a08e523f..adb460dd 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -3,7 +3,7 @@
       4.0.0
       org.apiaddicts.apitools.dosonarapi
       sonaropenapi-rules-community
    -  1.5.0-beta-1
    +  1.5.0-beta-2
       sonar-plugin
     
       SonarQube OpenAPI Community Rules
    diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java
    index 72ebeaeb..4756044d 100644
    --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java
    +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java
    @@ -33,6 +33,6 @@ public void validate(String type, String format, JsonNode typeNode) {
         }
     
         private boolean isInvalidString(String type, String format, Set validFormats) {
    -        return "string".equals(type) && (format == null || !validFormats.contains(format.toLowerCase()));
    +        return "string".equals(type) && format != null && !validFormats.contains(format.toLowerCase());
         }
     }
    \ No newline at end of file
    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 aa7992ef..15d3a523 100644
    --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java
    +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR044MediaTypeCheck.java
    @@ -52,7 +52,7 @@ public class OAR044MediaTypeCheck extends BaseCheck {
       );
       @VisibleForTesting
       static final Pattern MEDIA_RANGE_PATTERN = Pattern.compile(
    -      "[a-zA-Z0-9.][a-zA-Z0-9.!#$&_^+\\-]+/" +
    +      "(\\*|[a-zA-Z0-9.][a-zA-Z0-9.!#$&_^+\\-]+)/" +
           "(\\*|" +
           "[a-zA-Z0-9.][a-zA-Z0-9.!#$&_^+\\-]+" +
           "(; charset=[a-zA-Z0-9_\\-]+)?" +
    diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/core/OAR044.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/core/OAR044.html
    index 77bc10e6..221ea1bd 100644
    --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/core/OAR044.html
    +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/core/OAR044.html
    @@ -65,31 +65,35 @@ 

    Solución compatible (OpenAPI 2)

    JSON

     {
    -    "swagger": "2.0", 
    +    "swagger": "2.0",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "produces": [
    -        "application/json", 
    -        "text/plain"
    -    ], 
    +        "application/json",
    +        "text/plain",
    +        "application/vnd.ms-excel",
    +        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    +        "application/ld+json"
    +    ],
         "consumes": [
    -        "application/json", 
    -        "text/plain"
    -    ], 
    +        "application/json",
    +        "text/plain",
    +        "application/vnd.ms-excel"
    +    ],
         "paths": {
             "/pets": {
                 "get": {
                     "produces": [
    -                    "application/json", 
    +                    "application/json",
                         "text/plain"
    -                ], 
    +                ],
                     "consumes": [
    -                    "application/json", 
    +                    "application/json",
                         "text/plain"
    -                ], 
    -                "responses": { 
    +                ],
    +                "responses": {
                         "200": {
                             "description": "some operation"
                         }
    @@ -108,9 +112,13 @@ 

    YAML

    produces: - application/json - text/plain + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json consumes: - application/json - text/plain + - application/vnd.ms-excel paths: /pets: get: @@ -128,24 +136,28 @@

    Ejemplo de código no compatible (OpenAPI 3)

    JSON

     {
    -    "openapi": "3.0.1", 
    +    "openapi": "3.0.1",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "paths": {
    -        "/pets": null, 
    -        "post": {
    -            "requestBody": null, 
    -            "content": {
    -                "application": {}, 
    -                "text/*": {}
    -            }, 
    -            "responses": null, 
    -            "200": {
    -                "description": "some operation", 
    -                "content": null, 
    -                "application": {}
    +        "/pets": {
    +            "post": {
    +                "requestBody": {
    +                    "content": {
    +                        "application": {},
    +                        "invalid-no-slash": {}
    +                    }
    +                },
    +                "responses": {
    +                    "200": {
    +                        "description": "some operation",
    +                        "content": {
    +                            "application": {}
    +                        }
    +                    }
    +                }
                 }
             }
         }
    @@ -163,7 +175,7 @@ 

    YAML

    requestBody: content: 'application': {} - 'text/*': {} + 'invalid-no-slash': {} responses: '200': description: some operation @@ -174,25 +186,35 @@

    Solución compatible (OpenAPI 3)

    JSON

     {
    -    "openapi": "3.0.1", 
    +    "openapi": "3.0.1",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "paths": {
             "/pets": {
                 "post": {
    -                "requestBody": { 
    +                "requestBody": {
                         "content": {
    -                        "application/json": {}, 
    -                        "text/plain": {}
    +                        "application/json": {},
    +                        "multipart/form-data": {},
    +                        "application/vnd.ms-excel": {},
    +                        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {},
    +                        "application/ld+json": {},
    +                        "image/*": {},
    +                        "*/*": {}
                         }
                     },
                     "responses": {
                         "200": {
    -                        "description": "some operation", 
    +                        "description": "some operation",
                             "content": {
    -                            "application/json": {}
    +                            "application/json": {},
    +                            "text/csv": {},
    +                            "image/png": {},
    +                            "text/plain; charset=utf-8": {},
    +                            "application/vnd.ms-excel": {},
    +                            "application/vnd.github+json": {}
                             }
                         }
                     }
    @@ -213,10 +235,20 @@ 

    YAML

    requestBody: content: 'application/json': {} - 'text/plain': {} + 'multipart/form-data': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: '200': description: some operation content: 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'text/plain; charset=utf-8': {} + 'application/vnd.ms-excel': {} + 'application/vnd.github+json': {}
    \ No newline at end of file diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html index 7f3e14a0..873828be 100644 --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html @@ -1,5 +1,5 @@ -

    Una propiedad de tipo string sin formato, o con un formato no reconocido, puede provocar una implementación incorrecta de la API.

    -

    Esta regla dispara cuando el campo format está ausente o presente pero no es reconocido. Todo schema de tipo string debe declarar uno de los formatos válidos.

    +

    Una propiedad de tipo string con un valor de format no reconocido puede provocar una implementación incorrecta de la API.

    +

    Esta regla dispara únicamente cuando el campo format está presente pero no es reconocido. Los schemas de tipo string sin campo format son aceptados.

    Formatos válidos: date, date-time, password, byte, binary, email, uuid, uri, hostname, ipv4, ipv6, HEX, HEX(16), json, xml, base64.

    Configurable: formats-allowed — lista de formatos permitidos separados por coma (por defecto: la lista anterior).

    Ejemplo de código no compatible (OpenAPI 2)

    @@ -19,8 +19,6 @@

    Ejemplo de código no compatible (OpenAPI 2)

    items: type: object properties: - name: - type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format ausente date: type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format inválido format: 'dd/mm/yyyy' @@ -42,6 +40,8 @@

    Solución compatible (OpenAPI 2)

    items: type: object properties: + name: + type: string date: type: string format: date @@ -65,8 +65,6 @@

    Ejemplo de código no compatible (OpenAPI 3)

    items: type: object properties: - name: - type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format ausente date: type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format inválido format: dd/mm/yyyy @@ -90,7 +88,9 @@

    Solución compatible (OpenAPI 3)

    items: type: object properties: + name: + type: string date: type: string format: date -
    \ No newline at end of file +
    diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR044.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR044.html index 60c71fb4..7965b70b 100644 --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR044.html +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR044.html @@ -34,9 +34,13 @@

    Solución compatible (OpenAPI 2)

    produces: - application/json - text/plain + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json consumes: - application/json - text/plain + - application/vnd.ms-excel paths: /pets: get: @@ -62,7 +66,7 @@

    Ejemplo de código no compatible (OpenAPI 3)

    requestBody: content: 'application': {} - 'text/*': {} + 'invalid-no-slash': {} responses: '200': description: some operation @@ -81,10 +85,20 @@

    Solución compatible (OpenAPI 3)

    requestBody: content: 'application/json': {} - 'text/plain': {} + 'multipart/form-data': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: '200': description: some operation content: 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'text/plain; charset=utf-8': {} + 'application/vnd.ms-excel': {} + 'application/vnd.github+json': {}
    \ No newline at end of file diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/core/OAR044.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/core/OAR044.html index 07ab977f..202b2e9a 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/core/OAR044.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/core/OAR044.html @@ -65,30 +65,34 @@

    Compliant Solution (OpenAPI 2)

    JSON

     {
    -    "swagger": "2.0", 
    +    "swagger": "2.0",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "produces": [
    -        "application/json", 
    -        "text/plain"
    -    ], 
    +        "application/json",
    +        "text/plain",
    +        "application/vnd.ms-excel",
    +        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    +        "application/ld+json"
    +    ],
         "consumes": [
    -        "application/json", 
    -        "text/plain"
    -    ], 
    +        "application/json",
    +        "text/plain",
    +        "application/vnd.ms-excel"
    +    ],
         "paths": {
             "/pets": {
                 "get": {
                     "produces": [
    -                    "application/json", 
    +                    "application/json",
                         "text/plain"
    -                ], 
    +                ],
                     "consumes": [
    -                    "application/json", 
    +                    "application/json",
                         "text/plain"
    -                ], 
    +                ],
                     "responses": {
                         "200": {
                             "description": "some operation"
    @@ -108,9 +112,13 @@ 

    YAML

    produces: - application/json - text/plain + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json consumes: - application/json - text/plain + - application/vnd.ms-excel paths: /pets: get: @@ -128,24 +136,28 @@

    Noncompliant Code Example (OpenAPI 3)

    JSON

     {
    -    "openapi": "3.0.1", 
    +    "openapi": "3.0.1",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "paths": {
    -        "/pets": null, 
    -        "post": {
    -            "requestBody": null, 
    -            "content": {
    -                "application": {}, 
    -                "text/*": {}
    -            }, 
    -            "responses": null, 
    -            "200": {
    -                "description": "some operation", 
    -                "content": null, 
    -                "application": {}
    +        "/pets": {
    +            "post": {
    +                "requestBody": {
    +                    "content": {
    +                        "application": {},
    +                        "invalid-no-slash": {}
    +                    }
    +                },
    +                "responses": {
    +                    "200": {
    +                        "description": "some operation",
    +                        "content": {
    +                            "application": {}
    +                        }
    +                    }
    +                }
                 }
             }
         }
    @@ -163,7 +175,7 @@ 

    YAML

    requestBody: content: 'application': {} - 'text/*': {} + 'invalid-no-slash': {} responses: '200': description: some operation @@ -174,25 +186,35 @@

    Compliant Solution (OpenAPI 3)

    JSON

     {
    -    "openapi": "3.0.1", 
    +    "openapi": "3.0.1",
         "info": {
    -        "version": "1.0.0", 
    +        "version": "1.0.0",
             "title": "Swagger Petstore"
    -    }, 
    +    },
         "paths": {
             "/pets": {
                 "post": {
                     "requestBody": {
                         "content": {
    -                        "application/json": {}, 
    -                        "text/plain": {}
    +                        "application/json": {},
    +                        "multipart/form-data": {},
    +                        "application/vnd.ms-excel": {},
    +                        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {},
    +                        "application/ld+json": {},
    +                        "image/*": {},
    +                        "*/*": {}
                         }
    -                }, 
    -                "responses": { 
    +                },
    +                "responses": {
                         "200": {
    -                        "description": "some operation", 
    +                        "description": "some operation",
                             "content": {
    -                            "application/json": {}
    +                            "application/json": {},
    +                            "text/csv": {},
    +                            "image/png": {},
    +                            "text/plain; charset=utf-8": {},
    +                            "application/vnd.ms-excel": {},
    +                            "application/vnd.github+json": {}
                             }
                         }
                     }
    @@ -213,10 +235,20 @@ 

    YAML

    requestBody: content: 'application/json': {} - 'text/plain': {} + 'multipart/form-data': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: '200': description: some operation content: 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'text/plain; charset=utf-8': {} + 'application/vnd.ms-excel': {} + 'application/vnd.github+json': {}
    \ No newline at end of file diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html index 2ec09784..9229aa30 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html @@ -1,5 +1,5 @@ -

    A string schema without a format, or with an unrecognized format, may cause developers to use the wrong variable types in the API implementation.

    -

    This rule fires when the format field is absent or present but not recognized. Every string schema must declare one of the valid formats.

    +

    A string schema that declares a format field with an unrecognized value may cause developers to use the wrong variable types in the API implementation.

    +

    This rule fires only when the format field is present but not recognized. String schemas without a format field are accepted.

    Valid formats: date, date-time, password, byte, binary, email, uuid, uri, hostname, ipv4, ipv6, HEX, HEX(16), json, xml, base64.

    Configurable: formats-allowed — comma-separated list of allowed formats (default: the list above).

    Noncompliant Code Example (OpenAPI 2)

    @@ -19,8 +19,6 @@

    Noncompliant Code Example (OpenAPI 2)

    items: type: object properties: - name: - type: string # Noncompliant {{OAR037: String types requires a valid format}} — no format date: type: string # Noncompliant {{OAR037: String types requires a valid format}} — invalid format format: 'dd/mm/yyyy' @@ -42,6 +40,8 @@

    Compliant Solution (OpenAPI 2)

    items: type: object properties: + name: + type: string date: type: string format: date @@ -65,8 +65,6 @@

    Noncompliant Code Example (OpenAPI 3)

    items: type: object properties: - name: - type: string # Noncompliant {{OAR037: String types requires a valid format}} — no format date: type: string # Noncompliant {{OAR037: String types requires a valid format}} — invalid format format: dd/mm/yyyy @@ -90,7 +88,9 @@

    Compliant Solution (OpenAPI 3)

    items: type: object properties: + name: + type: string date: type: string format: date -
    \ No newline at end of file +
    diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR044.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR044.html index 008ed872..4ecec85a 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR044.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR044.html @@ -34,9 +34,13 @@

    Compliant Solution (OpenAPI 2)

    produces: - application/json - text/plain + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json consumes: - application/json - text/plain + - application/vnd.ms-excel paths: /pets: get: @@ -62,7 +66,7 @@

    Noncompliant Code Example (OpenAPI 3)

    requestBody: content: 'application': {} # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} - 'text/*': {} + 'invalid-no-slash': {} # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} responses: '200': description: some operation @@ -81,10 +85,20 @@

    Compliant Solution (OpenAPI 3)

    requestBody: content: 'application/json': {} - 'text/plain': {} + 'multipart/form-data': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: '200': description: some operation content: 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'text/plain; charset=utf-8': {} + 'application/vnd.ms-excel': {} + 'application/vnd.github+json': {}
    \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java index ad561e9b..bbba0aaa 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java @@ -70,6 +70,11 @@ public void verifyInV2BlankFormat() { verifyV2("blank-format"); } + @Test + public void verifyInV3StringWithoutFormatIsValid() { + verifyV3("no-format"); + } + @Override public void verifyParameters() { assertNumberOfParameters(1); diff --git a/src/test/resources/checks/v2/core/OAR044/media-type.json b/src/test/resources/checks/v2/core/OAR044/media-type.json index fb0e81ab..216fca4c 100644 --- a/src/test/resources/checks/v2/core/OAR044/media-type.json +++ b/src/test/resources/checks/v2/core/OAR044/media-type.json @@ -18,6 +18,8 @@ } }, "post" : { + "produces" : [ "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/ld+json", "text/csv", "image/png" ], + "consumes" : [ "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/ld+json" ], "responses" : { "200" : { "description" : "some operation" diff --git a/src/test/resources/checks/v2/core/OAR044/media-type.yaml b/src/test/resources/checks/v2/core/OAR044/media-type.yaml index 7a3a4849..fd2d08e0 100644 --- a/src/test/resources/checks/v2/core/OAR044/media-type.yaml +++ b/src/test/resources/checks/v2/core/OAR044/media-type.yaml @@ -20,7 +20,17 @@ paths: responses: '200': description: some operation - post: # without produces/consumes -> should not fail + post: + produces: + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json + - text/csv + - image/png + consumes: + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json responses: '200': description: some operation \ No newline at end of file diff --git a/src/test/resources/checks/v2/format/OAR037/plain.json b/src/test/resources/checks/v2/format/OAR037/plain.json index 038c38a3..7a695aca 100644 --- a/src/test/resources/checks/v2/format/OAR037/plain.json +++ b/src/test/resources/checks/v2/format/OAR037/plain.json @@ -14,7 +14,7 @@ "type" : "object", "properties" : { "without" : { - "type" : "string" # Noncompliant {{OAR037: String types requires a valid format}} + "type" : "string" }, "date" : { "type" : "string", diff --git a/src/test/resources/checks/v2/format/OAR037/plain.yaml b/src/test/resources/checks/v2/format/OAR037/plain.yaml index 957f5bc1..e58efd2a 100644 --- a/src/test/resources/checks/v2/format/OAR037/plain.yaml +++ b/src/test/resources/checks/v2/format/OAR037/plain.yaml @@ -12,7 +12,7 @@ paths: type: object properties: without: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string date: type: string format: date diff --git a/src/test/resources/checks/v2/format/OAR044/media-type.json b/src/test/resources/checks/v2/format/OAR044/media-type.json index fb0e81ab..216fca4c 100644 --- a/src/test/resources/checks/v2/format/OAR044/media-type.json +++ b/src/test/resources/checks/v2/format/OAR044/media-type.json @@ -18,6 +18,8 @@ } }, "post" : { + "produces" : [ "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/ld+json", "text/csv", "image/png" ], + "consumes" : [ "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/ld+json" ], "responses" : { "200" : { "description" : "some operation" diff --git a/src/test/resources/checks/v2/format/OAR044/media-type.yaml b/src/test/resources/checks/v2/format/OAR044/media-type.yaml index 7a3a4849..fd2d08e0 100644 --- a/src/test/resources/checks/v2/format/OAR044/media-type.yaml +++ b/src/test/resources/checks/v2/format/OAR044/media-type.yaml @@ -20,7 +20,17 @@ paths: responses: '200': description: some operation - post: # without produces/consumes -> should not fail + post: + produces: + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json + - text/csv + - image/png + consumes: + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/ld+json responses: '200': description: some operation \ No newline at end of file diff --git a/src/test/resources/checks/v3/core/OAR044/media-type.json b/src/test/resources/checks/v3/core/OAR044/media-type.json index 992ea7bd..d954dc15 100644 --- a/src/test/resources/checks/v3/core/OAR044/media-type.json +++ b/src/test/resources/checks/v3/core/OAR044/media-type.json @@ -34,9 +34,27 @@ } ] }, "post" : { + "requestBody" : { + "content" : { + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } + } + }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v3/core/OAR044/media-type.yaml b/src/test/resources/checks/v3/core/OAR044/media-type.yaml index 80aba718..3b6ac527 100644 --- a/src/test/resources/checks/v3/core/OAR044/media-type.yaml +++ b/src/test/resources/checks/v3/core/OAR044/media-type.yaml @@ -24,6 +24,21 @@ paths: - name: otherParam in: path post: + requestBody: + content: + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} diff --git a/src/test/resources/checks/v3/format/OAR037/complete.json b/src/test/resources/checks/v3/format/OAR037/complete.json index b9e6416d..dfefdf0b 100644 --- a/src/test/resources/checks/v3/format/OAR037/complete.json +++ b/src/test/resources/checks/v3/format/OAR037/complete.json @@ -54,7 +54,7 @@ "type": "object", "properties": { "without": { - "type": "string" # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string" }, "date": { "type": "string", diff --git a/src/test/resources/checks/v3/format/OAR037/complete.yaml b/src/test/resources/checks/v3/format/OAR037/complete.yaml index 2120e544..2e7b6f3f 100644 --- a/src/test/resources/checks/v3/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v3/format/OAR037/complete.yaml @@ -37,7 +37,7 @@ paths: type: object properties: without: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string date: type: string format: date diff --git a/src/test/resources/checks/v3/format/OAR037/no-format.json b/src/test/resources/checks/v3/format/OAR037/no-format.json new file mode 100644 index 00000000..7c0c15ba --- /dev/null +++ b/src/test/resources/checks/v3/format/OAR037/no-format.json @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore" + }, + "paths": { + "/invoices": { + "get": { + "responses": { + "200": { + "description": "A invoice.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "code": { + "type": "string" + } + } + } + } + } + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/format/OAR037/no-format.yaml b/src/test/resources/checks/v3/format/OAR037/no-format.yaml new file mode 100644 index 00000000..9ca042be --- /dev/null +++ b/src/test/resources/checks/v3/format/OAR037/no-format.yaml @@ -0,0 +1,21 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore +paths: + /invoices: + get: + responses: + 200: + description: A invoice. + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: + type: string + code: + type: string diff --git a/src/test/resources/checks/v3/format/OAR044/media-type.json b/src/test/resources/checks/v3/format/OAR044/media-type.json index 89bd88b8..3d3b5d7a 100644 --- a/src/test/resources/checks/v3/format/OAR044/media-type.json +++ b/src/test/resources/checks/v3/format/OAR044/media-type.json @@ -32,12 +32,26 @@ "requestBody" : { "content" : { "application" : { }, # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} - "text/*" : { } + "text/*" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } } }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v3/format/OAR044/media-type.yaml b/src/test/resources/checks/v3/format/OAR044/media-type.yaml index 46ffdc20..60d8c85a 100644 --- a/src/test/resources/checks/v3/format/OAR044/media-type.yaml +++ b/src/test/resources/checks/v3/format/OAR044/media-type.yaml @@ -23,6 +23,19 @@ paths: content: 'application': { } # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} 'text/*': { } + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} diff --git a/src/test/resources/checks/v31/core/OAR044/media-type.json b/src/test/resources/checks/v31/core/OAR044/media-type.json index 938aab91..2200bfd1 100644 --- a/src/test/resources/checks/v31/core/OAR044/media-type.json +++ b/src/test/resources/checks/v31/core/OAR044/media-type.json @@ -34,9 +34,27 @@ } ] }, "post" : { + "requestBody" : { + "content" : { + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } + } + }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v31/core/OAR044/media-type.yaml b/src/test/resources/checks/v31/core/OAR044/media-type.yaml index 67d72d30..23fe935e 100644 --- a/src/test/resources/checks/v31/core/OAR044/media-type.yaml +++ b/src/test/resources/checks/v31/core/OAR044/media-type.yaml @@ -24,6 +24,21 @@ paths: - name: otherParam in: path post: + requestBody: + content: + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} diff --git a/src/test/resources/checks/v31/format/OAR037/complete.json b/src/test/resources/checks/v31/format/OAR037/complete.json index 8c943299..94e76d39 100644 --- a/src/test/resources/checks/v31/format/OAR037/complete.json +++ b/src/test/resources/checks/v31/format/OAR037/complete.json @@ -54,7 +54,7 @@ "type": "object", "properties": { "without": { - "type": "string" # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string" }, "date": { "type": "string", diff --git a/src/test/resources/checks/v31/format/OAR037/complete.yaml b/src/test/resources/checks/v31/format/OAR037/complete.yaml index 769a4793..cfcb826e 100644 --- a/src/test/resources/checks/v31/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v31/format/OAR037/complete.yaml @@ -37,7 +37,7 @@ paths: type: object properties: without: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string date: type: string format: date diff --git a/src/test/resources/checks/v31/format/OAR044/media-type.json b/src/test/resources/checks/v31/format/OAR044/media-type.json index 86ec986a..05232934 100644 --- a/src/test/resources/checks/v31/format/OAR044/media-type.json +++ b/src/test/resources/checks/v31/format/OAR044/media-type.json @@ -32,12 +32,26 @@ "requestBody" : { "content" : { "application" : { }, # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} - "text/*" : { } + "text/*" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } } }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v31/format/OAR044/media-type.yaml b/src/test/resources/checks/v31/format/OAR044/media-type.yaml index 60015c9b..71f903d8 100644 --- a/src/test/resources/checks/v31/format/OAR044/media-type.yaml +++ b/src/test/resources/checks/v31/format/OAR044/media-type.yaml @@ -23,6 +23,19 @@ paths: content: 'application': { } # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} 'text/*': { } + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} diff --git a/src/test/resources/checks/v32/core/OAR044/media-type.json b/src/test/resources/checks/v32/core/OAR044/media-type.json index 517db4d8..9b7f036f 100644 --- a/src/test/resources/checks/v32/core/OAR044/media-type.json +++ b/src/test/resources/checks/v32/core/OAR044/media-type.json @@ -34,9 +34,27 @@ } ] }, "post" : { + "requestBody" : { + "content" : { + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } + } + }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v32/core/OAR044/media-type.yaml b/src/test/resources/checks/v32/core/OAR044/media-type.yaml index 22b32de1..bde8534c 100644 --- a/src/test/resources/checks/v32/core/OAR044/media-type.yaml +++ b/src/test/resources/checks/v32/core/OAR044/media-type.yaml @@ -24,6 +24,21 @@ paths: - name: otherParam in: path post: + requestBody: + content: + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} diff --git a/src/test/resources/checks/v32/format/OAR037/complete.json b/src/test/resources/checks/v32/format/OAR037/complete.json index 4b9f4508..509a1a44 100644 --- a/src/test/resources/checks/v32/format/OAR037/complete.json +++ b/src/test/resources/checks/v32/format/OAR037/complete.json @@ -54,7 +54,7 @@ "type": "object", "properties": { "without": { - "type": "string" # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string" }, "date": { "type": "string", diff --git a/src/test/resources/checks/v32/format/OAR037/complete.yaml b/src/test/resources/checks/v32/format/OAR037/complete.yaml index 2be1bfa4..f66402b0 100644 --- a/src/test/resources/checks/v32/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v32/format/OAR037/complete.yaml @@ -37,7 +37,7 @@ paths: type: object properties: without: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string date: type: string format: date diff --git a/src/test/resources/checks/v32/format/OAR044/media-type.json b/src/test/resources/checks/v32/format/OAR044/media-type.json index a37e77ee..0251e212 100644 --- a/src/test/resources/checks/v32/format/OAR044/media-type.json +++ b/src/test/resources/checks/v32/format/OAR044/media-type.json @@ -32,12 +32,26 @@ "requestBody" : { "content" : { "application" : { }, # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} - "text/*" : { } + "text/*" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/ld+json" : { }, + "image/*" : { }, + "*/*" : { } } }, "responses" : { "200" : { - "description" : "some operation" + "description" : "some operation", + "content" : { + "application/json" : { }, + "text/csv" : { }, + "image/png" : { }, + "application/vnd.ms-excel" : { }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : { }, + "application/vnd.github+json" : { }, + "text/plain; charset=utf-8" : { } + } } } } diff --git a/src/test/resources/checks/v32/format/OAR044/media-type.yaml b/src/test/resources/checks/v32/format/OAR044/media-type.yaml index 4dc3378b..6e1cbd7d 100644 --- a/src/test/resources/checks/v32/format/OAR044/media-type.yaml +++ b/src/test/resources/checks/v32/format/OAR044/media-type.yaml @@ -23,6 +23,19 @@ paths: content: 'application': { } # Noncompliant {{OAR044: Declared media type range should conform to RFC7231}} 'text/*': { } + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/ld+json': {} + 'image/*': {} + '*/*': {} responses: - '200': - description: some operation + '200': + description: some operation + content: + 'application/json': {} + 'text/csv': {} + 'image/png': {} + 'application/vnd.ms-excel': {} + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {} + 'application/vnd.github+json': {} + 'text/plain; charset=utf-8': {} From 6f12d5ffb34623797754968ffdf8d4b7ddff10ae Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Wed, 8 Jul 2026 21:51:18 -0500 Subject: [PATCH 05/10] feat: oar037 with format-or-pattern validation --- CHANGELOG.md | 6 +++ pom.xml | 2 +- .../checks/format/AbstractFormatCheck.java | 6 +-- .../format/OAR016NumericFormatCheck.java | 2 +- .../format/OAR037StringFormatCheck.java | 45 +++++++++++++++---- .../OAR052UndefinedNumericFormatCheck.java | 2 +- .../security/OAR076NumericFormatCheck.java | 2 +- src/main/resources/messages/errors.properties | 2 +- .../resources/messages/errors_es.properties | 2 +- .../openapi/rules/openapi/format/OAR037.html | 23 +++++++--- .../openapi/rules/openapi/format/OAR037.json | 6 +-- .../openapi/rules/openapi/format/OAR037.html | 23 +++++++--- .../openapi/rules/openapi/format/OAR037.json | 6 +-- .../format/OAR037StringFormatCheckTest.java | 4 +- .../checks/v2/format/OAR037/nested.json | 6 ++- .../checks/v2/format/OAR037/nested.yaml | 5 ++- .../checks/v2/format/OAR037/plain.json | 12 ++++- .../checks/v2/format/OAR037/plain.yaml | 8 +++- .../checks/v2/format/OAR037/with-$ref.json | 6 ++- .../checks/v2/format/OAR037/with-$ref.yaml | 5 ++- .../checks/v3/format/OAR037/complete.json | 18 +++++--- .../checks/v3/format/OAR037/complete.yaml | 14 ++++-- .../checks/v3/format/OAR037/nested.json | 6 ++- .../checks/v3/format/OAR037/nested.yaml | 5 ++- .../checks/v3/format/OAR037/no-format.json | 14 ++++-- .../checks/v3/format/OAR037/no-format.yaml | 11 +++-- .../checks/v3/format/OAR037/with-$ref.json | 6 ++- .../checks/v3/format/OAR037/with-$ref.yaml | 5 ++- .../checks/v31/format/OAR037/complete.json | 18 +++++--- .../checks/v31/format/OAR037/complete.yaml | 14 ++++-- .../checks/v31/format/OAR037/nested.json | 6 ++- .../checks/v31/format/OAR037/nested.yaml | 5 ++- .../checks/v31/format/OAR037/with-$ref.json | 6 ++- .../checks/v31/format/OAR037/with-$ref.yaml | 5 ++- .../checks/v32/format/OAR037/complete.json | 18 +++++--- .../checks/v32/format/OAR037/complete.yaml | 14 ++++-- .../checks/v32/format/OAR037/nested.json | 6 ++- .../checks/v32/format/OAR037/nested.yaml | 5 ++- .../checks/v32/format/OAR037/with-$ref.json | 6 ++- .../checks/v32/format/OAR037/with-$ref.yaml | 5 ++- 40 files changed, 270 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd94274..88a910d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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.5.0-beta-3] - 2026-07-08 + +### Changed + +- OAR037 - StringFormatCheck - Reclassified as a security rule (`VULNERABILITY`, tag `safety`, keeping its existing `format` rule group/package). String schemas must now declare a valid `format`, or — when no `format` is declared — a non-empty, syntactically valid `pattern`; schemas with neither a valid `format` nor a valid `pattern` are reported. + ## [1.5.0-beta-2] - 2026-06-24 ### Changed diff --git a/pom.xml b/pom.xml index adb460dd..ae54aede 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.5.0-beta-2 + 1.5.0-beta-3 sonar-plugin SonarQube OpenAPI Community Rules 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 5d4d728b..adb15cde 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractFormatCheck.java @@ -28,13 +28,13 @@ private void visitV2Node(JsonNode node) { String type = typeNode.getTokenValue(); JsonNode formatNode = node.get("format"); if (formatNode.isMissing()) { - validate(type, null, typeNode); + validate(type, null, typeNode, node); return; } String format = formatNode.getTokenValue(); if (format == null || format.isBlank()) return; - validate(type, format.trim(), typeNode); + validate(type, format.trim(), typeNode, node); } - public abstract void validate(String type, String format, JsonNode typeNode); + public abstract void validate(String type, String format, JsonNode typeNode, JsonNode node); } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR016NumericFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR016NumericFormatCheck.java index 4a0e417a..01ec200a 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR016NumericFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR016NumericFormatCheck.java @@ -10,7 +10,7 @@ public class OAR016NumericFormatCheck extends AbstractFormatCheck { private static final String MESSAGE = "OAR016.error"; @Override - public void validate(String type, String format, JsonNode typeNode) { + public void validate(String type, String format, JsonNode typeNode, JsonNode node) { if (isInvalidInteger(type, format) || isInvalidNumber(type, format)) { addIssue(KEY, translate(MESSAGE), typeNode.key()); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java index 4756044d..96e58d22 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheck.java @@ -4,6 +4,8 @@ import org.sonar.check.RuleProperty; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -22,17 +24,44 @@ public class OAR037StringFormatCheck extends AbstractFormatCheck { private String formatsAllowed = DEFAULT_FORMATS_ALLOWED; @Override - public void validate(String type, String format, JsonNode typeNode) { - Set validFormats = Stream.of(formatsAllowed.split(",")) - .map(f -> f.trim().toLowerCase()) - .collect(Collectors.toSet()); + public void validate(String type, String format, JsonNode typeNode, JsonNode node) { + if (!"string".equals(type)) { + return; + } + + if (format != null) { + Set validFormats = Stream.of(formatsAllowed.split(",")) + .map(f -> f.trim().toLowerCase()) + .collect(Collectors.toSet()); + if (!validFormats.contains(format.toLowerCase())) { + addIssue(KEY, translate(MESSAGE), typeNode.key()); + } + return; + } - if (isInvalidString(type, format, validFormats)) { + if (!hasValidPattern(node)) { addIssue(KEY, translate(MESSAGE), typeNode.key()); } } - private boolean isInvalidString(String type, String format, Set validFormats) { - return "string".equals(type) && format != null && !validFormats.contains(format.toLowerCase()); + private boolean hasValidPattern(JsonNode node) { + JsonNode patternNode = node.get("pattern"); + if (patternNode.isMissing()) { + return false; + } + String pattern = patternNode.getTokenValue(); + if (pattern == null || pattern.isBlank()) { + return false; + } + return isValidRegex(pattern.trim()); + } + + private boolean isValidRegex(String pattern) { + try { + Pattern.compile(pattern); + return true; + } catch (PatternSyntaxException e) { + return false; + } } -} \ No newline at end of file +} diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR052UndefinedNumericFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR052UndefinedNumericFormatCheck.java index 38b4006f..52a84979 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR052UndefinedNumericFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR052UndefinedNumericFormatCheck.java @@ -10,7 +10,7 @@ public class OAR052UndefinedNumericFormatCheck extends AbstractFormatCheck { private static final String MESSAGE = "OAR052.error"; @Override - public void validate(String type, String format, JsonNode typeNode) { + public void validate(String type, String format, JsonNode typeNode, JsonNode node) { if (("integer".equals(type) || "number".equals(type)) && format == null) { addIssue(KEY, translate(MESSAGE), typeNode.key()); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java index 0a6286e5..47cc722d 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java @@ -13,7 +13,7 @@ public class OAR076NumericFormatCheck extends AbstractFormatCheck { private static final String MESSAGE = "OAR076.error"; @Override - public void validate(String type, String format, JsonNode typeNode) { + public void validate(String type, String format, JsonNode typeNode, JsonNode node) { boolean isInvalid = false; if ("integer".equals(type)) { diff --git a/src/main/resources/messages/errors.properties b/src/main/resources/messages/errors.properties index 3b47830f..78ab417f 100644 --- a/src/main/resources/messages/errors.properties +++ b/src/main/resources/messages/errors.properties @@ -37,7 +37,7 @@ OAR032.error=Ambiguous path parts not encouraged: {0} OAR033.error-header-required=''{0}'' header must be required OAR035.error=Response code {0} must be defined for operations with security schemes defined OAR036.error=Cookie use is forbidden as a session mechanism -OAR037.error=String types requires a valid format +OAR037.error=String types require a valid format, or a valid pattern when no format is defined OAR038.error=''data'' or ''error'' property is required OAR038.error-required-schema=Response schema is required OAR038.error-required-one-property=At least you have to define the identifier property diff --git a/src/main/resources/messages/errors_es.properties b/src/main/resources/messages/errors_es.properties index 738ba524..7b509b68 100644 --- a/src/main/resources/messages/errors_es.properties +++ b/src/main/resources/messages/errors_es.properties @@ -37,7 +37,7 @@ OAR032.error=Nombres de partes de path ambiguos no permitidos: {0} OAR033.error-header-required=La cabecera ''{0}'' debe ser obligatoria OAR035.error=El código de respuesta {0} debe estar definido cuando la operación tiene esquemas de seguridad definidos OAR036.error=El uso de cookies está prohibido como mecanismo de sesión -OAR037.error=Las propiedades de tipo string deben definir un formato válido +OAR037.error=Las propiedades de tipo string deben definir un formato válido o, si no hay formato, un pattern válido OAR038.error=La propiedad ''data'' o ''error'' es obligatoria OAR038.error-required-schema=El esquema de respuesta es obligatorio OAR038.error-required-one-property=Se debe de definir al menos una propiedad diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html index 873828be..ce3e0344 100644 --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.html @@ -1,5 +1,10 @@ -

    Una propiedad de tipo string con un valor de format no reconocido puede provocar una implementación incorrecta de la API.

    -

    Esta regla dispara únicamente cuando el campo format está presente pero no es reconocido. Los schemas de tipo string sin campo format son aceptados.

    +

    Una propiedad de tipo string sin un format reconocido y sin un pattern válido puede permitir la entrada de valores arbitrarios sin validar, lo que puede llevar a implementaciones inseguras o incorrectas de la API.

    +

    Esta regla exige que todo schema de tipo string restrinja sus valores de una de estas dos formas:

    +
      +
    • declarando un campo format con un valor reconocido, o
    • +
    • si no se declara format, declarando un campo pattern que sea una expresión regular no vacía y sintácticamente válida.
    • +
    +

    La regla dispara cuando format está presente pero no es reconocido, o cuando format está ausente y pattern está ausente, vacío, o no es una expresión regular sintácticamente válida.

    Formatos válidos: date, date-time, password, byte, binary, email, uuid, uri, hostname, ipv4, ipv6, HEX, HEX(16), json, xml, base64.

    Configurable: formats-allowed — lista de formatos permitidos separados por coma (por defecto: la lista anterior).

    Ejemplo de código no compatible (OpenAPI 2)

    @@ -19,8 +24,10 @@

    Ejemplo de código no compatible (OpenAPI 2)

    items: type: object properties: + id: + type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato o pattern válido}} — sin format, sin pattern date: - type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format inválido + type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato o pattern válido}} — format inválido format: 'dd/mm/yyyy'

    Solución compatible (OpenAPI 2)

    @@ -40,8 +47,9 @@

    Solución compatible (OpenAPI 2)

    items: type: object properties: - name: + id: type: string + pattern: '^[A-Z]{3}-\d+$' date: type: string format: date @@ -65,8 +73,10 @@

    Ejemplo de código no compatible (OpenAPI 3)

    items: type: object properties: + id: + type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato o pattern válido}} — sin format, sin pattern date: - type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato válido}} — format inválido + type: string # No conforme {{OAR037: Las propiedades de tipo string deben definir un formato o pattern válido}} — format inválido format: dd/mm/yyyy

    Solución compatible (OpenAPI 3)

    @@ -88,8 +98,9 @@

    Solución compatible (OpenAPI 3)

    items: type: object properties: - name: + id: type: string + pattern: '^[A-Z]{3}-\d+$' date: type: string format: date diff --git a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.json b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.json index 07772b02..7e563a3a 100644 --- a/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.json +++ b/src/main/resources/org/sonar/l10n/es/openapi/rules/openapi/format/OAR037.json @@ -1,13 +1,13 @@ { - "title": "OAR037 - StringFormat - Los tipos string requieren un formato válido", - "type": "BUG", + "title": "OAR037 - StringFormat - Los tipos string requieren un formato o pattern válido", + "type": "VULNERABILITY", "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "30min" }, "tags": [ - "format" + "safety" ], "defaultSeverity": "MAJOR" } \ No newline at end of file diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html index 9229aa30..64fe2c3c 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.html @@ -1,5 +1,10 @@ -

    A string schema that declares a format field with an unrecognized value may cause developers to use the wrong variable types in the API implementation.

    -

    This rule fires only when the format field is present but not recognized. String schemas without a format field are accepted.

    +

    A string schema without a recognized format and without a valid pattern may allow arbitrary, unvalidated input, which can lead developers to use the wrong variable types or to accept unsafe values in the API implementation.

    +

    This rule requires every string schema to constrain its values in one of two ways:

    +
      +
    • declare a format field with a recognized value, or
    • +
    • if no format is declared, declare a pattern field that is a non-empty, syntactically valid regular expression.
    • +
    +

    The rule fires when format is present but not recognized, or when format is absent and pattern is missing, empty, or not a syntactically valid regular expression.

    Valid formats: date, date-time, password, byte, binary, email, uuid, uri, hostname, ipv4, ipv6, HEX, HEX(16), json, xml, base64.

    Configurable: formats-allowed — comma-separated list of allowed formats (default: the list above).

    Noncompliant Code Example (OpenAPI 2)

    @@ -19,8 +24,10 @@

    Noncompliant Code Example (OpenAPI 2)

    items: type: object properties: + id: + type: string # Noncompliant {{OAR037: String types require a valid format or pattern}} — no format, no pattern date: - type: string # Noncompliant {{OAR037: String types requires a valid format}} — invalid format + type: string # Noncompliant {{OAR037: String types require a valid format or pattern}} — invalid format format: 'dd/mm/yyyy'

    Compliant Solution (OpenAPI 2)

    @@ -40,8 +47,9 @@

    Compliant Solution (OpenAPI 2)

    items: type: object properties: - name: + id: type: string + pattern: '^[A-Z]{3}-\d+$' date: type: string format: date @@ -65,8 +73,10 @@

    Noncompliant Code Example (OpenAPI 3)

    items: type: object properties: + id: + type: string # Noncompliant {{OAR037: String types require a valid format or pattern}} — no format, no pattern date: - type: string # Noncompliant {{OAR037: String types requires a valid format}} — invalid format + type: string # Noncompliant {{OAR037: String types require a valid format or pattern}} — invalid format format: dd/mm/yyyy

    Compliant Solution (OpenAPI 3)

    @@ -88,8 +98,9 @@

    Compliant Solution (OpenAPI 3)

    items: type: object properties: - name: + id: type: string + pattern: '^[A-Z]{3}-\d+$' date: type: string format: date diff --git a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.json b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.json index c0032cf4..9e95db6b 100644 --- a/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.json +++ b/src/main/resources/org/sonar/l10n/openapi/rules/openapi/format/OAR037.json @@ -1,13 +1,13 @@ { - "title": "OAR037 - StringFormat - String types requires a valid format", - "type": "BUG", + "title": "OAR037 - StringFormat - String types require a valid format or pattern", + "type": "VULNERABILITY", "status": "ready", "remediation": { "func": "Constant\/Issue", "constantCost": "30min" }, "tags": [ - "format" + "safety" ], "defaultSeverity": "MAJOR" } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java index bbba0aaa..fb04f56a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/format/OAR037StringFormatCheckTest.java @@ -71,7 +71,7 @@ public void verifyInV2BlankFormat() { } @Test - public void verifyInV3StringWithoutFormatIsValid() { + public void verifyInV3PatternFallback() { verifyV3("no-format"); } @@ -83,6 +83,6 @@ public void verifyParameters() { @Override public void verifyRule() { - assertRuleProperties("OAR037 - StringFormat - String types requires a valid format", RuleType.BUG, Severity.MAJOR, tags("format")); + assertRuleProperties("OAR037 - StringFormat - String types require a valid format or pattern", RuleType.VULNERABILITY, Severity.MAJOR, tags("safety")); } } diff --git a/src/test/resources/checks/v2/format/OAR037/nested.json b/src/test/resources/checks/v2/format/OAR037/nested.json index 18663f0b..819ad8b7 100644 --- a/src/test/resources/checks/v2/format/OAR037/nested.json +++ b/src/test/resources/checks/v2/format/OAR037/nested.json @@ -17,8 +17,12 @@ "type" : "object", "properties" : { "value" : { - "type" : "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type" : "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format" : "YYYY-MM-DD" + }, + "code" : { + "type" : "string", + "pattern" : "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v2/format/OAR037/nested.yaml b/src/test/resources/checks/v2/format/OAR037/nested.yaml index 4ad7fd36..6a3c55c2 100644 --- a/src/test/resources/checks/v2/format/OAR037/nested.yaml +++ b/src/test/resources/checks/v2/format/OAR037/nested.yaml @@ -15,6 +15,9 @@ paths: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v2/format/OAR037/plain.json b/src/test/resources/checks/v2/format/OAR037/plain.json index 7a695aca..31df3943 100644 --- a/src/test/resources/checks/v2/format/OAR037/plain.json +++ b/src/test/resources/checks/v2/format/OAR037/plain.json @@ -14,7 +14,15 @@ "type" : "object", "properties" : { "without" : { - "type" : "string" + "type" : "string" # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + }, + "withPattern" : { + "type" : "string", + "pattern" : "^[A-Z]{3}-[0-9]+$" + }, + "withInvalidPattern" : { + "type" : "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern" : "[" }, "date" : { "type" : "string", @@ -61,7 +69,7 @@ "format" : "ipv6" }, "other" : { - "type" : "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type" : "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format" : "YYYY-MM-DD" } } diff --git a/src/test/resources/checks/v2/format/OAR037/plain.yaml b/src/test/resources/checks/v2/format/OAR037/plain.yaml index e58efd2a..a09e36e2 100644 --- a/src/test/resources/checks/v2/format/OAR037/plain.yaml +++ b/src/test/resources/checks/v2/format/OAR037/plain.yaml @@ -12,7 +12,13 @@ paths: type: object properties: without: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + withPattern: type: string + pattern: '^[A-Z]{3}-[0-9]+$' + withInvalidPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: '[' date: type: string format: date @@ -53,5 +59,5 @@ paths: type: string format: HEX(16) other: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD diff --git a/src/test/resources/checks/v2/format/OAR037/with-$ref.json b/src/test/resources/checks/v2/format/OAR037/with-$ref.json index 5d9329d7..e9c50847 100644 --- a/src/test/resources/checks/v2/format/OAR037/with-$ref.json +++ b/src/test/resources/checks/v2/format/OAR037/with-$ref.json @@ -32,8 +32,12 @@ "type" : "object", "properties" : { "value" : { - "type" : "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type" : "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format" : "YYYY-MM-DD" + }, + "code" : { + "type" : "string", + "pattern" : "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v2/format/OAR037/with-$ref.yaml b/src/test/resources/checks/v2/format/OAR037/with-$ref.yaml index 3fea1aa9..a589efeb 100644 --- a/src/test/resources/checks/v2/format/OAR037/with-$ref.yaml +++ b/src/test/resources/checks/v2/format/OAR037/with-$ref.yaml @@ -22,5 +22,8 @@ definitions: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v3/format/OAR037/complete.json b/src/test/resources/checks/v3/format/OAR037/complete.json index dfefdf0b..7c6f894f 100644 --- a/src/test/resources/checks/v3/format/OAR037/complete.json +++ b/src/test/resources/checks/v3/format/OAR037/complete.json @@ -10,7 +10,7 @@ "in": "header", "name": "paramOne", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } }, @@ -18,7 +18,7 @@ "in": "header", "name": "paramTwo", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -34,7 +34,7 @@ "in": "header", "name": "paramThree", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -54,7 +54,15 @@ "type": "object", "properties": { "without": { - "type": "string" + "type": "string" # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + }, + "withPattern": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" + }, + "withInvalidPattern": { + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern": "[" }, "date": { "type": "string", @@ -101,7 +109,7 @@ "format": "ipv6" }, "other": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } diff --git a/src/test/resources/checks/v3/format/OAR037/complete.yaml b/src/test/resources/checks/v3/format/OAR037/complete.yaml index 2e7b6f3f..0008a528 100644 --- a/src/test/resources/checks/v3/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v3/format/OAR037/complete.yaml @@ -8,13 +8,13 @@ components: in: header name: paramOne schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paramTwo: in: header name: paramTwo schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paths: /invoices: @@ -23,7 +23,7 @@ paths: - in: header name: paramThree schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD get: parameters: @@ -37,7 +37,13 @@ paths: type: object properties: without: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + withPattern: type: string + pattern: '^[A-Z]{3}-[0-9]+$' + withInvalidPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: '[' date: type: string format: date @@ -72,5 +78,5 @@ paths: type: string format: ipv6 other: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD diff --git a/src/test/resources/checks/v3/format/OAR037/nested.json b/src/test/resources/checks/v3/format/OAR037/nested.json index 4b6316e6..5f6a71dc 100644 --- a/src/test/resources/checks/v3/format/OAR037/nested.json +++ b/src/test/resources/checks/v3/format/OAR037/nested.json @@ -19,8 +19,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v3/format/OAR037/nested.yaml b/src/test/resources/checks/v3/format/OAR037/nested.yaml index e2ba0a91..9a0465f1 100644 --- a/src/test/resources/checks/v3/format/OAR037/nested.yaml +++ b/src/test/resources/checks/v3/format/OAR037/nested.yaml @@ -17,6 +17,9 @@ paths: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v3/format/OAR037/no-format.json b/src/test/resources/checks/v3/format/OAR037/no-format.json index 7c0c15ba..57a1dd0f 100644 --- a/src/test/resources/checks/v3/format/OAR037/no-format.json +++ b/src/test/resources/checks/v3/format/OAR037/no-format.json @@ -16,13 +16,19 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "pattern": "^[A-Za-z ]+$" }, - "description": { - "type": "string" + "emptyPattern": { + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern": "" + }, + "invalidPattern": { + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern": "[a-z" }, "code": { - "type": "string" + "type": "string" # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} } } } diff --git a/src/test/resources/checks/v3/format/OAR037/no-format.yaml b/src/test/resources/checks/v3/format/OAR037/no-format.yaml index 9ca042be..ec4f21f1 100644 --- a/src/test/resources/checks/v3/format/OAR037/no-format.yaml +++ b/src/test/resources/checks/v3/format/OAR037/no-format.yaml @@ -15,7 +15,12 @@ paths: properties: name: type: string - description: - type: string + pattern: '^[A-Za-z ]+$' + emptyPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: "" + invalidPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: "[a-z" code: - type: string + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} diff --git a/src/test/resources/checks/v3/format/OAR037/with-$ref.json b/src/test/resources/checks/v3/format/OAR037/with-$ref.json index 5707bfcb..51e70ff3 100644 --- a/src/test/resources/checks/v3/format/OAR037/with-$ref.json +++ b/src/test/resources/checks/v3/format/OAR037/with-$ref.json @@ -37,8 +37,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v3/format/OAR037/with-$ref.yaml b/src/test/resources/checks/v3/format/OAR037/with-$ref.yaml index c7d2f1f4..fbe316a5 100644 --- a/src/test/resources/checks/v3/format/OAR037/with-$ref.yaml +++ b/src/test/resources/checks/v3/format/OAR037/with-$ref.yaml @@ -25,5 +25,8 @@ components: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v31/format/OAR037/complete.json b/src/test/resources/checks/v31/format/OAR037/complete.json index 94e76d39..6584f397 100644 --- a/src/test/resources/checks/v31/format/OAR037/complete.json +++ b/src/test/resources/checks/v31/format/OAR037/complete.json @@ -10,7 +10,7 @@ "in": "header", "name": "paramOne", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } }, @@ -18,7 +18,7 @@ "in": "header", "name": "paramTwo", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -34,7 +34,7 @@ "in": "header", "name": "paramThree", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -54,7 +54,15 @@ "type": "object", "properties": { "without": { - "type": "string" + "type": "string" # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + }, + "withPattern": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" + }, + "withInvalidPattern": { + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern": "[" }, "date": { "type": "string", @@ -101,7 +109,7 @@ "format": "ipv6" }, "other": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } diff --git a/src/test/resources/checks/v31/format/OAR037/complete.yaml b/src/test/resources/checks/v31/format/OAR037/complete.yaml index cfcb826e..e75afc02 100644 --- a/src/test/resources/checks/v31/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v31/format/OAR037/complete.yaml @@ -8,13 +8,13 @@ components: in: header name: paramOne schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paramTwo: in: header name: paramTwo schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paths: /invoices: @@ -23,7 +23,7 @@ paths: - in: header name: paramThree schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD get: parameters: @@ -37,7 +37,13 @@ paths: type: object properties: without: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + withPattern: type: string + pattern: '^[A-Z]{3}-[0-9]+$' + withInvalidPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: '[' date: type: string format: date @@ -72,5 +78,5 @@ paths: type: string format: ipv6 other: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD diff --git a/src/test/resources/checks/v31/format/OAR037/nested.json b/src/test/resources/checks/v31/format/OAR037/nested.json index 7d632201..5fc02cb9 100644 --- a/src/test/resources/checks/v31/format/OAR037/nested.json +++ b/src/test/resources/checks/v31/format/OAR037/nested.json @@ -19,8 +19,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v31/format/OAR037/nested.yaml b/src/test/resources/checks/v31/format/OAR037/nested.yaml index 4a1accb7..302751f2 100644 --- a/src/test/resources/checks/v31/format/OAR037/nested.yaml +++ b/src/test/resources/checks/v31/format/OAR037/nested.yaml @@ -17,6 +17,9 @@ paths: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v31/format/OAR037/with-$ref.json b/src/test/resources/checks/v31/format/OAR037/with-$ref.json index d5ed29ea..0e0d12bc 100644 --- a/src/test/resources/checks/v31/format/OAR037/with-$ref.json +++ b/src/test/resources/checks/v31/format/OAR037/with-$ref.json @@ -37,8 +37,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v31/format/OAR037/with-$ref.yaml b/src/test/resources/checks/v31/format/OAR037/with-$ref.yaml index b6437060..36a92461 100644 --- a/src/test/resources/checks/v31/format/OAR037/with-$ref.yaml +++ b/src/test/resources/checks/v31/format/OAR037/with-$ref.yaml @@ -25,5 +25,8 @@ components: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v32/format/OAR037/complete.json b/src/test/resources/checks/v32/format/OAR037/complete.json index 509a1a44..74f1adcd 100644 --- a/src/test/resources/checks/v32/format/OAR037/complete.json +++ b/src/test/resources/checks/v32/format/OAR037/complete.json @@ -10,7 +10,7 @@ "in": "header", "name": "paramOne", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } }, @@ -18,7 +18,7 @@ "in": "header", "name": "paramTwo", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -34,7 +34,7 @@ "in": "header", "name": "paramThree", "schema": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } @@ -54,7 +54,15 @@ "type": "object", "properties": { "without": { - "type": "string" + "type": "string" # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + }, + "withPattern": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" + }, + "withInvalidPattern": { + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + "pattern": "[" }, "date": { "type": "string", @@ -101,7 +109,7 @@ "format": "ipv6" }, "other": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" } } diff --git a/src/test/resources/checks/v32/format/OAR037/complete.yaml b/src/test/resources/checks/v32/format/OAR037/complete.yaml index f66402b0..0e7f4cc7 100644 --- a/src/test/resources/checks/v32/format/OAR037/complete.yaml +++ b/src/test/resources/checks/v32/format/OAR037/complete.yaml @@ -8,13 +8,13 @@ components: in: header name: paramOne schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paramTwo: in: header name: paramTwo schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD paths: /invoices: @@ -23,7 +23,7 @@ paths: - in: header name: paramThree schema: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD get: parameters: @@ -37,7 +37,13 @@ paths: type: object properties: without: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + withPattern: type: string + pattern: '^[A-Z]{3}-[0-9]+$' + withInvalidPattern: + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} + pattern: '[' date: type: string format: date @@ -72,5 +78,5 @@ paths: type: string format: ipv6 other: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD diff --git a/src/test/resources/checks/v32/format/OAR037/nested.json b/src/test/resources/checks/v32/format/OAR037/nested.json index 08aa7e31..74e72c00 100644 --- a/src/test/resources/checks/v32/format/OAR037/nested.json +++ b/src/test/resources/checks/v32/format/OAR037/nested.json @@ -19,8 +19,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v32/format/OAR037/nested.yaml b/src/test/resources/checks/v32/format/OAR037/nested.yaml index 8a2d2c6b..a6fd15be 100644 --- a/src/test/resources/checks/v32/format/OAR037/nested.yaml +++ b/src/test/resources/checks/v32/format/OAR037/nested.yaml @@ -17,6 +17,9 @@ paths: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' diff --git a/src/test/resources/checks/v32/format/OAR037/with-$ref.json b/src/test/resources/checks/v32/format/OAR037/with-$ref.json index d88ad1bc..0589c17f 100644 --- a/src/test/resources/checks/v32/format/OAR037/with-$ref.json +++ b/src/test/resources/checks/v32/format/OAR037/with-$ref.json @@ -37,8 +37,12 @@ "type": "object", "properties": { "value": { - "type": "string", # Noncompliant {{OAR037: String types requires a valid format}} + "type": "string", # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} "format": "YYYY-MM-DD" + }, + "code": { + "type": "string", + "pattern": "^[A-Z]{3}-[0-9]+$" } } } diff --git a/src/test/resources/checks/v32/format/OAR037/with-$ref.yaml b/src/test/resources/checks/v32/format/OAR037/with-$ref.yaml index 40f2fc10..b631e251 100644 --- a/src/test/resources/checks/v32/format/OAR037/with-$ref.yaml +++ b/src/test/resources/checks/v32/format/OAR037/with-$ref.yaml @@ -25,5 +25,8 @@ components: type: object properties: value: - type: string # Noncompliant {{OAR037: String types requires a valid format}} + type: string # Noncompliant {{OAR037: String types require a valid format, or a valid pattern when no format is defined}} format: YYYY-MM-DD + code: + type: string + pattern: '^[A-Z]{3}-[0-9]+$' From ce641cd0e5320302cc4731aaae5a512aced5fe96 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Fri, 10 Jul 2026 08:45:46 -0500 Subject: [PATCH 06/10] fix: oar014, oar038 and oar085 rule properties --- .../OAR014ResourceLevelWithinNonSuggestedRangeCheck.java | 2 +- .../checks/operations/OAR038StandardCreateResponseCheck.java | 3 +-- .../openapi/checks/security/OAR085OpenAPIVersionCheck.java | 5 ++--- src/test/resources/checks/v2/operations/OAR014/plain.json | 4 ++-- src/test/resources/checks/v2/operations/OAR014/plain.yaml | 4 ++-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR014ResourceLevelWithinNonSuggestedRangeCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR014ResourceLevelWithinNonSuggestedRangeCheck.java index 75164014..504aabe0 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR014ResourceLevelWithinNonSuggestedRangeCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR014ResourceLevelWithinNonSuggestedRangeCheck.java @@ -32,6 +32,6 @@ public OAR014ResourceLevelWithinNonSuggestedRangeCheck() { @Override boolean matchLevel(long level) { - return minLevel <= level; + return minLevel <= level && level <= maxLevel; } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR038StandardCreateResponseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR038StandardCreateResponseCheck.java index 6cfaa928..57e27ce6 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR038StandardCreateResponseCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR038StandardCreateResponseCheck.java @@ -24,7 +24,6 @@ public class OAR038StandardCreateResponseCheck extends AbstractExplicitResponseC description = "Valid top-level property name for the standard response.", defaultValue = DATA_PROPERTY ) - @SuppressWarnings("unused") private String dataNode = DATA_PROPERTY; public OAR038StandardCreateResponseCheck() { @@ -44,7 +43,7 @@ protected void visitV2ExplicitNode(JsonNode node) { for (Map.Entry entry : properties.entrySet()) { String propName = entry.getKey(); - if (DATA_PROPERTY.equals(propName) || ERROR_PROPERTY.equals(propName)) { + if (dataNode.equals(propName) || ERROR_PROPERTY.equals(propName)) { Map subProps = getAllProperties(resolve(entry.getValue())); if (subProps.isEmpty()) { addIssue(KEY, translate("OAR038.error-required-one-property"), entry.getValue().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 2cc3ba66..7422bc70 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR085OpenAPIVersionCheck.java @@ -29,15 +29,14 @@ public class OAR085OpenAPIVersionCheck extends BaseCheck { ) private String validVersionsStr = DEFAULT_VALID_VERSIONS; - private final List validVersions = Arrays.asList(validVersionsStr.split(",")); - @Override protected void visitFile(JsonNode root) { JsonNode swaggerNode = root.get("swagger"); JsonNode openapiNode = root.get("openapi"); String version = getVersion(swaggerNode, openapiNode); - + List validVersions = Arrays.asList(validVersionsStr.split(",")); + if (version == null || !validVersions.contains(version)) { addIssue(KEY, translate(MESSAGE, version), root.key()); } diff --git a/src/test/resources/checks/v2/operations/OAR014/plain.json b/src/test/resources/checks/v2/operations/OAR014/plain.json index 7561c445..1dc0ee29 100644 --- a/src/test/resources/checks/v2/operations/OAR014/plain.json +++ b/src/test/resources/checks/v2/operations/OAR014/plain.json @@ -95,7 +95,7 @@ } } }, - "/one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six": { # Noncompliant {{OAR014: Resources depth level should be smaller}} + "/one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six": { "get": { "responses": { "default": { @@ -104,7 +104,7 @@ } } }, - "/one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six/{six}": { # Noncompliant {{OAR014: Resources depth level should be smaller}} + "/one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six/{six}": { "get": { "responses": { "default": { diff --git a/src/test/resources/checks/v2/operations/OAR014/plain.yaml b/src/test/resources/checks/v2/operations/OAR014/plain.yaml index abae063e..5647b610 100644 --- a/src/test/resources/checks/v2/operations/OAR014/plain.yaml +++ b/src/test/resources/checks/v2/operations/OAR014/plain.yaml @@ -53,12 +53,12 @@ paths: responses: default: description: Ok - /one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six: # Noncompliant {{OAR014: Resources depth level should be smaller}} + /one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six: # depth 6, out of the 4-5 non-suggested range (covered separately by OAR015) get: responses: default: description: Ok - /one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six/{six}: # Noncompliant {{OAR014: Resources depth level should be smaller}} + /one/{one}/two/{two}/three/{three}/four/{four}/five/{five}/six/{six}: get: responses: default: From 724c9ab2744116a5b7952f28aec244f42d6d9661 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Fri, 10 Jul 2026 16:32:08 -0500 Subject: [PATCH 07/10] fix: oar004, oar040, oar082, oar019, oar020 and oar021 rule properties --- .../wso2/AbstractPatternWso2ScopesCheck.java | 4 +- .../wso2/OAR004ValidWso2ScopesRolesCheck.java | 5 ++ .../OAR040StandardWso2ScopesNameCheck.java | 5 ++ ...AbstractCollectionQueryParameterCheck.java | 21 +----- .../AbstractQueryParameterCheck.java | 68 +++++++++---------- .../OAR019SelectParameterCheck.java | 39 +++++++++++ .../OAR020ExpandParameterCheck.java | 39 +++++++++++ .../OAR021ExcludeParameterCheck.java | 39 +++++++++++ .../OAR022OrderbyParameterCheck.java | 25 +++++++ .../parameters/OAR023TotalParameterCheck.java | 25 +++++++ .../parameters/OAR024StartParameterCheck.java | 25 +++++++ .../parameters/OAR025LimitParameterCheck.java | 25 +++++++ .../OAR028FilterParameterCheck.java | 30 ++++++-- .../OAR082BinaryOrByteFormatCheck.java | 7 +- .../OAR019SelectParameterCheckTest.java | 7 +- .../OAR020ExpandParameterCheckTest.java | 7 +- .../OAR021ExcludeParameterCheckTest.java | 7 +- .../checks/v3/parameters/OAR019/excluded.yaml | 2 +- .../v31/parameters/OAR019/excluded.yaml | 2 +- .../v32/parameters/OAR019/excluded.yaml | 2 +- 20 files changed, 312 insertions(+), 72 deletions(-) diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java index fb482421..8e5fb60e 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java @@ -13,7 +13,6 @@ public abstract class AbstractPatternWso2ScopesCheck extends AbstractWso2ScopesC protected final String defaultPatternValue; protected Pattern pattern; - private String patternStr; protected AbstractPatternWso2ScopesCheck(String key, String message, String fieldName, String defaultPatternValue) { this.ruleKey = key; @@ -22,8 +21,11 @@ protected AbstractPatternWso2ScopesCheck(String key, String message, String fiel this.defaultPatternValue = defaultPatternValue; } + protected abstract String getPatternStr(); + @Override protected void visitFile(JsonNode root) { + String patternStr = getPatternStr(); pattern = Pattern.compile(patternStr != null ? patternStr : defaultPatternValue); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java index 7cb8c273..8e520b51 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java @@ -19,4 +19,9 @@ public class OAR004ValidWso2ScopesRolesCheck extends AbstractPatternWso2ScopesCh public OAR004ValidWso2ScopesRolesCheck() { super(KEY, MESSAGE, "roles", DEFAULT_PATTERN_VALUE); } + + @Override + protected String getPatternStr() { + return patternStr; + } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java index bd67e0b1..ee3bd39b 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java @@ -19,4 +19,9 @@ public class OAR040StandardWso2ScopesNameCheck extends AbstractPatternWso2Scopes public OAR040StandardWso2ScopesNameCheck() { super(KEY, MESSAGE, "name", DEFAULT_PATTERN_VALUE); } + + @Override + protected String getPatternStr() { + return patternStr; + } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractCollectionQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractCollectionQueryParameterCheck.java index 0eef8f02..ab9c31dc 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractCollectionQueryParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractCollectionQueryParameterCheck.java @@ -1,30 +1,13 @@ package apiaddicts.sonar.openapi.checks.parameters; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - public abstract class AbstractCollectionQueryParameterCheck extends AbstractQueryParameterCheck { protected AbstractCollectionQueryParameterCheck( String ruleKey, String messageKey, - String parameterName, + String defaultParameterName, boolean applyToParameterizedPaths ) { - super(ruleKey, messageKey, parameterName, applyToParameterizedPaths); - } - - @Override - public void visitNode(JsonNode node) { - if (!"get".equals(node.key().getTokenValue())) return; - - String path = getPath(node); - - if (endsWithPathParam(path)) return; - if (path.contains("/me/") || path.endsWith("/me")) return; - if (path.contains("status") || path.contains("health") || path.contains("ping")) return; - - if (!hasParameterInNode(node)) { - addIssue(ruleKey, translate(messageKey, parameterName), node.key()); - } + super(ruleKey, messageKey, defaultParameterName, applyToParameterizedPaths); } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java index c5f31e28..4f0d406f 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java @@ -4,59 +4,54 @@ import com.google.common.collect.ImmutableSet; import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.AstNodeType; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; 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 org.sonar.check.RuleProperty; public abstract class AbstractQueryParameterCheck extends BaseCheck { protected static final String DEFAULT_PATH = "/examples"; protected static final String PATH_STRATEGY = "/include"; - private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; - private static final String PATH_STRATEGY_INCLUDE = "/include"; + protected static final String PATH_STRATEGY_EXCLUDE = "/exclude"; + protected static final String PATH_STRATEGY_INCLUDE = "/include"; protected final String ruleKey; protected final String messageKey; - protected String parameterName; + protected final String defaultParameterName; protected final boolean applyToParameterizedPaths; - protected Set paths; + protected List paths; protected JsonNode rootNode; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - protected String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - protected String pathCheckStrategy = PATH_STRATEGY; - protected AbstractQueryParameterCheck( String ruleKey, String messageKey, - String parameterName, + String defaultParameterName, boolean applyToParameterizedPaths ) { this.ruleKey = ruleKey; this.messageKey = messageKey; - this.parameterName = parameterName; + this.defaultParameterName = defaultParameterName; this.applyToParameterizedPaths = applyToParameterizedPaths; } + protected abstract String getPathsStr(); + + protected abstract String getPathCheckStrategy(); + + protected String getParameterName() { + return defaultParameterName; + } + @Override public Set subscribedKinds() { return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION, OpenApi32Grammar.OPERATION); @@ -65,7 +60,7 @@ public Set subscribedKinds() { @Override protected void visitFile(JsonNode root) { this.rootNode = root; - paths = parsePaths(pathsStr); + paths = parsePaths(getPathsStr()); super.visitFile(root); } @@ -86,7 +81,7 @@ public void visitNode(JsonNode node) { if (shouldIncludePath(path) && !hasParameter) { addIssue( ruleKey, - translate(messageKey, parameterName), + translate(messageKey, getParameterName()), node.key() ); } @@ -118,7 +113,7 @@ protected boolean hasNamedRefParameter(JsonNode parameterNode) { if (refParameterNode != null) { JsonNode nameNode = refParameterNode.get("name"); JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); + return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && getParameterName().equals(nameNode.getTokenValue()); } return false; } @@ -126,7 +121,7 @@ protected boolean hasNamedRefParameter(JsonNode parameterNode) { protected boolean hasDirectParameter(JsonNode parameterNode) { JsonNode nameNode = parameterNode.get("name"); JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); + return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && getParameterName().equals(nameNode.getTokenValue()); } protected String getPath(JsonNode node) { @@ -143,12 +138,13 @@ protected String getPath(JsonNode node) { protected boolean shouldIncludePath(String path) { if (paths.isEmpty()) { - return pathCheckStrategy.equals(PATH_STRATEGY_EXCLUDE); + return getPathCheckStrategy().equals(PATH_STRATEGY_EXCLUDE); } - if (pathCheckStrategy.equals(PATH_STRATEGY_EXCLUDE)) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals(PATH_STRATEGY_INCLUDE)) { - return paths.contains(path); + boolean matchesList = paths.stream().anyMatch(p -> p.matcher(path).find()); + if (getPathCheckStrategy().equals(PATH_STRATEGY_EXCLUDE)) { + return !matchesList; + } else if (getPathCheckStrategy().equals(PATH_STRATEGY_INCLUDE)) { + return matchesList; } return false; } @@ -161,13 +157,15 @@ protected boolean endsWithPathParam(String path) { return last.matches("^\\{[^}]+\\}$"); } - protected Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { + protected List parsePaths(String pathsStr) { + if (pathsStr != null && !pathsStr.trim().isEmpty()) { return Arrays.stream(pathsStr.split(",")) .map(String::trim) - .collect(Collectors.toSet()); + .filter(s -> !s.isEmpty()) + .map(Pattern::compile) + .collect(Collectors.toList()); } else { - return new HashSet<>(); + return new ArrayList<>(); } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java index 262bc099..37857a5f 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR019SelectParameterCheck.KEY) public class OAR019SelectParameterCheck extends AbstractQueryParameterCheck { @@ -8,6 +9,29 @@ public class OAR019SelectParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR019"; private static final String MESSAGE = "OAR019.error"; private static final String PARAM_NAME = "$select"; + private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String PATH_STRATEGY = "/exclude"; + + @RuleProperty( + key = "parameterName", + description = "Name of the 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", + defaultValue = DEFAULT_PATHS + ) + private String pathsOverride = DEFAULT_PATHS; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategyOverride = PATH_STRATEGY; public OAR019SelectParameterCheck() { super( @@ -18,4 +42,19 @@ public OAR019SelectParameterCheck() { ); } + @Override + protected String getParameterName() { + return parameterNameOverride; + } + + @Override + protected String getPathsStr() { + return pathsOverride; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategyOverride; + } + } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java index fdac58e8..05369e76 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR020ExpandParameterCheck.KEY) public class OAR020ExpandParameterCheck extends AbstractCollectionQueryParameterCheck { @@ -8,8 +9,46 @@ public class OAR020ExpandParameterCheck extends AbstractCollectionQueryParameter public static final String KEY = "OAR020"; private static final String MESSAGE = "OAR020.error"; private static final String PARAM_NAME = "$expand"; + private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String PATH_STRATEGY = "/exclude"; + + @RuleProperty( + key = "parameterName", + description = "Name of the 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", + defaultValue = DEFAULT_PATHS + ) + private String pathsOverride = DEFAULT_PATHS; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategyOverride = PATH_STRATEGY; public OAR020ExpandParameterCheck() { super(KEY, MESSAGE, PARAM_NAME, false); } + + @Override + protected String getParameterName() { + return parameterNameOverride; + } + + @Override + protected String getPathsStr() { + return pathsOverride; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategyOverride; + } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java index a8b52bfc..7b09eddb 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR021ExcludeParameterCheck.KEY) public class OAR021ExcludeParameterCheck extends AbstractCollectionQueryParameterCheck { @@ -8,8 +9,46 @@ public class OAR021ExcludeParameterCheck extends AbstractCollectionQueryParamete public static final String KEY = "OAR021"; private static final String MESSAGE = "OAR021.error"; private static final String PARAM_NAME = "$exclude"; + private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String PATH_STRATEGY = "/exclude"; + + @RuleProperty( + key = "parameterName", + description = "Name of the 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", + defaultValue = DEFAULT_PATHS + ) + private String pathsOverride = DEFAULT_PATHS; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategyOverride = PATH_STRATEGY; public OAR021ExcludeParameterCheck() { super(KEY, MESSAGE, PARAM_NAME, false); } + + @Override + protected String getParameterName() { + return parameterNameOverride; + } + + @Override + protected String getPathsStr() { + return pathsOverride; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategyOverride; + } } \ No newline at end of file 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 0cbe783e..ea2c70fc 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR022OrderbyParameterCheck.KEY) public class OAR022OrderbyParameterCheck extends AbstractQueryParameterCheck { @@ -9,6 +10,20 @@ public class OAR022OrderbyParameterCheck extends AbstractQueryParameterCheck { private static final String MESSAGE = "OAR022.error"; private static final String PARAM_NAME = "$orderby"; + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + private String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategy = PATH_STRATEGY; + public OAR022OrderbyParameterCheck() { super( KEY, @@ -17,4 +32,14 @@ public OAR022OrderbyParameterCheck() { false ); } + + @Override + protected String getPathsStr() { + return pathsStr; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategy; + } } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java index 8337d644..9cffb226 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR023TotalParameterCheck.KEY) public class OAR023TotalParameterCheck extends AbstractQueryParameterCheck { @@ -9,6 +10,20 @@ public class OAR023TotalParameterCheck extends AbstractQueryParameterCheck { private static final String MESSAGE = "OAR023.error"; private static final String PARAM_NAME = "$total"; + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + private String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategy = PATH_STRATEGY; + public OAR023TotalParameterCheck() { super( KEY, @@ -17,4 +32,14 @@ public OAR023TotalParameterCheck() { true ); } + + @Override + protected String getPathsStr() { + return pathsStr; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategy; + } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java index aedcaafa..6da4d16b 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR024StartParameterCheck.KEY) public class OAR024StartParameterCheck extends AbstractQueryParameterCheck { @@ -9,6 +10,20 @@ public class OAR024StartParameterCheck extends AbstractQueryParameterCheck { private static final String MESSAGE = "OAR024.error"; private static final String PARAM_NAME = "$start"; + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + private String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategy = PATH_STRATEGY; + public OAR024StartParameterCheck() { super( KEY, @@ -17,4 +32,14 @@ public OAR024StartParameterCheck() { true ); } + + @Override + protected String getPathsStr() { + return pathsStr; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategy; + } } \ No newline at end of file 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 2ea5e207..8268f050 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi.checks.parameters; import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; @Rule(key = OAR025LimitParameterCheck.KEY) public class OAR025LimitParameterCheck extends AbstractQueryParameterCheck { @@ -9,6 +10,20 @@ public class OAR025LimitParameterCheck extends AbstractQueryParameterCheck { private static final String MESSAGE = "OAR025.error"; private static final String PARAM_NAME = "$limit"; + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + private String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategy = PATH_STRATEGY; + public OAR025LimitParameterCheck() { super( KEY, @@ -17,4 +32,14 @@ public OAR025LimitParameterCheck() { false ); } + + @Override + protected String getPathsStr() { + return pathsStr; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategy; + } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR028FilterParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR028FilterParameterCheck.java index 703e56e9..f39117d0 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR028FilterParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR028FilterParameterCheck.java @@ -1,6 +1,5 @@ package apiaddicts.sonar.openapi.checks.parameters; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; @@ -18,13 +17,36 @@ public class OAR028FilterParameterCheck extends AbstractCollectionQueryParameter ) private String filterParamName = DEFAULT_PARAM_NAME; + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + private String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + private String pathCheckStrategy = PATH_STRATEGY; + public OAR028FilterParameterCheck() { super(KEY, MESSAGE, DEFAULT_PARAM_NAME, false); } @Override - protected void visitFile(JsonNode root) { - this.parameterName = filterParamName; - super.visitFile(root); + protected String getParameterName() { + return filterParamName; + } + + @Override + protected String getPathsStr() { + return pathsStr; + } + + @Override + protected String getPathCheckStrategy() { + return pathCheckStrategy; } } 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 0c831f4a..cbaf2ad1 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR082BinaryOrByteFormatCheck.java @@ -31,13 +31,18 @@ public class OAR082BinaryOrByteFormatCheck extends BaseCheck { ) private String fieldsApply = FIELDS_TO_APPLY; - private List fieldsList = Arrays.asList(fieldsApply.split(",")); + private List fieldsList; @Override public Set subscribedKinds() { return ImmutableSet.of(OpenApi2Grammar.SCHEMA, OpenApi2Grammar.PARAMETER, OpenApi3Grammar.SCHEMA, OpenApi31Grammar.SCHEMA, OpenApi32Grammar.SCHEMA); } + @Override + protected void visitFile(JsonNode root) { + fieldsList = Arrays.asList(fieldsApply.split(",")); + } + @Override public void visitNode(JsonNode node) { visitV2Node(node); diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java index 49359130..63785b90 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java @@ -117,8 +117,9 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(2); - assertParameterProperties("paths", "/examples", RuleParamType.STRING); - assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); + assertNumberOfParameters(3); + assertParameterProperties("parameterName", "$select", RuleParamType.STRING); + assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java index ab2f92a4..397f16b6 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java @@ -150,8 +150,9 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(2); - assertParameterProperties("paths", "/examples", RuleParamType.STRING); - assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); + assertNumberOfParameters(3); + assertParameterProperties("parameterName", "$expand", RuleParamType.STRING); + assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java index b758f663..72ee8a4a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java @@ -150,8 +150,9 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(2); - assertParameterProperties("paths", "/examples", RuleParamType.STRING); - assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); + assertNumberOfParameters(3); + assertParameterProperties("parameterName", "$exclude", RuleParamType.STRING); + assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/resources/checks/v3/parameters/OAR019/excluded.yaml b/src/test/resources/checks/v3/parameters/OAR019/excluded.yaml index 7758deac..5bb25ac8 100644 --- a/src/test/resources/checks/v3/parameters/OAR019/excluded.yaml +++ b/src/test/resources/checks/v3/parameters/OAR019/excluded.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: Swagger Petstore paths: - /another: + /status: get: parameters: - in: query diff --git a/src/test/resources/checks/v31/parameters/OAR019/excluded.yaml b/src/test/resources/checks/v31/parameters/OAR019/excluded.yaml index 8a1c820c..67391c40 100644 --- a/src/test/resources/checks/v31/parameters/OAR019/excluded.yaml +++ b/src/test/resources/checks/v31/parameters/OAR019/excluded.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: Swagger Petstore paths: - /another: + /status: get: parameters: - in: query diff --git a/src/test/resources/checks/v32/parameters/OAR019/excluded.yaml b/src/test/resources/checks/v32/parameters/OAR019/excluded.yaml index 8a15d474..387db41b 100644 --- a/src/test/resources/checks/v32/parameters/OAR019/excluded.yaml +++ b/src/test/resources/checks/v32/parameters/OAR019/excluded.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: Swagger Petstore paths: - /another: + /status: get: parameters: - in: query From f2cd11e9747c966e17fe11c50ba812c42694d8f2 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Tue, 14 Jul 2026 07:38:42 -0500 Subject: [PATCH 08/10] fix: oar019, oar020 and oar021 default value and solve for swagger v2 --- .../checks/parameters/AbstractQueryParameterCheck.java | 9 ++++++++- .../checks/parameters/OAR019SelectParameterCheck.java | 2 +- .../checks/parameters/OAR020ExpandParameterCheck.java | 2 +- .../checks/parameters/OAR021ExcludeParameterCheck.java | 2 +- .../parameters/OAR019SelectParameterCheckTest.java | 2 +- .../parameters/OAR020ExpandParameterCheckTest.java | 2 +- .../parameters/OAR021ExcludeParameterCheckTest.java | 2 +- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java index 4f0d406f..d8a22e54 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java @@ -157,12 +157,19 @@ protected boolean endsWithPathParam(String path) { return last.matches("^\\{[^}]+\\}$"); } + /** + * Each entry in {@code pathsStr} is a literal path segment (e.g. "/status"), not a regular + * expression — callers configure this like they would type a real path. It's matched as + * that literal segment appearing anywhere in the path, bounded by "/" or the end of the + * path, so "/status" matches "/status", "/api/status", and "/status/health" but not + * "/status-report" or "/user-status". + */ protected List parsePaths(String pathsStr) { if (pathsStr != null && !pathsStr.trim().isEmpty()) { return Arrays.stream(pathsStr.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) - .map(Pattern::compile) + .map(segment -> Pattern.compile(Pattern.quote(segment) + "(/|$)")) .collect(Collectors.toList()); } else { return new ArrayList<>(); diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java index 37857a5f..04a735df 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java @@ -9,7 +9,7 @@ public class OAR019SelectParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR019"; private static final String MESSAGE = "OAR019.error"; private static final String PARAM_NAME = "$select"; - private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String DEFAULT_PATHS = "/me,/health,/ping,/status"; private static final String PATH_STRATEGY = "/exclude"; @RuleProperty( diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java index 05369e76..a2b0c1c6 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java @@ -9,7 +9,7 @@ public class OAR020ExpandParameterCheck extends AbstractCollectionQueryParameter public static final String KEY = "OAR020"; private static final String MESSAGE = "OAR020.error"; private static final String PARAM_NAME = "$expand"; - private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String DEFAULT_PATHS = "/me,/health,/ping,/status"; private static final String PATH_STRATEGY = "/exclude"; @RuleProperty( diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java index 7b09eddb..346a3607 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java @@ -9,7 +9,7 @@ public class OAR021ExcludeParameterCheck extends AbstractCollectionQueryParamete public static final String KEY = "OAR021"; private static final String MESSAGE = "OAR021.error"; private static final String PARAM_NAME = "$exclude"; - private static final String DEFAULT_PATHS = "\\/me(\\/|$),status|health|ping"; + private static final String DEFAULT_PATHS = "/me,/health,/ping,/status"; private static final String PATH_STRATEGY = "/exclude"; @RuleProperty( diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java index 63785b90..87631f99 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java @@ -119,7 +119,7 @@ public void verifyRule() { public void verifyParameters() { assertNumberOfParameters(3); assertParameterProperties("parameterName", "$select", RuleParamType.STRING); - assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("paths", "/me,/health,/ping,/status", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java index 397f16b6..1be17b22 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java @@ -152,7 +152,7 @@ public void verifyRule() { public void verifyParameters() { assertNumberOfParameters(3); assertParameterProperties("parameterName", "$expand", RuleParamType.STRING); - assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("paths", "/me,/health,/ping,/status", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java index 72ee8a4a..da09cb4a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java @@ -152,7 +152,7 @@ public void verifyRule() { public void verifyParameters() { assertNumberOfParameters(3); assertParameterProperties("parameterName", "$exclude", RuleParamType.STRING); - assertParameterProperties("paths", "\\/me(\\/|$),status|health|ping", RuleParamType.STRING); + assertParameterProperties("paths", "/me,/health,/ping,/status", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file From ee7fddbb335d61e5ff1029996b4e480ac9ba5017 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Tue, 14 Jul 2026 08:19:21 -0500 Subject: [PATCH 09/10] update changelog --- CHANGELOG.md | 18 ++++++++++++++++++ pom.xml | 2 +- .../AbstractQueryParameterCheck.java | 7 ------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a910d7..21481194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ 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.5.0-beta-4] - 2026-07-14 + +### Fixed + +- OAR004 - ValidWso2ScopesRoles - Fixed a field-shadowing bug in the shared base class that made the `pattern` parameter have no effect. +- OAR014 - ResourceLevelWithinNonSuggestedRange - `matchLevel` ignored `maxLevel` entirely; the parameter is now applied. +- OAR019 - SelectParameterCheck - Added real support for `paths` and `pathValidationStrategy`, and re-added the `parameterName` parameter (removed in an earlier refactor). +- OAR020 - ExpandParameterCheck - Removed hardcoded path-exclusion logic that bypassed the configurable `paths` parameter; re-added `parameterName`. +- OAR021 - ExcludeParameterCheck - Removed hardcoded path-exclusion logic that bypassed the configurable `paths` parameter; re-added `parameterName`. +- OAR038 - StandardCreateResponse - The `dataNode` parameter was never read; the check always used its default value instead. +- OAR040 - StandardWso2ScopesName - Fixed the same field-shadowing bug as OAR004. +- OAR082 - BinaryOrByteFormat - `fields-to-apply` was read before Sonar injected its configured value, so the parameter had no effect. +- OAR085 - OpenAPIVersion - `valid-versions` was read before Sonar injected its configured value, so the parameter had no effect. + +### Changed + +- OAR019, OAR020, OAR021 - `paths` now takes plain path segments (e.g. `/status`) instead of a regular expression. Default excluded paths (`/me`, `/health`, `/ping`, `/status`) are now matched as real path segments instead of a loose substring, so a path like `/subscription-status-reports` is no longer wrongly excluded. + ## [1.5.0-beta-3] - 2026-07-08 ### Changed diff --git a/pom.xml b/pom.xml index ae54aede..161983e8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.5.0-beta-3 + 1.5.0-beta-4 sonar-plugin SonarQube OpenAPI Community Rules diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java index d8a22e54..7d4fb2d4 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java @@ -157,13 +157,6 @@ protected boolean endsWithPathParam(String path) { return last.matches("^\\{[^}]+\\}$"); } - /** - * Each entry in {@code pathsStr} is a literal path segment (e.g. "/status"), not a regular - * expression — callers configure this like they would type a real path. It's matched as - * that literal segment appearing anywhere in the path, bounded by "/" or the end of the - * path, so "/status" matches "/status", "/api/status", and "/status/health" but not - * "/status-report" or "/user-status". - */ protected List parsePaths(String pathsStr) { if (pathsStr != null && !pathsStr.trim().isEmpty()) { return Arrays.stream(pathsStr.split(",")) From f8a2367f6c89aa0bf0dc60bbc28f8bddd4e8328f Mon Sep 17 00:00:00 2001 From: Sebastian Diaz Torres Date: Tue, 28 Jul 2026 00:57:58 -0500 Subject: [PATCH 10/10] feat: Release 1.5.0 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ pom.xml | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21481194..888e9717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,37 @@ 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.5.0] - 2026-07-28 + +### Added + +- OAR022 - OrderbyParameterCheck - Added `single-resource` test cases (v2, v3, v31, v32) verifying that paths ending with a path parameter (e.g. `/examples/{id}`) are correctly excluded by `applyToParameterizedPaths = false`. +- OAR025 - LimitParameterCheck - Added `single-resource` test cases (v2, v3, v31, v32) verifying that paths ending with a path parameter (e.g. `/examples/{id}`) are correctly excluded by `applyToParameterizedPaths = false`. +- OAR031 - ExamplesCheck - Per-level configuration via rule parameters `validate-response`, `validate-request-body`, `validate-parameter` and `validate-property` (all `true` by default); each level can be disabled independently. + +### Fixed + +- OAR017 - ResourcePathCheck - Added `delete` to the `exclude_patterns` default (now `get,me,search,delete`); paths ending with `/delete` (e.g. `/orders/delete`, `/orders/{orderId}/delete`) are now treated as pseudo-parameters and no longer trigger the alternation rule. +- OAR020 - ExpandParameterCheck - Fixed `verifyInV2PathEndingWithParam` test method that was incorrectly calling `verifyV3("with-param")` instead of `verifyV2("with-param")`; the Swagger 2.0 `with-param` test fixtures are now correctly exercised in v2 mode. +- OAR021 - ExcludeParameterCheck - Fixed `verifyInV2PathEndingWithParam` test, now correctly calls `verifyV2("with-param")`. +- OAR044 - MediaTypeCheck - Fixed `MEDIA_RANGE_PATTERN` to allow `*/*` (full wildcard) as a valid OAP3 media range; the type component now accepts `*` in addition to RFC 6838 type names. Added test coverage for vendor-specific types (`application/vnd.ms-excel`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`, `application/ld+json`, `application/vnd.github+json`). +- OAR004 - ValidWso2ScopesRoles - Fixed a field-shadowing bug in the shared base class that made the `pattern` parameter have no effect. +- OAR014 - ResourceLevelWithinNonSuggestedRange - `matchLevel` ignored `maxLevel` entirely; the parameter is now applied. +- OAR019 - SelectParameterCheck - Added real support for `paths` and `pathValidationStrategy`, and re-added the `parameterName` parameter (removed in an earlier refactor). +- OAR020 - ExpandParameterCheck - Removed hardcoded path-exclusion logic that bypassed the configurable `paths` parameter; re-added `parameterName`. +- OAR021 - ExcludeParameterCheck - Removed hardcoded path-exclusion logic that bypassed the configurable `paths` parameter; re-added `parameterName`. +- OAR038 - StandardCreateResponse - The `dataNode` parameter was never read; the check always used its default value instead. +- OAR040 - StandardWso2ScopesName - Fixed the same field-shadowing bug as OAR004. +- OAR082 - BinaryOrByteFormat - `fields-to-apply` was read before Sonar injected its configured value, so the parameter had no effect. +- OAR085 - OpenAPIVersion - `valid-versions` was read before Sonar injected its configured value, so the parameter had no effect. + +### Changed + +- OAR019, OAR020, OAR021 - `paths` now takes plain path segments (e.g. `/status`) instead of a regular expression. Default excluded paths (`/me`, `/health`, `/ping`, `/status`) are now matched as real path segments instead of a loose substring, so a path like `/subscription-status-reports` is no longer wrongly excluded. +- OAR037 - StringFormatCheck - Reclassified as a security rule (`VULNERABILITY`, tag `safety`, keeping its existing `format` rule group/package). String schemas must now declare a valid `format`, or — when no `format` is declared — a non-empty, syntactically valid `pattern`; schemas with neither a valid `format` nor a valid `pattern` are reported. +- OAR037 - StringFormatCheck - Rule no longer fires when a string schema omits the `format` field entirely; it only fires when `format` is present but not a recognized value. +- OAR031 - ExamplesCheck - Examples are now validated as four **independent** levels (response, request body, parameter, property). The response/request-body/parameter levels require an example declared at the media-type or schema **root** (non-recursive); examples nested inside schema properties no longer satisfy them. Aligns OAR031 with the Spectral ruleset (identical findings on the same document) and is stricter than before, so existing specs may surface new findings. + ## [1.5.0-beta-4] - 2026-07-14 ### Fixed diff --git a/pom.xml b/pom.xml index 161983e8..55865267 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.5.0-beta-4 + 1.5.0 sonar-plugin SonarQube OpenAPI Community Rules