diff --git a/CHANGELOG.md b/CHANGELOG.md index d34f6869..081a0b9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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.0] - 2026-01-05 + +### Changed + - OAR104 - ResourcesByPostVerbCheck + +### Fixed + - OAR019 - SelectParameterCheck + - OAR020 - ExpandParameterCheck + ## [1.2.5] - 2025-12-31 ### Fixed diff --git a/pom.xml b/pom.xml index e078c8c0..a89ab7ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.apiaddicts.apitools.dosonarapi sonaropenapi-rules-community - 1.2.5 + 1.3.0 sonar-plugin SonarQube OpenAPI Community Rules diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java index 449f2f7a..8c48753a 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java @@ -20,7 +20,7 @@ public class OAR104ResourcesByPostVerbCheck extends BaseCheck { public static final String KEY = "OAR104"; private static final String MESSAGE = "OAR104.error"; - private static final String RESERVED_WORDS = "me"; + private static final String RESERVED_WORDS = "me,search"; @RuleProperty( key = "words-to-exclude", @@ -74,22 +74,22 @@ private boolean isCorrect(String path) { .filter(p -> !p.trim().isEmpty()) .toArray(String[]::new); if (parts.length == 0) return true; - + for (int i = 0; i < parts.length - 1; i++) { if (!isVariable(parts[i]) && !isSpecialVariable(parts[i]) && !isVariable(parts[i + 1]) && !isSpecialVariable(parts[i + 1])) { return false; } } - + return true; } - + private boolean isVariable(String part) { return part.startsWith("{") && part.endsWith("}"); } private boolean isSpecialVariable(String part) { - return "me".equalsIgnoreCase(part); + return reservedWords.contains(part.toLowerCase()); } private String formatMessage(String path) { diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java new file mode 100644 index 00000000..92fdda27 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java @@ -0,0 +1,185 @@ +package apiaddicts.sonar.openapi.checks.parameters; + +import apiaddicts.sonar.openapi.checks.BaseCheck; +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.AstNodeType; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import org.sonar.check.RuleProperty; + +public abstract class AbstractQueryParameterCheck extends BaseCheck { + + protected static final String DEFAULT_PATH = "/examples"; + protected static final String PATH_STRATEGY = "/include"; + + protected final String ruleKey; + protected final String messageKey; + protected final String parameterName; + protected final boolean applyToParameterizedPaths; + + protected Set paths; + protected JsonNode rootNode; + + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = DEFAULT_PATH + ) + protected String pathsStr = DEFAULT_PATH; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = PATH_STRATEGY + ) + protected String pathCheckStrategy = PATH_STRATEGY; + + protected AbstractQueryParameterCheck( + String ruleKey, + String messageKey, + String parameterName, + boolean applyToParameterizedPaths + ) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + this.parameterName = parameterName; + this.applyToParameterizedPaths = applyToParameterizedPaths; + } + + @Override + public Set subscribedKinds() { + return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); + } + + @Override + protected void visitFile(JsonNode root) { + this.rootNode = root; + paths = parsePaths(pathsStr); + super.visitFile(root); + } + + @Override + public void visitNode(JsonNode node) { + if (!"get".equals(node.key().getTokenValue())) { + return; + } + + String path = getPath(node); + + if (!applyToParameterizedPaths && endsWithPathParam(path)) { + return; + } + + boolean hasParameter = hasParameterInNode(node); + + if (shouldIncludePath(path) && !hasParameter) { + addIssue( + ruleKey, + translate(messageKey, parameterName), + node.key() + ); + } + } + + protected boolean hasParameterInNode(JsonNode node) { + JsonNode parametersNode = node.get("parameters"); + if (parametersNode != null) { + + for (JsonNode parameterNode : parametersNode.elements()) { + if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { + return true; + } + if (hasDirectParameter(parameterNode)) { + return true; + } + } + } + return false; + } + + protected boolean isRefParameter(JsonNode parameterNode) { + return parameterNode.get("$ref") != null; + } + + protected boolean hasNamedRefParameter(JsonNode parameterNode) { + String refValue = parameterNode.get("$ref").getTokenValue(); + JsonNode refParameterNode = resolveReference(refValue, rootNode); + if (refParameterNode != null) { + JsonNode nameNode = refParameterNode.get("name"); + JsonNode inNode = refParameterNode.get("in"); + return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); + } + return false; + } + + protected boolean hasDirectParameter(JsonNode parameterNode) { + JsonNode nameNode = parameterNode.get("name"); + JsonNode inNode = parameterNode.get("in"); + return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); + } + + protected String getPath(JsonNode node) { + StringBuilder pathBuilder = new StringBuilder(); + AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); + if (pathNode != null) { + while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { + pathNode = pathNode.getParent(); + } + pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); + } + return pathBuilder.toString(); + } + + protected boolean shouldIncludePath(String path) { + if (pathCheckStrategy.equals("/exclude")) { + return !paths.contains(path); + } else if (pathCheckStrategy.equals(PATH_STRATEGY)) { + return paths.contains(path); + } + return false; + } + + protected boolean endsWithPathParam(String path) { + String[] segments = path.split("/"); + if (segments.length == 0) return false; + + String last = segments[segments.length - 1].trim(); + return last.matches("^\\{[^}]+\\}$"); + } + + protected Set parsePaths(String pathsStr) { + if (!pathsStr.trim().isEmpty()) { + return Arrays.stream(pathsStr.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + } else { + return new HashSet<>(); + } + } + + protected JsonNode resolveReference(String refValue, JsonNode root) { + if (refValue == null || !refValue.startsWith("#/")) { + return null; + } + + String pathToReference = refValue.substring(2); + String[] pathParts = pathToReference.split("/"); + + JsonNode currentNode = root; + for (String part : pathParts) { + if (currentNode == null) { + return null; + } + currentNode = currentNode.get(part); + } + + return currentNode; + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java index 47cacecc..262bc099 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java @@ -1,172 +1,21 @@ package apiaddicts.sonar.openapi.checks.parameters; -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; @Rule(key = OAR019SelectParameterCheck.KEY) -public class OAR019SelectParameterCheck extends BaseCheck { +public class OAR019SelectParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR019"; private static final String MESSAGE = "OAR019.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$select"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); + public OAR019SelectParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + false + ); } - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; - } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java index 08cafe2f..355ff4d4 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java @@ -1,173 +1,21 @@ package apiaddicts.sonar.openapi.checks.parameters; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; @Rule(key = OAR020ExpandParameterCheck.KEY) -public class OAR020ExpandParameterCheck extends BaseCheck { +public class OAR020ExpandParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR020"; private static final String MESSAGE = "OAR020.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$expand"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); + public OAR020ExpandParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + false + ); } - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; - } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java index e6057e7c..65d031db 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java @@ -1,186 +1,20 @@ package apiaddicts.sonar.openapi.checks.parameters; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; @Rule(key = OAR021ExcludeParameterCheck.KEY) -public class OAR021ExcludeParameterCheck extends BaseCheck { +public class OAR021ExcludeParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR021"; private static final String MESSAGE = "OAR021.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$exclude"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - if (endsWithPathParam(path)) { - return; - } - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean endsWithPathParam(String path) { - String[] segments = path.split("/"); - if (segments.length == 0) return false; - - String last = segments[segments.length - 1].trim(); - return last.matches("^\\{[^}]+\\}$"); - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; + public OAR021ExcludeParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + false + ); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java index 30924966..0cbe783e 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java @@ -1,185 +1,20 @@ package apiaddicts.sonar.openapi.checks.parameters; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; @Rule(key = OAR022OrderbyParameterCheck.KEY) -public class OAR022OrderbyParameterCheck extends BaseCheck { +public class OAR022OrderbyParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR022"; private static final String MESSAGE = "OAR022.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$orderby"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH , OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean endsWithPathParam(String path) { - String[] segments = path.split("/"); - if (segments.length == 0) return false; - - String last = segments[segments.length - 1].trim(); - return last.matches("^\\{[^}]+\\}$"); - } - - private boolean shouldIncludePath(String path) { - if (endsWithPathParam(path)) { - return false; - } - - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; + public OAR022OrderbyParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + false + ); } -} \ No newline at end of file +} diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java index b8226c4a..8337d644 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java @@ -1,172 +1,20 @@ package apiaddicts.sonar.openapi.checks.parameters; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; @Rule(key = OAR023TotalParameterCheck.KEY) -public class OAR023TotalParameterCheck extends BaseCheck { +public class OAR023TotalParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR023"; private static final String MESSAGE = "OAR023.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$total"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; + public OAR023TotalParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + true + ); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java index 7da3eb0c..aedcaafa 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java @@ -1,173 +1,20 @@ package apiaddicts.sonar.openapi.checks.parameters; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; @Rule(key = OAR024StartParameterCheck.KEY) -public class OAR024StartParameterCheck extends BaseCheck { +public class OAR024StartParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR024"; private static final String MESSAGE = "OAR024.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$start"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; + public OAR024StartParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + true + ); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java index 828637f1..2ea5e207 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java @@ -1,174 +1,20 @@ package apiaddicts.sonar.openapi.checks.parameters; -import apiaddicts.sonar.openapi.checks.BaseCheck; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; @Rule(key = OAR025LimitParameterCheck.KEY) -public class OAR025LimitParameterCheck extends BaseCheck { +public class OAR025LimitParameterCheck extends AbstractQueryParameterCheck { public static final String KEY = "OAR025"; private static final String MESSAGE = "OAR025.error"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; private static final String PARAM_NAME = "$limit"; - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH - ) - private String pathsStr = DEFAULT_PATH; - - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - @RuleProperty( - key = "parameterName", - description = "Name of the parameter to be checked", - defaultValue = PARAM_NAME - ) - private String parameterName = PARAM_NAME; - - private Set paths; - private JsonNode rootNode; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - this.rootNode = root; - paths = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equals(node.key().getTokenValue())) { - - String path = getPath(node); - - boolean hasParameter = hasParameterInNode(node); - - if (shouldIncludePath(path) && !hasParameter && !isSingleResourcePath(path)) { - addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key()); - } - } - } - - private boolean hasParameterInNode(JsonNode node) { - JsonNode parametersNode = node.get("parameters"); - if (parametersNode != null) { - - for (JsonNode parameterNode : parametersNode.elements()) { - if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) { - return true; - } else if (hasDirectParameter(parameterNode)) { - return true; - } - } - } - return false; - } - - private boolean isRefParameter(JsonNode parameterNode) { - JsonNode refNode = parameterNode.get("$ref"); - if (refNode != null) { - return true; - } - return false; - } - - private boolean hasNamedRefParameter(JsonNode parameterNode) { - String refValue = parameterNode.get("$ref").getTokenValue(); - JsonNode refParameterNode = resolveReference(refValue, rootNode); - if (refParameterNode != null) { - JsonNode nameNode = refParameterNode.get("name"); - JsonNode inNode = refParameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - return false; - } - - private boolean hasDirectParameter(JsonNode parameterNode) { - JsonNode nameNode = parameterNode.get("name"); - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue()); - } - - private String getPath(JsonNode node) { - StringBuilder pathBuilder = new StringBuilder(); - AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH); - if (pathNode != null) { - while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) { - pathNode = pathNode.getParent(); - } - pathBuilder.append(((JsonNode) pathNode).key().getTokenValue()); - } - return pathBuilder.toString(); - } - - private boolean shouldIncludePath(String path) { - if (pathCheckStrategy.equals("/exclude")) { - return !paths.contains(path); - } else if (pathCheckStrategy.equals("/include")) { - return paths.contains(path); - } - return false; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - - private JsonNode resolveReference(String refValue, JsonNode root) { - if (refValue == null || !refValue.startsWith("#/")) { - return null; - } - - String pathToReference = refValue.substring(2); - String[] pathParts = pathToReference.split("/"); - - JsonNode currentNode = root; - for (String part : pathParts) { - if (currentNode == null) { - return null; - } - currentNode = currentNode.get(part); - } - - if (currentNode == null) { - } else { - } - - return currentNode; - } - - private boolean isSingleResourcePath(String path) { - return path.matches(".*/\\{[^/]+\\}$"); + public OAR025LimitParameterCheck() { + super( + KEY, + MESSAGE, + PARAM_NAME, + false + ); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java index bb1063fd..54c621d7 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java @@ -19,10 +19,10 @@ public void init() { v3Path = getV3Path("operations"); } - /*@Test + @Test public void verifyInV2() { - verifyV2("plain"); - }*/ + verifyV2("plain.yaml"); + } @Test public void verifyInV3() { @@ -37,6 +37,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "me", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", "me,search", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java index 90282011..8b1fc903 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java @@ -40,6 +40,11 @@ public void verifyInV2WithRef() { verifyV2("with-ref"); } + @Test + public void verifyInV2PathEndingWithParam() { + verifyV3("with-param"); + } + @Test public void verifyInV3() { verifyV3("plain"); @@ -80,6 +85,11 @@ public void verifyInV31WithRef() { verifyV31("with-ref"); } + @Test + public void verifyInV3PathEndingWithParam() { + verifyV3("with-param"); + } + @Override public void verifyRule() { assertRuleProperties("OAR019 - SelectParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MINOR, tags("parameters")); @@ -87,10 +97,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); - assertParameterProperties("parameterName", "$select", RuleParamType.STRING); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java index 81134ce3..6e9c358b 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java @@ -39,6 +39,11 @@ public void verifyInV2WithRef() { verifyV2("with-ref"); } + @Test + public void verifyInV2PathEndingWithParam() { + verifyV3("with-param"); + } + @Test public void verifyInV3() { verifyV3("plain2"); @@ -59,6 +64,11 @@ public void verifyInV3WithRef() { verifyV3("with-ref"); } + @Test + public void verifyInV3PathEndingWithParam() { + verifyV3("with-param"); + } + @Override public void verifyRule() { assertRuleProperties("OAR020 - ExpandParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MINOR, tags("parameters")); @@ -66,9 +76,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$expand", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java index 688a53f3..5d559cd3 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java @@ -76,9 +76,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java index 40981cbe..cae16c7a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java @@ -55,9 +55,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$orderby", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java index 2bc638e3..7b6ab8a1 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java @@ -55,9 +55,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$total", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java index 3322bec2..b978f505 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java @@ -55,9 +55,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$start", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java index ad29af88..12a0cf26 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java @@ -55,9 +55,8 @@ public void verifyRule() { @Override public void verifyParameters() { - assertNumberOfParameters(3); + assertNumberOfParameters(2); assertParameterProperties("paths", "/examples", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING); - assertParameterProperties("parameterName", "$limit", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/resources/checks/v2/operations/OAR104/plain.yaml b/src/test/resources/checks/v2/operations/OAR104/plain.yaml index 81f57f1d..24178247 100644 --- a/src/test/resources/checks/v2/operations/OAR104/plain.yaml +++ b/src/test/resources/checks/v2/operations/OAR104/plain.yaml @@ -9,7 +9,7 @@ paths: 200: description: Ok /resources/{r_id}: - post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/{r_id}}} + post: parameters: - name: r_id in: path @@ -19,12 +19,12 @@ paths: 200: description: Ok /resources/get: - post: + post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/get}} responses: 200: description: Ok /resources/delete: - post: + post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/delete}} parameters: - name: delete in: path @@ -44,7 +44,12 @@ paths: 200: description: Ok /resources/me: - post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/me}} + post: + responses: + 200: + description: Ok + /resources/search: + post: responses: 200: description: Ok \ No newline at end of file diff --git a/src/test/resources/checks/v2/parameters/OAR019/with-param.json b/src/test/resources/checks/v2/parameters/OAR019/with-param.json new file mode 100644 index 00000000..799521d9 --- /dev/null +++ b/src/test/resources/checks/v2/parameters/OAR019/with-param.json @@ -0,0 +1,27 @@ +{ + "swagger": "2.0", + "info": { + "title": "Swagger Petstore", + "version": "1.0.0" + }, + "paths": { + "/examples/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/examples/{id}/items/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml b/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml new file mode 100644 index 00000000..3f4a3407 --- /dev/null +++ b/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml @@ -0,0 +1,15 @@ +swagger: "2.0" +info: + title: Swagger Petstore + version: "1.0.0" +paths: + /examples/{id}: + get: + responses: + 200: + description: OK + /examples/{id}/items/{id}: + get: + responses: + 200: + description: OK diff --git a/src/test/resources/checks/v2/parameters/OAR020/with-param.json b/src/test/resources/checks/v2/parameters/OAR020/with-param.json new file mode 100644 index 00000000..799521d9 --- /dev/null +++ b/src/test/resources/checks/v2/parameters/OAR020/with-param.json @@ -0,0 +1,27 @@ +{ + "swagger": "2.0", + "info": { + "title": "Swagger Petstore", + "version": "1.0.0" + }, + "paths": { + "/examples/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/examples/{id}/items/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml b/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml new file mode 100644 index 00000000..3f4a3407 --- /dev/null +++ b/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml @@ -0,0 +1,15 @@ +swagger: "2.0" +info: + title: Swagger Petstore + version: "1.0.0" +paths: + /examples/{id}: + get: + responses: + 200: + description: OK + /examples/{id}/items/{id}: + get: + responses: + 200: + description: OK diff --git a/src/test/resources/checks/v3/operations/OAR104/plain.yaml b/src/test/resources/checks/v3/operations/OAR104/plain.yaml index 2d5e946b..521a52ca 100644 --- a/src/test/resources/checks/v3/operations/OAR104/plain.yaml +++ b/src/test/resources/checks/v3/operations/OAR104/plain.yaml @@ -9,17 +9,17 @@ paths: '200': description: Ok /resources/me/cars: - post: + post: responses: '200': description: Ok - /resources/get: + /resources/get: post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/get}} responses: '200': description: Ok - /resources/hola: - post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/hola}} + /resources/hello: + post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/hello}} responses: '200': description: Ok @@ -28,8 +28,13 @@ paths: responses: '200': description: Ok - /resources/me: - post: + /resources/me: + post: + responses: + '200': + description: Ok + /resources/search: + post: responses: '200': description: Ok \ No newline at end of file diff --git a/src/test/resources/checks/v3/parameters/OAR019/with-param.json b/src/test/resources/checks/v3/parameters/OAR019/with-param.json new file mode 100644 index 00000000..1097b436 --- /dev/null +++ b/src/test/resources/checks/v3/parameters/OAR019/with-param.json @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Swagger Petstore", + "version": "1.0.0" + }, + "paths": { + "/examples/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/examples/{id}/items/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml b/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml new file mode 100644 index 00000000..8b012753 --- /dev/null +++ b/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.0 +info: + title: Swagger Petstore + version: 1.0.0 +paths: + /examples/{id}: + get: + responses: + '200': + description: OK + /examples/{id}/items/{id}: + get: + responses: + '200': + description: OK \ No newline at end of file diff --git a/src/test/resources/checks/v3/parameters/OAR020/with-param.json b/src/test/resources/checks/v3/parameters/OAR020/with-param.json new file mode 100644 index 00000000..1097b436 --- /dev/null +++ b/src/test/resources/checks/v3/parameters/OAR020/with-param.json @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Swagger Petstore", + "version": "1.0.0" + }, + "paths": { + "/examples/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/examples/{id}/items/{id}": { + "get": { + "responses": { + "200": { + "description": "OK" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml b/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml new file mode 100644 index 00000000..8b012753 --- /dev/null +++ b/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.0 +info: + title: Swagger Petstore + version: 1.0.0 +paths: + /examples/{id}: + get: + responses: + '200': + description: OK + /examples/{id}/items/{id}: + get: + responses: + '200': + description: OK \ No newline at end of file