Skip to content

Commit 7fc0bd0

Browse files
authored
Merge pull request #116 from apiaddicts/develop
Develop
2 parents 7d417b9 + e6478e1 commit 7fc0bd0

1,954 files changed

Lines changed: 71491 additions & 1858 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: SonarCloud Analysis
2727
env:
2828
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
29-
run: mvn sonar:sonar -Dsonar.token=$SONAR_TOKEN
29+
run: mvn sonar:sonar -Dsonar.token=$SONAR_TOKEN -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apiaddicts -Dsonar.projectKey=apiaddicts_sonaropenapi-rules
3030

3131
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
3232
- name: Update dependency graph

CHANGELOG.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,100 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.4.1] - 2026-06-04
9+
10+
### Added
11+
12+
- Add OpenAPI language support without YAML and JSON conflicts.
13+
14+
### Changed
15+
16+
- Bump plugin version to `1.4.1`.
17+
- Update `sonaropenapi.version` to `1.2.1`.
18+
- Reference `openapi-front-end` and `openapi-test-tools` dependencies via `${sonaropenapi.version}` property instead of hardcoded version.
19+
20+
### Fixed
21+
22+
- OAR004 - ValidWso2ScopesRoles - Fixed false negative where `roles` defined as a YAML/JSON array were not validated element by element. Updated `AbstractPatternWso2ScopesCheck.visitScope()` to iterate array elements via `fieldNode.elements()` and validate each one individually. Added test fixtures for array roles in v2, v3, v31 and v32 formats.
23+
- OAR014 - ResourceLevelWithinNonSuggestedRange - Removed upper bound threshold: rule now fires for all depths ≥ 4 (previously only fired for depths 4–5), aligning with Spectral behavior. Updated v2 test fixtures to mark depth-6 paths as noncompliant.
24+
- OAR015 - ResourceLevelMaxAllowed - Updated depth calculation algorithm in `AbstractResourceLevelCheck.matchLevel(String path)` to count only literal segments, explicitly excluding path parameters (e.g. `{customerId}`) and `/me` segments — matching Spectral's algorithm exactly. Previously used a `pathParts − literalParamPairs` formula that produced different results for paths starting with parameters, consecutive parameters, or containing `/me`.
25+
- OAR020 - ExpandParameterCheck - Fixed false negative where GET operations on non-`/examples` paths (e.g. `/pets`, `/orders`) without a `parameters` block were not reported. Changed default path strategy from include-only `/examples` to exclude-all (empty exclude list), so the rule now applies to all collection GET endpoints. Added `/me` path exclusion and health-check path exclusion (`status`, `health`, `ping`) in `visitNode`, aligning with Spectral's filter. Added `without-parameters` test cases for v2, v3, v31 and v32.
26+
- OAR021 - ExcludeParameterCheck - Same fix as OAR020 applied for `$exclude` parameter. Changed default path strategy to exclude-all, added `/me` and health-check exclusions, added `without-parameters` test cases.
27+
- OAR028 - FilterParameterCheck - Rewritten to extend `AbstractQueryParameterCheck`. Fires exactly once per GET operation when `$filter` query parameter is absent; does not fire if `$filter` is present alongside other parameters; resolves `$filter` referenced via `$ref` to components. Covers ALL collection GET endpoints except `/me` paths, terminal `/{id}` paths and health-check paths (`status`, `health`, `ping`).
28+
- OAR037 - StringFormatCheck - Fixed false negative where string schemas without a `format` field were not reported. Updated `isInvalidString` to also fire when `format == null`.
29+
- OAR038 - StandardCreateResponseCheck - POST 201 responses must have a schema whose properties are named `data` or `error`, each with at least one sub-property. Fires with a distinct message when the property name is invalid vs. when sub-properties are missing.
30+
- OAR066 - SnakeCaseNamingConventionCheck - Fixed false positives on industry-standard property name prefixes. Skip properties whose names start with `@` or `x-`.
31+
- OAR073 - RateLimitCheck - Extended default excluded paths from `/status, /health-check` to `/status, /health, /health-check, /ping, /liveness, /readiness` in `DEFAULT_PATHS`.
32+
33+
## [1.4.1-beta-5] - 2026-06-02
34+
35+
### Fixed
36+
37+
- OAR028 - FilterParameterCheck - Rewritten to extend `AbstractQueryParameterCheck`. Fires exactly once per GET operation when `$filter` query parameter is absent; does not fire if `$filter` is present alongside other parameters; resolves `$filter` referenced via `$ref` to components. Covers ALL collection GET endpoints except `/me` paths, terminal `/{id}` paths and health-check paths (`status`, `health`, `ping`).
38+
39+
## [1.4.1-beta-4] - 2026-05-31
40+
41+
### Fixed
42+
43+
- OAR020 - ExpandParameterCheck - Fixed false negative where GET operations on non-`/examples` paths (e.g. `/pets`, `/orders`) without a `parameters` block were not reported. Changed default path strategy from include-only `/examples` to exclude-all (empty exclude list), so the rule now applies to all collection GET endpoints. Added `/me` path exclusion and health-check path exclusion (`status`, `health`, `ping`) in `visitNode`, aligning with Spectral's filter. Added `without-parameters` test cases for v2, v3, v31 and v32.
44+
- OAR021 - ExcludeParameterCheck - Same fix as OAR020 applied for `$exclude` parameter. Changed default path strategy to exclude-all, added `/me` and health-check exclusions, added `without-parameters` test cases.
45+
- OAR037 - StringFormatCheck - Fixed false negative where string schemas without a `format` field were not reported. Updated `isInvalidString` to also fire when `format == null`.
46+
- OAR038 - StandardCreateResponseCheck - POST 201 responses must have a schema whose properties are named `data` or `error`, each with at least one sub-property. Fires with a distinct message when the property name is invalid vs. when sub-properties are missing.
47+
- OAR066 - SnakeCaseNamingConventionCheck - Fixed false positives on industry-standard property name prefixes. Skip properties whose names start with `@` or `x-`.
48+
- OAR073 - RateLimitCheck - Extended default excluded paths from `/status, /health-check` to `/status, /health, /health-check, /ping, /liveness, /readiness` in `DEFAULT_PATHS`.
49+
50+
## [1.4.1-beta-3] - 2026-05-29
51+
52+
### Fixed
53+
54+
- OAR004 - ValidWso2ScopesRoles - Fixed false negative where `roles` defined as a YAML/JSON array were not validated element by element. Updated `AbstractPatternWso2ScopesCheck.visitScope()` to iterate array elements via `fieldNode.elements()` and validate each one individually. Added test fixtures for array roles in v2, v3, v31 and v32 formats.
55+
- OAR014 - ResourceLevelWithinNonSuggestedRange - Removed upper bound threshold: rule now fires for all depths ≥ 4 (previously only fired for depths 4–5), aligning with Spectral behavior. Updated v2 test fixtures to mark depth-6 paths as noncompliant.
56+
- OAR015 - ResourceLevelMaxAllowed - Updated depth calculation algorithm in `AbstractResourceLevelCheck.matchLevel(String path)` to count only literal segments, explicitly excluding path parameters (e.g. `{customerId}`) and `/me` segments — matching Spectral's algorithm exactly. Previously used a `pathParts − literalParamPairs` formula that produced different results for paths starting with parameters, consecutive parameters, or containing `/me`.
57+
58+
## [1.4.1-beta-2] - 2026-05-28
59+
60+
### Added
61+
62+
- Add OpenAPI language support without YAML and JSON conflicts.
63+
64+
## [1.4.1-beta-1] - 2026-05-26
65+
66+
### Changed
67+
68+
- Bump plugin version to `1.4.1-beta-1`.
69+
- Update `sonaropenapi.version` to `1.2.1-beta-1`.
70+
- Reference `openapi-front-end` and `openapi-test-tools` dependencies via `${sonaropenapi.version}` property instead of hardcoded version.
71+
72+
## [1.4.0] - 2026-05-22
73+
74+
### Security
75+
76+
- Upgrade `org.json:json` to `20231013` to fix CVE vulnerabilities
77+
- Upgrade `jackson-dataformat-yaml` from 2.13.3 to 2.18.6 to fix CVE alerts.
78+
- Upgrade `assertj-core` from 3.22.0 to 3.27.7 to fix XXE vulnerability.
79+
80+
### Changed
81+
82+
- Move sonar organization config to github action
83+
84+
### Added
85+
86+
#### Now, support for OpenAPI 3.2 is included. These are some of the new changes:
87+
88+
- All existing rules (OAR001 - OAR115) have been updated and validated for compatibility with the OpenAPI 3.2 specification.
89+
- Added a comprehensive set of test cases for every rule to ensure correct behavior and validation under OpenAPI 3.2 schemas.
90+
- Enhanced the engine to support new 3.2 structural changes, including updated reference handling and metadata fields.
91+
92+
### Fixed
93+
94+
- Resolve language suffix conflict between the plugin's custom YAML/JSON support and SonarQube's built-in language detection.
95+
- OAR020 - ExpandParameterCheck: rule now explicitly requires `$expand` (with `$` prefix) as the query parameter name, rejecting `expand` without prefix, aligning with Spectral behavior.
96+
- OAR021 - ExcludeParameterCheck: rule now explicitly requires `$exclude` (with `$` prefix) as the query parameter name, rejecting `exclude` without prefix, aligning with Spectral behavior.
97+
- OAR028 - FilterParameterCheck: rule now only evaluates `query` parameters; header, path and cookie parameters are ignored, aligning with Spectral behavior.
98+
- OAR051 - DescriptionDiffersSummaryCheck: rule now evaluates all HTTP methods (GET, POST, PUT, PATCH, DELETE), not only GET, aligning with Spectral behavior.
99+
- OAR066 - SnakeCaseNamingConventionCheck: rule now recursively validates nested schema property names at all depth levels, aligning with Spectral behavior.
100+
101+
8102
## [1.3.7] - 2026-05-18
9103

