From 07fc048a24269e032f3768a1d2829c335a5edbce Mon Sep 17 00:00:00 2001 From: apiquality-support Date: Wed, 1 Apr 2026 07:59:59 -0500 Subject: [PATCH 1/8] fix: Jacoco config for sonar --- pom.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a89ab7ec..ccab886a 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ 0.8.6 3.7.0.1746 jacoco - ${project.basedir}/../target/jacoco.exec + ${project.basedir}/target/site/jacoco/jacoco.xml java @@ -266,10 +266,6 @@ org.jacoco jacoco-maven-plugin ${jacoco.maven.plugin.version} - - ${sonar.jacoco.reportPaths} - true - agent @@ -277,6 +273,13 @@ prepare-agent + + report + verify + + report + + From 0e71dbade31882328942dd665f0226f267a7ecfa Mon Sep 17 00:00:00 2001 From: Sebastian Diaz Torres Date: Wed, 1 Apr 2026 09:20:11 -0500 Subject: [PATCH 2/8] fix: github workflow --- .github/workflows/maven.yml | 10 ++++++++-- pom.xml | 6 ++++++ sonar-project.properties | 2 ++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 6031fcd7..c892b322 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -13,14 +13,20 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for SonarCloud to get full git history - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml + - name: Build, Test and Coverage + run: mvn -B verify --file pom.xml + - name: SonarCloud Analysis + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn sonar:sonar -Dsonar.token=${{ secrets.SONAR_TOKEN }} # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - name: Update dependency graph diff --git a/pom.xml b/pom.xml index ccab886a..3130927b 100644 --- a/pom.xml +++ b/pom.xml @@ -262,6 +262,12 @@ + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar.maven.plugin.version} + + org.jacoco jacoco-maven-plugin diff --git a/sonar-project.properties b/sonar-project.properties index 222b77be..a77fa934 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,2 +1,4 @@ +sonar.organization=apiaddicts +sonar.projectKey=apiaddicts_sonaropenapi-rules sonar.exclusions=**/*.html,**/*.json sonar.cpd.exclusions=**/*.html,**/*.json \ No newline at end of file From c7fc892cdaffa21f14991b7f614b052b0ebc8bb4 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Wed, 1 Apr 2026 16:02:14 -0500 Subject: [PATCH 3/8] feat: enhance schemas check with additional tests for coverage and examples --- .../schemas/OAR080SecuritySchemasCheck.java | 10 +- .../security/AbstractPropertiesCheck.java | 36 ------- .../openapi/OpenAPICustomPluginTest.java | 31 ++++++ .../OpenAPICustomProfileDefinitionTest.java | 26 +++++ .../OpenAPICustomRuleRepositoryTest.java | 22 ++++ .../OpenAPICustomRulesDefinitionTest.java | 29 +++++ ...OAR029StandardResponseSchemaCheckTest.java | 31 ++++++ .../OAR080SecuritySchemasCheckTest.java | 101 ++++++++++++++++++ .../checks/v2/schemas/OAR080/.gitkeep | 0 .../schemas/OAR080/empty-global-security.json | 27 +++++ .../schemas/OAR080/empty-global-security.yaml | 17 +++ .../v2/schemas/OAR080/empty-security.json | 38 +++++++ .../v2/schemas/OAR080/empty-security.yaml | 24 +++++ .../v2/schemas/OAR080/global-security.json | 48 +++++++++ .../v2/schemas/OAR080/global-security.yaml | 49 +++++++++ .../v2/schemas/OAR080/wrong-scheme.json | 61 +++++++++++ .../v2/schemas/OAR080/wrong-scheme.yaml | 40 +++++++ .../checks/v3/schemas/OAR080/.gitkeep | 0 .../schemas/OAR080/empty-global-security.json | 20 ++++ .../schemas/OAR080/empty-global-security.yaml | 12 +++ .../v3/schemas/OAR080/empty-security.json | 31 ++++++ .../v3/schemas/OAR080/empty-security.yaml | 19 ++++ .../v3/schemas/OAR080/global-security.json | 53 +++++++++ .../v3/schemas/OAR080/global-security.yaml | 51 +++++++++ .../v3/schemas/OAR080/wrong-scheme.json | 54 ++++++++++ .../v3/schemas/OAR080/wrong-scheme.yaml | 35 ++++++ 26 files changed, 822 insertions(+), 43 deletions(-) delete mode 100644 src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPropertiesCheck.java create mode 100644 src/test/java/apiaddicts/sonar/openapi/OpenAPICustomPluginTest.java create mode 100644 src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java create mode 100644 src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRuleRepositoryTest.java create mode 100644 src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java delete mode 100644 src/test/resources/checks/v2/schemas/OAR080/.gitkeep create mode 100644 src/test/resources/checks/v2/schemas/OAR080/empty-global-security.json create mode 100644 src/test/resources/checks/v2/schemas/OAR080/empty-global-security.yaml create mode 100644 src/test/resources/checks/v2/schemas/OAR080/empty-security.json create mode 100644 src/test/resources/checks/v2/schemas/OAR080/empty-security.yaml create mode 100644 src/test/resources/checks/v2/schemas/OAR080/global-security.json create mode 100644 src/test/resources/checks/v2/schemas/OAR080/global-security.yaml create mode 100644 src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.json create mode 100644 src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.yaml delete mode 100644 src/test/resources/checks/v3/schemas/OAR080/.gitkeep create mode 100644 src/test/resources/checks/v3/schemas/OAR080/empty-global-security.json create mode 100644 src/test/resources/checks/v3/schemas/OAR080/empty-global-security.yaml create mode 100644 src/test/resources/checks/v3/schemas/OAR080/empty-security.json create mode 100644 src/test/resources/checks/v3/schemas/OAR080/empty-security.yaml create mode 100644 src/test/resources/checks/v3/schemas/OAR080/global-security.json create mode 100644 src/test/resources/checks/v3/schemas/OAR080/global-security.yaml create mode 100644 src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.json create mode 100644 src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.yaml diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheck.java index b3021139..6b22172d 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheck.java @@ -49,7 +49,7 @@ public Set subscribedKinds() { @Override protected void visitFile(JsonNode root) { JsonNode security = root.get("security"); - hasGlobalSecurity = !(security.isMissing() || security.isNull() || security.elements().isEmpty()); + hasGlobalSecurity = !(security.isMissing() || security.elements().isEmpty()); expectedSecuritySchemes = Arrays.stream(expectedSecurityScheme.split(",")) .map(String::trim) @@ -61,11 +61,7 @@ protected void visitFile(JsonNode root) { @Override public void visitNode(JsonNode node) { if (hasGlobalSecurity) return; - - if (node.is(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, - OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION)) { - visitOperationNode(node); - } + visitOperationNode(node); } private void visitOperationNode(JsonNode node) { @@ -75,7 +71,7 @@ private void visitOperationNode(JsonNode node) { } JsonNode security = node.get("security"); - if (security.isMissing() || security.isNull() || security.elements().isEmpty()) { + if (security.isMissing() || security.elements().isEmpty()) { addIssue(KEY, translate(MESSAGE), node.key()); return; } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPropertiesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPropertiesCheck.java deleted file mode 100644 index f02a75d9..00000000 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPropertiesCheck.java +++ /dev/null @@ -1,36 +0,0 @@ -package apiaddicts.sonar.openapi.checks.security; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; -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 apiaddicts.sonar.openapi.checks.BaseCheck; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.Set; - -public abstract class AbstractPropertiesCheck extends BaseCheck { - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.SCHEMA, OpenApi2Grammar.PARAMETER, OpenApi3Grammar.SCHEMA, OpenApi31Grammar.SCHEMA); - } - - @Override - public void visitNode(JsonNode node) { - visitV2Node(node); - } - - private void visitV2Node(JsonNode node) { - JsonNode propertiesNode = node.get("properties"); - String properties = propertiesNode.getTokenValue(); - JsonNode typeNode = node.get("type"); - String type = typeNode.getTokenValue(); - JsonNode formatNode = node.get("format"); - String format = formatNode.isMissing() ? null : formatNode.getTokenValue(); - validate(type, format, properties, typeNode, propertiesNode); - } - - public abstract void validate(String type, String format, String properties, JsonNode typeNode, JsonNode propertiesNode); -} diff --git a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomPluginTest.java b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomPluginTest.java new file mode 100644 index 00000000..db247fca --- /dev/null +++ b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomPluginTest.java @@ -0,0 +1,31 @@ +package apiaddicts.sonar.openapi; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; +import org.sonar.api.Plugin; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarProduct; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.utils.Version; + +public class OpenAPICustomPluginTest { + + @Test + public void testDefine() { + OpenAPICustomPlugin plugin = new OpenAPICustomPlugin(); + SonarRuntime runtime = new SonarRuntime() { + @Override public Version getApiVersion() { return Version.create(9, 9); } + @Override public SonarProduct getProduct() { return SonarProduct.SONARQUBE; } + @Override public SonarQubeSide getSonarQubeSide() { return SonarQubeSide.SERVER; } + @Override public SonarEdition getEdition() { return SonarEdition.COMMUNITY; } + }; + Plugin.Context context = new Plugin.Context(runtime); + plugin.define(context); + assertThat(context.getExtensions()).hasSize(3); + assertThat(context.getExtensions()).contains( + OpenAPICustomProfileDefinition.class, + OpenAPICustomRulesDefinition.class, + OpenAPICustomRuleRepository.class); + } +} diff --git a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java new file mode 100644 index 00000000..36ba0eb3 --- /dev/null +++ b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java @@ -0,0 +1,26 @@ +package apiaddicts.sonar.openapi; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; + +import apiaddicts.sonar.openapi.checks.RulesLists; + +public class OpenAPICustomProfileDefinitionTest { + + @Test + public void testDefine() { + I18nContext.setLang("en"); + OpenAPICustomProfileDefinition profileDefinition = new OpenAPICustomProfileDefinition(); + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + profileDefinition.define(context); + BuiltInQualityProfile profile = context.profile("openapi", OpenAPICustomProfileDefinition.MY_COMPANY_WAY); + assertThat(profile).isNotNull(); + assertThat(profile.language()).isEqualTo("openapi"); + assertThat(profile.name()).isEqualTo(OpenAPICustomProfileDefinition.MY_COMPANY_WAY); + // OAR112 is a template rule and must be excluded from the profile + assertThat(profile.rules()).hasSize(RulesLists.getAllChecks().size() - 1); + assertThat(profile.rules().stream().noneMatch(r -> r.ruleKey().equals("OAR112"))).isTrue(); + } +} diff --git a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRuleRepositoryTest.java b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRuleRepositoryTest.java new file mode 100644 index 00000000..180b70cd --- /dev/null +++ b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRuleRepositoryTest.java @@ -0,0 +1,22 @@ +package apiaddicts.sonar.openapi; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +import apiaddicts.sonar.openapi.checks.RulesLists; + +public class OpenAPICustomRuleRepositoryTest { + + @Test + public void testRepositoryKey() { + OpenAPICustomRuleRepository repository = new OpenAPICustomRuleRepository(); + assertThat(repository.repositoryKey()).isEqualTo(OpenAPICustomRulesDefinition.REPOSITORY_KEY); + } + + @Test + public void testCheckClasses() { + OpenAPICustomRuleRepository repository = new OpenAPICustomRuleRepository(); + assertThat(repository.checkClasses()).isEqualTo(RulesLists.getAllChecks()); + assertThat(repository.checkClasses()).isNotEmpty(); + } +} diff --git a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRulesDefinitionTest.java b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRulesDefinitionTest.java index 7c4714bc..e507fcb3 100644 --- a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRulesDefinitionTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomRulesDefinitionTest.java @@ -1,6 +1,7 @@ package apiaddicts.sonar.openapi; import static org.assertj.core.api.Assertions.assertThat; +import java.lang.reflect.Method; import org.junit.Test; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.api.server.rule.RulesDefinition.Repository; @@ -20,4 +21,32 @@ public void testRepository() { assertThat(repository.language()).isEqualTo("openapi"); assertThat(repository.rules()).hasSize(RulesLists.getAllChecks().size()); } + + @Test + public void testGetPathWithSpanishLanguage() throws Exception { + I18nContext.setLang("es"); + OpenAPICustomRulesDefinition rulesDefinition = new OpenAPICustomRulesDefinition(); + + Method method = OpenAPICustomRulesDefinition.class.getDeclaredMethod("getPath", String.class); + method.setAccessible(true); + String path = (String) method.invoke(rulesDefinition, "security"); + + assertThat(path).contains("/es/"); + } + + @Test + public void testMarkAsTemplateWithNonExistentRule() throws Exception { + I18nContext.setLang("en"); + OpenAPICustomRulesDefinition rulesDefinition = new OpenAPICustomRulesDefinition(); + RulesDefinition.Context context = new RulesDefinition.Context(); + RulesDefinition.NewRepository newRepo = context.createRepository("test-repo", "openapi").setName("Test"); + + Method method = OpenAPICustomRulesDefinition.class.getDeclaredMethod( + "markAsTemplate", RulesDefinition.NewRepository.class, String.class); + method.setAccessible(true); + method.invoke(rulesDefinition, newRepo, "NON_EXISTENT_RULE"); + + newRepo.done(); + assertThat(context.repository("test-repo").rules()).isEmpty(); + } } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheckTest.java index f7feae0d..628456db 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR029StandardResponseSchemaCheckTest.java @@ -27,6 +27,21 @@ public void verifyInV2AllOf() { verifyV2("valid-all-of"); } + @Test + public void verifyInV2ValidAllOfMd() { + verifyV2("valid-all-of-md"); + } + + @Test + public void verifyInV2ValidMd() { + verifyV2("valid-md"); + } + + + @Test + public void verifyInV2ValidR() { + verifyV2("valid-r"); + } @Test public void verifyInV3() { @@ -38,6 +53,22 @@ public void verifyInV3AllOf() { verifyV3("valid-all-of"); } + @Test + public void verifyInV3ValidAllOfMd() { + verifyV3("valid-all-of-md"); + } + + @Test + public void verifyInV3ValidMd() { + verifyV3("valid-md"); + } + + @Test + public void verifyInV3ValidR() { + verifyV3("valid-r"); + } + + @Override public void verifyParameters() { assertNumberOfParameters(2); diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java new file mode 100644 index 00000000..3fb1fd2f --- /dev/null +++ b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java @@ -0,0 +1,101 @@ +package apiaddicts.sonar.openapi.checks.schemas; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.rule.RuleParamType; +import apiaddicts.sonar.openapi.BaseCheckTest; + +public class OAR080SecuritySchemasCheckTest extends BaseCheckTest { + + @Before + public void init() { + ruleName = "OAR080"; + check = new OAR080SecuritySchemasCheck(); + v2Path = getV2Path("schemas"); + v3Path = getV3Path("schemas"); + } + + // ---- V2 tests -------------------------------------------------------- + + @Test + public void verifyV2WithSecurity() { + verifyV2("with-security"); + } + + @Test + public void verifyV2WithoutSecurity() { + verifyV2("without-security"); + } + + @Test + public void verifyV2GlobalSecurity() { + verifyV2("global-security"); + } + + @Test + public void verifyV2WrongScheme() { + verifyV2("wrong-scheme"); + } + + @Test + public void verifyV2EmptySecurity() { + verifyV2("empty-security"); + } + + @Test + public void verifyV2EmptyGlobalSecurity() { + verifyV2("empty-global-security"); + } + + // ---- V3 tests -------------------------------------------------------- + + @Test + public void verifyV3WithSecurity() { + verifyV3("with-security"); + } + + @Test + public void verifyV3WithoutSecurity() { + verifyV3("without-security"); + } + + @Test + public void verifyV3GlobalSecurity() { + verifyV3("global-security"); + } + + @Test + public void verifyV3WrongScheme() { + verifyV3("wrong-scheme"); + } + + @Test + public void verifyV3EmptySecurity() { + verifyV3("empty-security"); + } + + @Test + public void verifyV3EmptyGlobalSecurity() { + verifyV3("empty-global-security"); + } + + // ---- Rule metadata --------------------------------------------------- + + @Override + public void verifyRule() { + assertRuleProperties( + "OAR080 - SecuritySchemas - The security scheme must be among those allowed by the organization and must be complete.", + RuleType.VULNERABILITY, + Severity.MAJOR, + tags("schemas") + ); + } + + @Override + public void verifyParameters() { + assertNumberOfParameters(1); + assertParameterProperties("expected-security-scheme", "oauth2, apiKey", RuleParamType.STRING); + } +} diff --git a/src/test/resources/checks/v2/schemas/OAR080/.gitkeep b/src/test/resources/checks/v2/schemas/OAR080/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.json b/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.json new file mode 100644 index 00000000..cf2c9091 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.json @@ -0,0 +1,27 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "security": [], + "securityDefinitions": { + "apiKey": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + }, + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.yaml b/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.yaml new file mode 100644 index 00000000..1711e583 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/empty-global-security.yaml @@ -0,0 +1,17 @@ +swagger: "2.0" +info: + version: "1.0.0" + title: "Sample API" +security: [] +securityDefinitions: + apiKey: + type: apiKey + name: X-API-Key + in: header +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v2/schemas/OAR080/empty-security.json b/src/test/resources/checks/v2/schemas/OAR080/empty-security.json new file mode 100644 index 00000000..ace8b8b4 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/empty-security.json @@ -0,0 +1,38 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "securityDefinitions": { + "apiKey": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + }, + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "security": [], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Create a new product", + "security": [], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/schemas/OAR080/empty-security.yaml b/src/test/resources/checks/v2/schemas/OAR080/empty-security.yaml new file mode 100644 index 00000000..389de1e9 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/empty-security.yaml @@ -0,0 +1,24 @@ +swagger: "2.0" +info: + version: "1.0.0" + title: "Sample API" +securityDefinitions: + apiKey: + type: apiKey + name: X-API-Key + in: header +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + security: [] + responses: + '200': + description: OK + /products: + post: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Create a new product + security: [] + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v2/schemas/OAR080/global-security.json b/src/test/resources/checks/v2/schemas/OAR080/global-security.json new file mode 100644 index 00000000..4b6dd680 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/global-security.json @@ -0,0 +1,48 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "security": [ + { "apiKey": [] }, + { "oauth2": [] } + ], + "securityDefinitions": { + "oauth2": { + "type": "oauth2", + "flow": "implicit", + "authorizationUrl": "https://example.com/oauth/authorize", + "scopes": { + "read": "Read access" + } + }, + "apiKey": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + }, + "paths": { + "/users": { + "get": { + "summary": "Get all users", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { + "summary": "Create a new product", + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/schemas/OAR080/global-security.yaml b/src/test/resources/checks/v2/schemas/OAR080/global-security.yaml new file mode 100644 index 00000000..4fa12698 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/global-security.yaml @@ -0,0 +1,49 @@ +swagger: "2.0" +info: + version: "1.0.0" + title: "Sample API" +security: + - apiKey: [] + - oauth2: [] +securityDefinitions: + oauth2: + type: oauth2 + flow: implicit + authorizationUrl: https://example.com/oauth/authorize + scopes: + read: Read access + apiKey: + type: apiKey + name: X-API-Key + in: header +paths: + /users: + get: + summary: Get all users + responses: + '200': + description: OK + /products: + post: + summary: Create a new product + responses: + '200': + description: OK + /orders: + put: + summary: Update an order + responses: + '200': + description: OK + /invoices: + delete: + summary: Delete an invoice + responses: + '200': + description: OK + /items: + patch: + summary: Patch an item + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.json b/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.json new file mode 100644 index 00000000..f15da5bb --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.json @@ -0,0 +1,61 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "securityDefinitions": { + "unknownScheme": { + "type": "apiKey", + "name": "X-Token", + "in": "header" + } + }, + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "security": [ + { + "unknownScheme": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Create a new product", + "security": [ + { + "apiKey": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/orders": { + "patch": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Patch an order", + "security": [ + { + "unknownScheme": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.yaml b/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.yaml new file mode 100644 index 00000000..14e347a0 --- /dev/null +++ b/src/test/resources/checks/v2/schemas/OAR080/wrong-scheme.yaml @@ -0,0 +1,40 @@ +swagger: "2.0" +info: + version: "1.0.0" + title: "Sample API" +securityDefinitions: + unknownScheme: + type: apiKey + name: X-Token + in: header +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + security: + - unknownScheme: [] + responses: + '200': + description: OK + /products: + post: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Create a new product + security: + - apiKey: [] + responses: + '200': + description: OK + /orders: + patch: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Patch an order + security: + - unknownScheme: [] + responses: + '200': + description: OK + /items: + head: + summary: Head items (non-security verb, no issue) + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/schemas/OAR080/.gitkeep b/src/test/resources/checks/v3/schemas/OAR080/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.json b/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.json new file mode 100644 index 00000000..81763b5c --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.json @@ -0,0 +1,20 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "security": [], + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.yaml b/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.yaml new file mode 100644 index 00000000..05fb3ba3 --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/empty-global-security.yaml @@ -0,0 +1,12 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample API +security: [] +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/schemas/OAR080/empty-security.json b/src/test/resources/checks/v3/schemas/OAR080/empty-security.json new file mode 100644 index 00000000..b7f31ef0 --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/empty-security.json @@ -0,0 +1,31 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "security": [], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Create a new product", + "security": [], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/schemas/OAR080/empty-security.yaml b/src/test/resources/checks/v3/schemas/OAR080/empty-security.yaml new file mode 100644 index 00000000..8821cacf --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/empty-security.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample API +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + security: [] + responses: + '200': + description: OK + /products: + post: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Create a new product + security: [] + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/schemas/OAR080/global-security.json b/src/test/resources/checks/v3/schemas/OAR080/global-security.json new file mode 100644 index 00000000..645a4a88 --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/global-security.json @@ -0,0 +1,53 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "security": [ + { "apiKey": [] }, + { "oauth2": [] } + ], + "components": { + "securitySchemes": { + "oauth2": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://example.com/oauth/authorize", + "scopes": { + "read": "Read access" + } + } + } + }, + "apiKey": { + "type": "apiKey", + "name": "X-API-Key", + "in": "header" + } + } + }, + "paths": { + "/users": { + "get": { + "summary": "Get all users", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { + "summary": "Create a new product", + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/schemas/OAR080/global-security.yaml b/src/test/resources/checks/v3/schemas/OAR080/global-security.yaml new file mode 100644 index 00000000..3205ed2a --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/global-security.yaml @@ -0,0 +1,51 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample API +security: + - apiKey: [] + - oauth2: [] +components: + securitySchemes: + oauth2: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/oauth/authorize + scopes: + read: Read access + apiKey: + type: apiKey + name: X-API-Key + in: header +paths: + /users: + get: + summary: Get all users + responses: + '200': + description: OK + /products: + post: + summary: Create a new product + responses: + '200': + description: OK + /orders: + put: + summary: Update an order + responses: + '200': + description: OK + /invoices: + delete: + summary: Delete an invoice + responses: + '200': + description: OK + /items: + patch: + summary: Patch an item + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.json b/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.json new file mode 100644 index 00000000..b042b2b4 --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.json @@ -0,0 +1,54 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Sample API" + }, + "paths": { + "/users": { + "get": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Get all users", + "security": [ + { + "unknownScheme": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/products": { + "post": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Create a new product", + "security": [ + { + "apiKey": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/orders": { + "patch": { # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + "summary": "Patch an order", + "security": [ + { + "unknownScheme": [] + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.yaml b/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.yaml new file mode 100644 index 00000000..86220d9f --- /dev/null +++ b/src/test/resources/checks/v3/schemas/OAR080/wrong-scheme.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample API +paths: + /users: + get: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Get all users + security: + - unknownScheme: [] + responses: + '200': + description: OK + /products: + post: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Create a new product + security: + - apiKey: [] + responses: + '200': + description: OK + /orders: + patch: # Noncompliant {{OAR080: The security scheme must be among those allowed by the organization and must be complete.}} + summary: Patch an order + security: + - unknownScheme: [] + responses: + '200': + description: OK + /items: + head: + summary: Head – non-security verb, ignored + responses: + '200': + description: OK From 0deb35970cbb2e49152dd74691ef51c27c75e2bd Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Wed, 1 Apr 2026 16:07:21 -0500 Subject: [PATCH 4/8] feat: update version, changelog and add tests for OAR112 checks --- CHANGELOG.md | 12 ++ README.md | 2 +- pom.xml | 2 +- .../OpenAPICustomProfileDefinitionTest.java | 2 +- .../checks/regex/OAR112RegexCheckTest.java | 159 ++++++++++++++++++ .../OAR080SecuritySchemasCheckTest.java | 6 - .../regex/OAR112/external-docs-invalid.json | 14 ++ .../regex/OAR112/external-docs-invalid.yaml | 10 ++ .../v2/regex/OAR112/external-docs-valid.json | 14 ++ .../v2/regex/OAR112/external-docs-valid.yaml | 10 ++ .../checks/v2/regex/OAR112/info-invalid.json | 11 ++ .../checks/v2/regex/OAR112/info-invalid.yaml | 8 + .../checks/v2/regex/OAR112/minimal.json | 10 ++ .../checks/v2/regex/OAR112/minimal.yaml | 7 + .../v2/regex/OAR112/missing-description.json | 10 ++ .../v2/regex/OAR112/missing-description.yaml | 7 + .../v2/regex/OAR112/operation-invalid.json | 21 +++ .../v2/regex/OAR112/operation-invalid.yaml | 13 ++ .../v2/regex/OAR112/operation-valid.json | 21 +++ .../v2/regex/OAR112/operation-valid.yaml | 13 ++ .../checks/v2/regex/OAR112/tags-invalid.json | 13 ++ .../checks/v2/regex/OAR112/tags-invalid.yaml | 9 + .../checks/v2/regex/OAR112/tags-valid.json | 13 ++ .../checks/v2/regex/OAR112/tags-valid.yaml | 9 + .../regex/OAR112/external-docs-invalid.json | 12 ++ .../regex/OAR112/external-docs-invalid.yaml | 8 + .../v3/regex/OAR112/external-docs-valid.json | 12 ++ .../v3/regex/OAR112/external-docs-valid.yaml | 8 + .../checks/v3/regex/OAR112/info-invalid.json | 9 + .../checks/v3/regex/OAR112/info-invalid.yaml | 6 + .../checks/v3/regex/OAR112/minimal.json | 8 + .../checks/v3/regex/OAR112/minimal.yaml | 5 + .../v3/regex/OAR112/missing-description.json | 8 + .../v3/regex/OAR112/missing-description.yaml | 5 + .../v3/regex/OAR112/operation-invalid.json | 19 +++ .../v3/regex/OAR112/operation-invalid.yaml | 11 ++ .../v3/regex/OAR112/operation-valid.json | 19 +++ .../v3/regex/OAR112/operation-valid.yaml | 11 ++ .../v3/regex/OAR112/parameters-invalid.json | 26 +++ .../v3/regex/OAR112/parameters-invalid.yaml | 16 ++ .../v3/regex/OAR112/parameters-valid.json | 26 +++ .../v3/regex/OAR112/parameters-valid.yaml | 16 ++ .../v3/regex/OAR112/servers-invalid.json | 12 ++ .../v3/regex/OAR112/servers-invalid.yaml | 8 + .../checks/v3/regex/OAR112/servers-valid.json | 12 ++ .../checks/v3/regex/OAR112/servers-valid.yaml | 8 + .../checks/v3/regex/OAR112/tags-invalid.json | 11 ++ .../checks/v3/regex/OAR112/tags-invalid.yaml | 7 + .../checks/v3/regex/OAR112/tags-valid.json | 11 ++ .../checks/v3/regex/OAR112/tags-valid.yaml | 7 + 50 files changed, 688 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/external-docs-valid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/external-docs-valid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/info-invalid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/info-invalid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/minimal.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/minimal.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/missing-description.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/missing-description.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/operation-invalid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/operation-invalid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/operation-valid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/operation-valid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/tags-invalid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/tags-invalid.yaml create mode 100644 src/test/resources/checks/v2/regex/OAR112/tags-valid.json create mode 100644 src/test/resources/checks/v2/regex/OAR112/tags-valid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/external-docs-valid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/external-docs-valid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/info-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/info-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/minimal.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/minimal.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/missing-description.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/missing-description.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/operation-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/operation-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/operation-valid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/operation-valid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/parameters-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/parameters-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/parameters-valid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/parameters-valid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/servers-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/servers-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/servers-valid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/servers-valid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/tags-invalid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/tags-invalid.yaml create mode 100644 src/test/resources/checks/v3/regex/OAR112/tags-valid.json create mode 100644 src/test/resources/checks/v3/regex/OAR112/tags-valid.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index f28ae6eb..3ce9b595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +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.3.4] - 2026-04-01 + +### Fixed + - OAR029 - StandardResponseSchemaCheck Test + - OAR080 - SecuritySchemasCheck Test + - OAR112 - RegexCheck Test + + - OpenAPICustomPlugin Test + - OpenAPICustomProfileDefinition Test + - OpenAPICustomRuleRepository Test + - OpenAPICustomRulesDefinition Test + ## [1.3.3] - 2026-03-20 ### Fixed diff --git a/README.md b/README.md index bdd2aba7..f7d3d3fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# 🛠️ sonaropenapi-rules ![Release](https://img.shields.io/badge/release-1.2.1-purple) ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=flat&logo=openjdk&logoColor=white) [![License: LGPL v3](https://img.shields.io/badge/license-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) +# 🛠️ sonaropenapi-rules ![Release](https://img.shields.io/badge/release-1.3.4-purple) ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=flat&logo=openjdk&logoColor=white) [![License: LGPL v3](https://img.shields.io/badge/license-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) This repository contains a set of custom SonarQube rules specifically designed to analyze and improve the quality of OpenAPI specifications. By integrating these rules, teams can ensure best practices, maintainability, and consistency in their API definitions. diff --git a/pom.xml b/pom.xml index b5a50e4c..e3fd5673 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.3.3 + 1.3.4 sonar-plugin SonarQube OpenAPI Community Rules diff --git a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java index 36ba0eb3..904cf042 100644 --- a/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/OpenAPICustomProfileDefinitionTest.java @@ -19,7 +19,7 @@ public void testDefine() { assertThat(profile).isNotNull(); assertThat(profile.language()).isEqualTo("openapi"); assertThat(profile.name()).isEqualTo(OpenAPICustomProfileDefinition.MY_COMPANY_WAY); - // OAR112 is a template rule and must be excluded from the profile + assertThat(profile.rules()).hasSize(RulesLists.getAllChecks().size() - 1); assertThat(profile.rules().stream().noneMatch(r -> r.ruleKey().equals("OAR112"))).isTrue(); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java index 2e48af14..ea21795a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java @@ -7,6 +7,8 @@ import org.sonar.api.server.rule.RuleParamType; import apiaddicts.sonar.openapi.BaseCheckTest; +import java.lang.reflect.Field; + public class OAR112RegexCheckTest extends BaseCheckTest { @Before @@ -17,6 +19,16 @@ public void init() { v3Path = getV3Path("regex"); } + private void setField(String fieldName, String value) { + try { + Field field = OAR112RegexCheck.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(check, value); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + @Test public void verifyInV2() { verifyV2("plain"); @@ -27,6 +39,153 @@ public void verifyInV3() { verifyV3("plain"); } + @Test + public void verifyInfoDescriptionInvalidInV2() { + verifyV2("info-invalid"); + } + + @Test + public void verifyInfoDescriptionInvalidInV3() { + verifyV3("info-invalid"); + } + + @Test + public void verifyServersDescriptionValidInV3() { + setField("nodes", "servers/description"); + verifyV3("servers-valid"); + } + + @Test + public void verifyServersDescriptionInvalidInV3() { + setField("nodes", "servers/description"); + verifyV3("servers-invalid"); + } + + @Test + public void verifyOperationSummaryValidInV3() { + setField("nodes", "paths/get/summary"); + verifyV3("operation-valid"); + } + + @Test + public void verifyOperationSummaryInvalidInV3() { + setField("nodes", "paths/get/summary"); + verifyV3("operation-invalid"); + } + + @Test + public void verifyOperationSummaryValidInV2() { + setField("nodes", "paths/get/summary"); + verifyV2("operation-valid"); + } + + @Test + public void verifyOperationSummaryInvalidInV2() { + setField("nodes", "paths/get/summary"); + verifyV2("operation-invalid"); + } + + @Test + public void verifyTagsNameValidInV3() { + setField("nodes", "tags/name"); + verifyV3("tags-valid"); + } + + @Test + public void verifyTagsNameInvalidInV3() { + setField("nodes", "tags/name"); + verifyV3("tags-invalid"); + } + + @Test + public void verifyTagsNameValidInV2() { + setField("nodes", "tags/name"); + verifyV2("tags-valid"); + } + + @Test + public void verifyTagsNameInvalidInV2() { + setField("nodes", "tags/name"); + verifyV2("tags-invalid"); + } + + @Test + public void verifyExternalDocsDescriptionValidInV3() { + setField("nodes", "externalDocs/description"); + verifyV3("external-docs-valid"); + } + + @Test + public void verifyExternalDocsDescriptionInvalidInV3() { + setField("nodes", "externalDocs/description"); + verifyV3("external-docs-invalid"); + } + + @Test + public void verifyExternalDocsDescriptionValidInV2() { + setField("nodes", "externalDocs/description"); + verifyV2("external-docs-valid"); + } + + @Test + public void verifyExternalDocsDescriptionInvalidInV2() { + setField("nodes", "externalDocs/description"); + verifyV2("external-docs-invalid"); + } + + @Test + public void verifyParametersDescriptionValidInV3() { + setField("nodes", "paths/get/parameters/description"); + verifyV3("parameters-valid"); + } + + @Test + public void verifyParametersDescriptionInvalidInV3() { + setField("nodes", "paths/get/parameters/description"); + verifyV3("parameters-invalid"); + } + + @Test + public void verifyBooleanTrueMissingFieldInV3() { + setField("nodes", "info/description"); + setField("valid", "true"); + verifyV3("missing-description"); + } + + @Test + public void verifyBooleanTrueMissingFieldInV2() { + setField("nodes", "info/description"); + setField("valid", "true"); + verifyV2("missing-description"); + } + + @Test + public void verifyBooleanFalseFieldPresentInV3() { + setField("nodes", "info/description"); + setField("valid", "false"); + verifyV3("info-invalid"); + } + + @Test + public void verifyBooleanFalseFieldPresentInV2() { + setField("nodes", "info/description"); + setField("valid", "false"); + verifyV2("info-invalid"); + } + + @Test + public void verifyBooleanFalseFieldAbsentInV3() { + setField("nodes", "info/description"); + setField("valid", "false"); + verifyV3("minimal"); + } + + @Test + public void verifyBooleanFalseFieldAbsentInV2() { + setField("nodes", "info/description"); + setField("valid", "false"); + verifyV2("minimal"); + } @Override public void verifyRule() { diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java index 3fb1fd2f..1f44678a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/schemas/OAR080SecuritySchemasCheckTest.java @@ -17,8 +17,6 @@ public void init() { v3Path = getV3Path("schemas"); } - // ---- V2 tests -------------------------------------------------------- - @Test public void verifyV2WithSecurity() { verifyV2("with-security"); @@ -49,8 +47,6 @@ public void verifyV2EmptyGlobalSecurity() { verifyV2("empty-global-security"); } - // ---- V3 tests -------------------------------------------------------- - @Test public void verifyV3WithSecurity() { verifyV3("with-security"); @@ -81,8 +77,6 @@ public void verifyV3EmptyGlobalSecurity() { verifyV3("empty-global-security"); } - // ---- Rule metadata --------------------------------------------------- - @Override public void verifyRule() { assertRuleProperties( diff --git a/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.json b/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.json new file mode 100644 index 00000000..5c13a402 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.json @@ -0,0 +1,14 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "externalDocs" : { + "description" : "lowercase external documentation", # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + "url" : "https://example.com" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.yaml b/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.yaml new file mode 100644 index 00000000..fb8623f7 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/external-docs-invalid.yaml @@ -0,0 +1,10 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +externalDocs: + description: lowercase external documentation # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + url: https://example.com +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.json b/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.json new file mode 100644 index 00000000..c1b4ce51 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.json @@ -0,0 +1,14 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "externalDocs" : { + "description" : "Valid external documentation", + "url" : "https://example.com" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.yaml b/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.yaml new file mode 100644 index 00000000..6deea6af --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/external-docs-valid.yaml @@ -0,0 +1,10 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +externalDocs: + description: Valid external documentation + url: https://example.com +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/info-invalid.json b/src/test/resources/checks/v2/regex/OAR112/info-invalid.json new file mode 100644 index 00000000..9c1c6937 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/info-invalid.json @@ -0,0 +1,11 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0", + "description" : "lowercase description" # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + }, + "host" : "api.example.com", + "basePath" : "/v1", + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/info-invalid.yaml b/src/test/resources/checks/v2/regex/OAR112/info-invalid.yaml new file mode 100644 index 00000000..94dc7fff --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/info-invalid.yaml @@ -0,0 +1,8 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 + description: lowercase description # Noncompliant {{OAR112: The field must start with an uppercase letter.}} +host: api.example.com +basePath: /v1 +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/minimal.json b/src/test/resources/checks/v2/regex/OAR112/minimal.json new file mode 100644 index 00000000..c20bf053 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/minimal.json @@ -0,0 +1,10 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/minimal.yaml b/src/test/resources/checks/v2/regex/OAR112/minimal.yaml new file mode 100644 index 00000000..a6e9a56d --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/minimal.yaml @@ -0,0 +1,7 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/missing-description.json b/src/test/resources/checks/v2/regex/OAR112/missing-description.json new file mode 100644 index 00000000..c6f37935 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/missing-description.json @@ -0,0 +1,10 @@ +{ + "swagger" : "2.0", + "info" : { # Noncompliant {{OAR112: Expected to find a value but didn't.}} + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/missing-description.yaml b/src/test/resources/checks/v2/regex/OAR112/missing-description.yaml new file mode 100644 index 00000000..88cf27ff --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/missing-description.yaml @@ -0,0 +1,7 @@ +swagger: '2.0' +info: # Noncompliant {{OAR112: Expected to find a value but didn't.}} + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/operation-invalid.json b/src/test/resources/checks/v2/regex/OAR112/operation-invalid.json new file mode 100644 index 00000000..a7747c6a --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/operation-invalid.json @@ -0,0 +1,21 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "paths" : { + "/test" : { + "get" : { + "summary" : "lowercase operation summary", # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/regex/OAR112/operation-invalid.yaml b/src/test/resources/checks/v2/regex/OAR112/operation-invalid.yaml new file mode 100644 index 00000000..e22dd817 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/operation-invalid.yaml @@ -0,0 +1,13 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +paths: + /test: + get: + summary: lowercase operation summary # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v2/regex/OAR112/operation-valid.json b/src/test/resources/checks/v2/regex/OAR112/operation-valid.json new file mode 100644 index 00000000..1edbf0f0 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/operation-valid.json @@ -0,0 +1,21 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "paths" : { + "/test" : { + "get" : { + "summary" : "Valid Operation Summary", + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v2/regex/OAR112/operation-valid.yaml b/src/test/resources/checks/v2/regex/OAR112/operation-valid.yaml new file mode 100644 index 00000000..a3698a64 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/operation-valid.yaml @@ -0,0 +1,13 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +paths: + /test: + get: + summary: Valid Operation Summary + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v2/regex/OAR112/tags-invalid.json b/src/test/resources/checks/v2/regex/OAR112/tags-invalid.json new file mode 100644 index 00000000..52a0562d --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/tags-invalid.json @@ -0,0 +1,13 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "tags" : [ { + "name" : "invalidTag" # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/tags-invalid.yaml b/src/test/resources/checks/v2/regex/OAR112/tags-invalid.yaml new file mode 100644 index 00000000..b657bee1 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/tags-invalid.yaml @@ -0,0 +1,9 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +tags: + - name: invalidTag # Noncompliant {{OAR112: The field must start with an uppercase letter.}} +paths: {} diff --git a/src/test/resources/checks/v2/regex/OAR112/tags-valid.json b/src/test/resources/checks/v2/regex/OAR112/tags-valid.json new file mode 100644 index 00000000..91d80136 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/tags-valid.json @@ -0,0 +1,13 @@ +{ + "swagger" : "2.0", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "host" : "api.example.com", + "basePath" : "/v1", + "tags" : [ { + "name" : "ValidTag" + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v2/regex/OAR112/tags-valid.yaml b/src/test/resources/checks/v2/regex/OAR112/tags-valid.yaml new file mode 100644 index 00000000..0bad0c33 --- /dev/null +++ b/src/test/resources/checks/v2/regex/OAR112/tags-valid.yaml @@ -0,0 +1,9 @@ +swagger: '2.0' +info: + title: Test API + version: 1.0.0 +host: api.example.com +basePath: /v1 +tags: + - name: ValidTag +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.json b/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.json new file mode 100644 index 00000000..17586e7f --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.json @@ -0,0 +1,12 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "externalDocs" : { + "description" : "lowercase external documentation", # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + "url" : "https://example.com" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.yaml new file mode 100644 index 00000000..587823ce --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/external-docs-invalid.yaml @@ -0,0 +1,8 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +externalDocs: + description: lowercase external documentation # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + url: https://example.com +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.json b/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.json new file mode 100644 index 00000000..b557673e --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.json @@ -0,0 +1,12 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "externalDocs" : { + "description" : "Valid external documentation", + "url" : "https://example.com" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.yaml b/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.yaml new file mode 100644 index 00000000..a273f219 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/external-docs-valid.yaml @@ -0,0 +1,8 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +externalDocs: + description: Valid external documentation + url: https://example.com +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/info-invalid.json b/src/test/resources/checks/v3/regex/OAR112/info-invalid.json new file mode 100644 index 00000000..1febb6b1 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/info-invalid.json @@ -0,0 +1,9 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0", + "description" : "lowercase description" # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/info-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/info-invalid.yaml new file mode 100644 index 00000000..32fbcf71 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/info-invalid.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 + description: lowercase description # Noncompliant {{OAR112: The field must start with an uppercase letter.}} +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/minimal.json b/src/test/resources/checks/v3/regex/OAR112/minimal.json new file mode 100644 index 00000000..2c084747 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/minimal.json @@ -0,0 +1,8 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/minimal.yaml b/src/test/resources/checks/v3/regex/OAR112/minimal.yaml new file mode 100644 index 00000000..f379c272 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/minimal.yaml @@ -0,0 +1,5 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/missing-description.json b/src/test/resources/checks/v3/regex/OAR112/missing-description.json new file mode 100644 index 00000000..f2549007 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/missing-description.json @@ -0,0 +1,8 @@ +{ + "openapi" : "3.0.3", + "info" : { # Noncompliant {{OAR112: Expected to find a value but didn't.}} + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/missing-description.yaml b/src/test/resources/checks/v3/regex/OAR112/missing-description.yaml new file mode 100644 index 00000000..db4ae777 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/missing-description.yaml @@ -0,0 +1,5 @@ +openapi: 3.0.3 +info: # Noncompliant {{OAR112: Expected to find a value but didn't.}} + title: Test API + version: 1.0.0 +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/operation-invalid.json b/src/test/resources/checks/v3/regex/OAR112/operation-invalid.json new file mode 100644 index 00000000..8bfbed2b --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/operation-invalid.json @@ -0,0 +1,19 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : { + "/test" : { + "get" : { + "summary" : "lowercase operation summary", # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/regex/OAR112/operation-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/operation-invalid.yaml new file mode 100644 index 00000000..0bc1243b --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/operation-invalid.yaml @@ -0,0 +1,11 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + summary: lowercase operation summary # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/regex/OAR112/operation-valid.json b/src/test/resources/checks/v3/regex/OAR112/operation-valid.json new file mode 100644 index 00000000..84ad7faa --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/operation-valid.json @@ -0,0 +1,19 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : { + "/test" : { + "get" : { + "summary" : "Valid Operation Summary", + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/regex/OAR112/operation-valid.yaml b/src/test/resources/checks/v3/regex/OAR112/operation-valid.yaml new file mode 100644 index 00000000..9b62b40c --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/operation-valid.yaml @@ -0,0 +1,11 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + summary: Valid Operation Summary + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.json b/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.json new file mode 100644 index 00000000..46495733 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.json @@ -0,0 +1,26 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : { + "/test" : { + "get" : { + "parameters" : [ { + "name" : "page", + "in" : "query", + "description" : "lowercase parameter description", # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + "schema" : { + "type" : "integer" + } + } ], + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.yaml new file mode 100644 index 00000000..86159506 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/parameters-invalid.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + parameters: + - name: page + in: query + description: lowercase parameter description # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + schema: + type: integer + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/regex/OAR112/parameters-valid.json b/src/test/resources/checks/v3/regex/OAR112/parameters-valid.json new file mode 100644 index 00000000..27af1750 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/parameters-valid.json @@ -0,0 +1,26 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "paths" : { + "/test" : { + "get" : { + "parameters" : [ { + "name" : "page", + "in" : "query", + "description" : "Valid parameter description", + "schema" : { + "type" : "integer" + } + } ], + "responses" : { + "200" : { + "description" : "OK" + } + } + } + } + } +} diff --git a/src/test/resources/checks/v3/regex/OAR112/parameters-valid.yaml b/src/test/resources/checks/v3/regex/OAR112/parameters-valid.yaml new file mode 100644 index 00000000..9ec6ebd6 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/parameters-valid.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + parameters: + - name: page + in: query + description: Valid parameter description + schema: + type: integer + responses: + '200': + description: OK diff --git a/src/test/resources/checks/v3/regex/OAR112/servers-invalid.json b/src/test/resources/checks/v3/regex/OAR112/servers-invalid.json new file mode 100644 index 00000000..c5cda036 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/servers-invalid.json @@ -0,0 +1,12 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "servers" : [ { + "url" : "https://api.example.com", + "description" : "lowercase server description" # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/servers-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/servers-invalid.yaml new file mode 100644 index 00000000..835f2b06 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/servers-invalid.yaml @@ -0,0 +1,8 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +servers: + - url: https://api.example.com + description: lowercase server description # Noncompliant {{OAR112: The field must start with an uppercase letter.}} +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/servers-valid.json b/src/test/resources/checks/v3/regex/OAR112/servers-valid.json new file mode 100644 index 00000000..735983f4 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/servers-valid.json @@ -0,0 +1,12 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "servers" : [ { + "url" : "https://api.example.com", + "description" : "Valid server description" + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/servers-valid.yaml b/src/test/resources/checks/v3/regex/OAR112/servers-valid.yaml new file mode 100644 index 00000000..511f93d7 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/servers-valid.yaml @@ -0,0 +1,8 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +servers: + - url: https://api.example.com + description: Valid server description +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/tags-invalid.json b/src/test/resources/checks/v3/regex/OAR112/tags-invalid.json new file mode 100644 index 00000000..5995f7ba --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/tags-invalid.json @@ -0,0 +1,11 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "tags" : [ { + "name" : "invalidTag" # Noncompliant {{OAR112: The field must start with an uppercase letter.}} + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/tags-invalid.yaml b/src/test/resources/checks/v3/regex/OAR112/tags-invalid.yaml new file mode 100644 index 00000000..48c06b71 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/tags-invalid.yaml @@ -0,0 +1,7 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +tags: + - name: invalidTag # Noncompliant {{OAR112: The field must start with an uppercase letter.}} +paths: {} diff --git a/src/test/resources/checks/v3/regex/OAR112/tags-valid.json b/src/test/resources/checks/v3/regex/OAR112/tags-valid.json new file mode 100644 index 00000000..f28ad145 --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/tags-valid.json @@ -0,0 +1,11 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Test API", + "version" : "1.0.0" + }, + "tags" : [ { + "name" : "ValidTag" + } ], + "paths" : {} +} diff --git a/src/test/resources/checks/v3/regex/OAR112/tags-valid.yaml b/src/test/resources/checks/v3/regex/OAR112/tags-valid.yaml new file mode 100644 index 00000000..045b483f --- /dev/null +++ b/src/test/resources/checks/v3/regex/OAR112/tags-valid.yaml @@ -0,0 +1,7 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +tags: + - name: ValidTag +paths: {} From 62aaa8afe4e5e60499554403cb6b4de1e95e2729 Mon Sep 17 00:00:00 2001 From: Melsy Huamani Date: Mon, 6 Apr 2026 10:25:52 -0500 Subject: [PATCH 5/8] feat: OAR112 test with validation scenarios --- .../sonar/openapi/checks/regex/OAR112RegexCheckTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java index ea21795a..924a317e 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/regex/OAR112RegexCheckTest.java @@ -150,6 +150,10 @@ public void verifyBooleanTrueMissingFieldInV3() { setField("nodes", "info/description"); setField("valid", "true"); verifyV3("missing-description"); + + setField("valid", "false"); + verifyV3("info-invalid"); + verifyV3("minimal"); } @Test @@ -157,6 +161,10 @@ public void verifyBooleanTrueMissingFieldInV2() { setField("nodes", "info/description"); setField("valid", "true"); verifyV2("missing-description"); + + setField("valid", "false"); + verifyV2("info-invalid"); + verifyV2("minimal"); } @Test From cb8e7341469d1ef16d6be14183777aec7820f88a Mon Sep 17 00:00:00 2001 From: Sebastian Diaz Torres Date: Mon, 6 Apr 2026 15:34:45 -0500 Subject: [PATCH 6/8] fix: Sonar workflow add sonar cloud url --- pom.xml | 2 +- sonar-project.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e3fd5673..21d302e9 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 3.22.0 0.8.6 - 3.7.0.1746 + 5.5.0.6356 jacoco ${project.basedir}/target/site/jacoco/jacoco.xml java diff --git a/sonar-project.properties b/sonar-project.properties index 3b6db440..77784ec9 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,3 +1,4 @@ +sonar.host.url=https://sonarcloud.io sonar.organization=apiaddicts sonar.projectKey=apiaddicts_sonaropenapi-rules sonar.sources=. From 2d8af73ea99850ac38f867cac7905626a7556932 Mon Sep 17 00:00:00 2001 From: Sebastian Diaz Torres Date: Mon, 6 Apr 2026 17:55:13 -0500 Subject: [PATCH 7/8] fix: add sonar.organization, sonar.host.url and sonar.projectKey to pom.xml Required properties for sonar-maven-plugin were only defined in sonar-project.properties (used by SonarScanner CLI) but not in pom.xml, causing the mandatory property error when running mvn sonar:sonar. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 21d302e9..1c899a46 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,9 @@ 5.5.0.6356 jacoco ${project.basedir}/target/site/jacoco/jacoco.xml + https://sonarcloud.io + apiaddicts + apiaddicts_sonaropenapi-rules java **/*.html,**/*.json **/*.html,**/*.json From 11e93517a8ad133ea586107c652b631d9fd5ddd7 Mon Sep 17 00:00:00 2001 From: Sebastian Diaz Torres Date: Mon, 6 Apr 2026 18:01:02 -0500 Subject: [PATCH 8/8] fix: Workflow issue with secrets --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c892b322..6c484687 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -26,7 +26,7 @@ jobs: - name: SonarCloud Analysis env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn sonar:sonar -Dsonar.token=${{ secrets.SONAR_TOKEN }} + run: mvn sonar:sonar -Dsonar.token=$SONAR_TOKEN # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - name: Update dependency graph