10104
### Fixed

pom.xml

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>org.apiaddicts.apitools.dosonarapi</groupId>
55
<artifactId>sonaropenapi-rules-community</artifactId>
6-
<version>1.3.7</version>
6+
<version>1.4.1</version>
77
<packaging>sonar-plugin</packaging>
88

99
<name>SonarQube OpenAPI Community Rules</name>
@@ -64,19 +64,16 @@
6464

6565
<sonar.version>8.7.0.41497</sonar.version>
6666
<sonarQubeMinVersion>6.7</sonarQubeMinVersion>
67-
<sonaropenapi.version>1.1.1</sonaropenapi.version>
67+
<sonaropenapi.version>1.2.1</sonaropenapi.version>
6868
<sonaranalyzer.version>1.22.0.848</sonaranalyzer.version>
69-
<orgjson.version>20220320</orgjson.version>
69+
<orgjson.version>20231013</orgjson.version>
7070
<junit.version>4.13.2</junit.version>
71-
<assertj.version>3.22.0</assertj.version>
71+
<assertj.version>3.27.7</assertj.version>
7272

7373
<jacoco.maven.plugin.version>0.8.6</jacoco.maven.plugin.version>
7474
<sonar.maven.plugin.version>5.5.0.6356</sonar.maven.plugin.version>
7575
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
7676
<sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
77-
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
78-
<sonar.organization>apiaddicts</sonar.organization>
79-
<sonar.projectKey>apiaddicts_sonaropenapi-rules</sonar.projectKey>
8077
<sonar.language>java</sonar.language>
8178
<sonar.exclusions>**/*.html,**/*.json</sonar.exclusions>
8279
<sonar.cpd.exclusions>**/*.html,**/*.json</sonar.cpd.exclusions>
@@ -94,7 +91,13 @@
9491
<dependency>
9592
<groupId>org.apiaddicts.apitools.dosonarapi</groupId>
9693
<artifactId>openapi-front-end</artifactId>
97-
<version>1.1.1</version>
94+
<version>${sonaropenapi.version}</version>
95+
</dependency>
96+
<dependency>
97+
<groupId>org.apiaddicts.apitools.dosonarapi</groupId>
98+
<artifactId>openapi-checks</artifactId>
99+
<version>${sonaropenapi.version}</version>
100+
<scope>provided</scope>
98101
</dependency>
99102
<dependency>
100103
<groupId>org.sonarsource.sonarqube</groupId>
@@ -105,7 +108,7 @@
105108
<dependency>
106109
<groupId>com.fasterxml.jackson.dataformat</groupId>
107110
<artifactId>jackson-dataformat-yaml</artifactId>
108-
<version>2.13.3</version>
111+
<version>2.18.6</version>
109112
</dependency>
110113
<dependency>
111114
<groupId>org.sonarsource.analyzer-commons</groupId>
@@ -128,7 +131,7 @@
128131
<dependency>
129132
<groupId>org.apiaddicts.apitools.dosonarapi</groupId>
130133
<artifactId>openapi-test-tools</artifactId>
131-
<version>1.1.1</version>
134+
<version>${sonaropenapi.version}</version>
132135
</dependency>
133136
<dependency>
134137
<groupId>junit</groupId>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package apiaddicts.sonar.openapi;
2+
3+
import org.sonar.api.ExtensionPoint;
4+
import org.sonar.api.scanner.ScannerSide;
5+
import org.apiaddicts.apitools.dosonarapi.api.OpenApiCustomRuleRepository;
6+
import apiaddicts.sonar.openapi.checks.RulesLists;
7+
import org.sonarsource.api.sonarlint.SonarLintSide;
8+
9+
import java.util.List;
10+
11+
import static apiaddicts.sonar.openapi.OpenAPICustomRulesDefinition.JSON_REPOSITORY_KEY;
12+
13+
@SonarLintSide
14+
@ScannerSide
15+
@ExtensionPoint
16+
public class OpenAPICustomJsonRuleRepository implements OpenApiCustomRuleRepository {
17+
@Override
18+
public String repositoryKey() {
19+
return JSON_REPOSITORY_KEY;
20+
}
21+
22+
@Override
23+
public List<Class<?>> checkClasses() {
24+
return RulesLists.getAllChecks();
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package apiaddicts.sonar.openapi;
2+
3+
import org.sonar.api.ExtensionPoint;
4+
import org.sonar.api.scanner.ScannerSide;
5+
import org.apiaddicts.apitools.dosonarapi.api.OpenApiCustomRuleRepository;
6+
import apiaddicts.sonar.openapi.checks.RulesLists;
7+
import org.sonarsource.api.sonarlint.SonarLintSide;
8+
9+
import java.util.List;
10+
11+
import static apiaddicts.sonar.openapi.OpenAPICustomRulesDefinition.OPENAPI_REPOSITORY_KEY;
12+
13+
@SonarLintSide
14+
@ScannerSide
15+
@ExtensionPoint
16+
public class OpenAPICustomOpenApiRuleRepository implements OpenApiCustomRuleRepository {
17+
@Override
18+
public String repositoryKey() {
19+
return OPENAPI_REPOSITORY_KEY;
20+
}
21+
22+
@Override
23+
public List<Class<?>> checkClasses() {
24+
return RulesLists.getAllChecks();
25+
}
26+
}

src/main/java/apiaddicts/sonar/openapi/OpenAPICustomPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ public void define(Context context) {
1414
OpenAPICustomProfileDefinition.class,
1515
OpenAPICustomRulesDefinition.class,
1616
// batch extensions -> objects are instantiated during code analysis
17-
OpenAPICustomRuleRepository.class
17+
OpenAPICustomRuleRepository.class,
18+
OpenAPICustomJsonRuleRepository.class,
19+
OpenAPICustomOpenApiRuleRepository.class
1820
);
1921
}
2022

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package apiaddicts.sonar.openapi;
22

33
import org.apiaddicts.apitools.dosonarapi.api.OpenApiCustomRuleRepository;
4+
import org.apiaddicts.apitools.dosonarapi.checks.CheckList;
45
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
56
import org.sonar.api.utils.AnnotationUtils;
67
import org.sonar.check.Rule;
@@ -9,28 +10,40 @@
910
import javax.annotation.Nullable;
1011
import java.util.List;
1112

12-
/**
13-
* Declare a new quality profile that comprises all the custom rules, plus the SonarOpenApi standard rules.
14-
* <p>
15-
* This allows to create a built-in profile that extends the Sonar Way profile, and that includes your rules.
16-
* This profile will automatically inherit any new rule brought in by the core plugin.
17-
*/
1813
public class OpenAPICustomProfileDefinition implements BuiltInQualityProfilesDefinition {
19-
public static final String MY_COMPANY_WAY = "Custom";
14+
public static final String OPENAPI_WAY = "OpenAPI";
2015

2116
public OpenAPICustomProfileDefinition() {
2217
this(null);
2318
}
2419

2520
public OpenAPICustomProfileDefinition(@Nullable OpenApiCustomRuleRepository[] repositories) {
26-
// Intentional blank
21+
// Intentional blank
2722
}
2823

2924
@Override
3025
public void define(Context context) {
31-
NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile(MY_COMPANY_WAY, "openapi");
32-
addRepositoryRules(profile, OpenAPICustomRulesDefinition.REPOSITORY_KEY, RulesLists.getAllChecks());
33-
profile.done();
26+
NewBuiltInQualityProfile yamlProfile = context.createBuiltInQualityProfile(OPENAPI_WAY, "yaml");
27+
addBaseRules(yamlProfile, CheckList.YAML_REPOSITORY_KEY);
28+
addRepositoryRules(yamlProfile, OpenAPICustomRulesDefinition.YAML_REPOSITORY_KEY, RulesLists.getAllChecks());
29+
yamlProfile.done();
30+
31+
NewBuiltInQualityProfile jsonProfile = context.createBuiltInQualityProfile(OPENAPI_WAY, "json");
32+
addBaseRules(jsonProfile, CheckList.JSON_REPOSITORY_KEY);
33+
addRepositoryRules(jsonProfile, OpenAPICustomRulesDefinition.JSON_REPOSITORY_KEY, RulesLists.getAllChecks());
34+
jsonProfile.done();
35+
36+
NewBuiltInQualityProfile openapiProfile = context.createBuiltInQualityProfile(OPENAPI_WAY, CheckList.OPENAPI_LANGUAGE);
37+
addBaseRules(openapiProfile, CheckList.OPENAPI_REPOSITORY_KEY);
38+
addRepositoryRules(openapiProfile, OpenAPICustomRulesDefinition.OPENAPI_REPOSITORY_KEY, RulesLists.getAllChecks());
39+
openapiProfile.done();
40+
}
41+
42+
private void addBaseRules(NewBuiltInQualityProfile profile, String repositoryKey) {
43+
for (Class<?> check : CheckList.getChecks()) {
44+
Rule annotation = AnnotationUtils.getAnnotation(check, Rule.class);
45+
profile.activateRule(repositoryKey, annotation.key());
46+
}
3447
}
3548

3649
private void addRepositoryRules(NewBuiltInQualityProfile profile, String key, List<Class<?>> checks) {
@@ -41,8 +54,8 @@ private void addRepositoryRules(NewBuiltInQualityProfile profile, String key, Li
4154
}
4255
}
4356
}
44-
57+
4558
private boolean isTemplateRule(String ruleKey) {
46-
return "OAR112".equals(ruleKey);
59+
return "OAR112".equals(ruleKey);
4760
}
4861
}

src/main/java/apiaddicts/sonar/openapi/OpenAPICustomRuleRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import java.util.List;
1010

11-
import static apiaddicts.sonar.openapi.OpenAPICustomRulesDefinition.REPOSITORY_KEY;
11+
import static apiaddicts.sonar.openapi.OpenAPICustomRulesDefinition.YAML_REPOSITORY_KEY;
1212

1313
/**
1414
* Makes the rules visible to the OpenAPI scanner sensor,
@@ -22,7 +22,7 @@
2222
public class OpenAPICustomRuleRepository implements OpenApiCustomRuleRepository {
2323
@Override
2424
public String repositoryKey() {
25-
return REPOSITORY_KEY;
25+
return YAML_REPOSITORY_KEY;
2626
}
2727

2828
@Override

src/main/java/apiaddicts/sonar/openapi/OpenAPICustomRulesDefinition.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
@ScannerSide
1616
@ExtensionPoint
1717
public class OpenAPICustomRulesDefinition implements RulesDefinition {
18-
public static final String REPOSITORY_KEY = "openapi-custom";
18+
public static final String YAML_REPOSITORY_KEY = "openapi-custom-yaml";
19+
public static final String JSON_REPOSITORY_KEY = "openapi-custom-json";
20+
public static final String OPENAPI_REPOSITORY_KEY = "openapi-custom";
1921
private static final String REPOSITORY_NAME = "OpenAPI Custom";
2022
private static final String ROOT_RESOURCE_FOLDER = "org/sonar/l10n/openapi/rules/openapi/";
2123
private static final String SECURITY_GROUP = "security";
@@ -31,11 +33,16 @@ public class OpenAPICustomRulesDefinition implements RulesDefinition {
3133
@Override
3234
public void define(Context context) {
3335
I18nContext.initializeFromUserLanguage();
36+
populateRepository(context, YAML_REPOSITORY_KEY, "yaml");
37+
populateRepository(context, JSON_REPOSITORY_KEY, "json");
38+
populateRepository(context, OPENAPI_REPOSITORY_KEY, "openapi");
39+
}
40+
41+
private void populateRepository(Context context, String key, String language) {
3442
NewRepository repository = context
35-
.createRepository(REPOSITORY_KEY, "openapi")
43+
.createRepository(key, language)
3644
.setName(REPOSITORY_NAME);
3745

38-
// Carga de reglas para cada grupo
3946
new RuleMetadataLoader(getPath(SECURITY_GROUP)).addRulesByAnnotatedClass(repository, RulesLists.getSecurityChecks());
4047
new RuleMetadataLoader(getPath(FORMAT_GROUP)).addRulesByAnnotatedClass(repository, RulesLists.getFormatChecks());
4148
new RuleMetadataLoader(getPath(SCHEMAS_GROUP)).addRulesByAnnotatedClass(repository, RulesLists.getSchemasChecks());

0 commit comments

Comments
 (0)