diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java new file mode 100644 index 00000000..ce651971 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractPatternWso2ScopesCheck.java @@ -0,0 +1,39 @@ +package apiaddicts.sonar.openapi.checks.apim.wso2; + +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.regex.Pattern; + +public abstract class AbstractPatternWso2ScopesCheck extends AbstractWso2ScopesCheck { + + protected final String ruleKey; + protected final String messageKey; + protected final String fieldName; + protected final String defaultPatternValue; + + protected Pattern pattern; + private String patternStr; + + protected AbstractPatternWso2ScopesCheck(String key, String message, String fieldName, String defaultPatternValue) { + this.ruleKey = key; + this.messageKey = message; + this.fieldName = fieldName; + this.defaultPatternValue = defaultPatternValue; + } + + @Override + protected void visitFile(JsonNode root) { + pattern = Pattern.compile(patternStr != null ? patternStr : defaultPatternValue); + } + + @Override + protected void visitScope(JsonNode scope) { + JsonNode fieldNode = scope.propertyMap().get(fieldName); + if (fieldNode == null || fieldNode.isNull() || fieldNode.isMissing()) return; + + String fieldText = fieldNode.getTokenValue(); + boolean notValid = !pattern.matcher(fieldText).matches(); + + if (notValid) addIssue(ruleKey, translate(messageKey), fieldNode.value()); + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2OperationCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2OperationCheck.java new file mode 100644 index 00000000..a8b4c7f0 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/AbstractWso2OperationCheck.java @@ -0,0 +1,28 @@ +package apiaddicts.sonar.openapi.checks.apim.wso2; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.Set; + +public abstract class AbstractWso2OperationCheck extends BaseCheck { + + @Override + public Set subscribedKinds() { + return ImmutableSet.of( + OpenApi2Grammar.OPERATION, + OpenApi3Grammar.OPERATION + ); + } + + @Override + public void visitNode(JsonNode node) { + visitOperationNode(node); + } + + protected abstract void visitOperationNode(JsonNode node); +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java index badf5869..7cb8c273 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR004ValidWso2ScopesRolesCheck.java @@ -2,36 +2,21 @@ import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.regex.Pattern; @Rule(key = OAR004ValidWso2ScopesRolesCheck.KEY) -public class OAR004ValidWso2ScopesRolesCheck extends AbstractWso2ScopesCheck { - - public static final String KEY = "OAR004"; - private static final String DEFAULT_PATTERN_VALUE = "^[a-zA-Z0-9_\\-., ]+$"; - private static final String MESSAGE = "OAR004.error"; - - @RuleProperty( - key = "pattern", - description = "Regular expression used to check the 'roles' field against.", - defaultValue = DEFAULT_PATTERN_VALUE) - public String patternStr = DEFAULT_PATTERN_VALUE; - - private Pattern pattern; - - @Override - protected void visitFile(JsonNode root) { - pattern = Pattern.compile(patternStr); - } - - @Override - protected void visitScope(JsonNode scope) { - JsonNode roles = scope.propertyMap().get("roles"); - if (roles == null || roles.isNull() || roles.isMissing()) return; - String rolesText = roles.getTokenValue(); - boolean notValid = !pattern.matcher(rolesText).matches(); - if (notValid) addIssue(KEY, translate(MESSAGE), roles); - } -} +public class OAR004ValidWso2ScopesRolesCheck extends AbstractPatternWso2ScopesCheck { + + public static final String KEY = "OAR004"; + private static final String MESSAGE = "OAR004.error"; + private static final String DEFAULT_PATTERN_VALUE = "^[a-zA-Z0-9_\\-., ]+$"; + + @RuleProperty( + key = "pattern", + description = "Regular expression used to check the 'roles' field against.", + defaultValue = DEFAULT_PATTERN_VALUE) + private String patternStr = DEFAULT_PATTERN_VALUE; + + public OAR004ValidWso2ScopesRolesCheck() { + super(KEY, MESSAGE, "roles", DEFAULT_PATTERN_VALUE); + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR005UndefinedWso2ScopeUseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR005UndefinedWso2ScopeUseCheck.java index 90d9faf6..22dc33d3 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR005UndefinedWso2ScopeUseCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR005UndefinedWso2ScopeUseCheck.java @@ -1,12 +1,7 @@ package apiaddicts.sonar.openapi.checks.apim.wso2; -import com.google.common.collect.ImmutableSet; import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import apiaddicts.sonar.openapi.checks.BaseCheck; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import java.util.Collections; @@ -16,47 +11,50 @@ import static java.util.Objects.isNull; @Rule(key = OAR005UndefinedWso2ScopeUseCheck.KEY) -public class OAR005UndefinedWso2ScopeUseCheck extends BaseCheck { - - public static final String KEY = "OAR005"; - private static final String MESSAGE = "OAR005.error"; - - private Set definedScopes; - - @Override - protected void visitFile(JsonNode root) { - definedScopes = getScopes(root); - } - - private Set getScopes(JsonNode root) { - JsonNode scopes = root.get("x-wso2-security").get("apim").get("x-wso2-scopes"); - if (scopes.isMissing() || scopes.isNull()) return Collections.emptySet(); - return scopes - .elements() - .stream() - .map(node -> node.get("name")) - .filter(node -> !node.isMissing() && !node.isNull()) - .map(AstNode::getTokenValue) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - visitV2Node(node); - } - - private void visitV2Node(JsonNode node) { - JsonNode scopeNode = node.get("x-scope"); - if (scopeNode.isMissing()) return; - String scope = scopeNode.isNull() ? null : scopeNode.getTokenValue(); - if (isNull(scope) || !definedScopes.contains(scope)) { - addIssue(KEY, translate(MESSAGE), scopeNode); - } - } - -} +public class OAR005UndefinedWso2ScopeUseCheck + extends AbstractWso2OperationCheck { + + public static final String KEY = "OAR005"; + private static final String MESSAGE = "OAR005.error"; + + private Set definedScopes; + + @Override + protected void visitFile(JsonNode root) { + definedScopes = getScopes(root); + } + + private Set getScopes(JsonNode root) { + + JsonNode scopes = root + .get("x-wso2-security") + .get("apim") + .get("x-wso2-scopes"); + + if (scopes.isMissing() || scopes.isNull()) { + return Collections.emptySet(); + } + + return scopes.elements().stream() + .map(node -> node.get("name")) + .filter(node -> !node.isMissing() && !node.isNull()) + .map(AstNode::getTokenValue) + .collect(Collectors.toSet()); + } + + @Override + protected void visitOperationNode(JsonNode node) { + + JsonNode scopeNode = node.get("x-scope"); + + if (scopeNode.isMissing()) return; + + String scope = scopeNode.isNull() + ? null + : scopeNode.getTokenValue(); + + if (isNull(scope) || !definedScopes.contains(scope)) { + addIssue(KEY, translate(MESSAGE), scopeNode); + } + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java index 23be1cc4..bd67e0b1 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR040StandardWso2ScopesNameCheck.java @@ -2,36 +2,21 @@ import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.regex.Pattern; @Rule(key = OAR040StandardWso2ScopesNameCheck.KEY) -public class OAR040StandardWso2ScopesNameCheck extends AbstractWso2ScopesCheck { - - public static final String KEY = "OAR040"; - private static final String DEFAULT_PATTERN_VALUE = "^[a-zA-Z]{4,}_(SC|sc)_[a-zA-Z0-9]{1,}$"; - private static final String MESSAGE = "OAR040.error"; - - @RuleProperty( - key = "pattern", - description = "Regular expression used to check the 'name' field against.", - defaultValue = DEFAULT_PATTERN_VALUE) - public String patternStr = DEFAULT_PATTERN_VALUE; - - private Pattern pattern; - - @Override - protected void visitFile(JsonNode root) { - pattern = Pattern.compile(patternStr); - } - - @Override - protected void visitScope(JsonNode scope) { - JsonNode name = scope.propertyMap().get("name"); - if (name == null || name.isNull() || name.isMissing()) return; - String nameText = name.getTokenValue(); - boolean notValid = !pattern.matcher(nameText).matches(); - if (notValid) addIssue(KEY, translate(MESSAGE), name.value()); - } -} +public class OAR040StandardWso2ScopesNameCheck extends AbstractPatternWso2ScopesCheck { + + public static final String KEY = "OAR040"; + private static final String MESSAGE = "OAR040.error"; + private static final String DEFAULT_PATTERN_VALUE = "^[a-zA-Z]{4,}_(SC|sc)_[a-zA-Z0-9]{1,}$"; + + @RuleProperty( + key = "pattern", + description = "Regular expression used to check the 'name' field against.", + defaultValue = DEFAULT_PATTERN_VALUE) + private String patternStr = DEFAULT_PATTERN_VALUE; + + public OAR040StandardWso2ScopesNameCheck() { + super(KEY, MESSAGE, "name", DEFAULT_PATTERN_VALUE); + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR041UndefinedAuthTypeForWso2ScopeCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR041UndefinedAuthTypeForWso2ScopeCheck.java index e0ad9f13..14c01a73 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR041UndefinedAuthTypeForWso2ScopeCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/apim/wso2/OAR041UndefinedAuthTypeForWso2ScopeCheck.java @@ -1,37 +1,26 @@ package apiaddicts.sonar.openapi.checks.apim.wso2; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import apiaddicts.sonar.openapi.checks.BaseCheck; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; -import java.util.Set; - @Rule(key = OAR041UndefinedAuthTypeForWso2ScopeCheck.KEY) -public class OAR041UndefinedAuthTypeForWso2ScopeCheck extends BaseCheck { +public class OAR041UndefinedAuthTypeForWso2ScopeCheck + extends AbstractWso2OperationCheck { public static final String KEY = "OAR041"; private static final String MESSAGE = "OAR041.error"; @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - visitV2Node(node); - } + protected void visitOperationNode(JsonNode node) { - private void visitV2Node(JsonNode node) { JsonNode scopeNode = node.get("x-scope"); + if (scopeNode.isMissing()) return; + JsonNode authTypeNode = node.get("x-auth-type"); + if (authTypeNode.isMissing()) { addIssue(KEY, translate(MESSAGE), scopeNode.key()); } } -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractInfoObjectMappingCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractInfoObjectMappingCheck.java new file mode 100644 index 00000000..82b50705 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/AbstractInfoObjectMappingCheck.java @@ -0,0 +1,75 @@ +package apiaddicts.sonar.openapi.checks.format; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.List; +import java.util.Set; + +public abstract class AbstractInfoObjectMappingCheck extends BaseCheck { + + private final String ruleKey; + private final String messageKey; + private final String targetProperty; + + protected AbstractInfoObjectMappingCheck( + String ruleKey, + String messageKey, + String targetProperty + ) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + this.targetProperty = targetProperty; + } + + @Override + public Set subscribedKinds() { + return ImmutableSet.of( + OpenApi2Grammar.INFO, + OpenApi3Grammar.INFO, + OpenApi31Grammar.INFO + ); + } + + @Override + public void visitNode(JsonNode node) { + validateInfo(node); + } + + private void validateInfo(JsonNode infoNode) { + if (infoNode == null || infoNode.isMissing()) return; + List children = infoNode.getJsonChildren(); + + for (JsonNode child : children) { + String property = child.key().getTokenValue(); + if (targetProperty.equals(property)) { + validateMapping(child); + break; + } + } + } + + private void validateMapping(JsonNode mappingNode) { + if (mappingNode == null || mappingNode.isMissing()) return; + List children = mappingNode.getJsonChildren(); + boolean isEmpty = true; + + for (JsonNode child : children) { + String repr = child.toString(); + if (repr.contains("BLOCK_MAPPING") + || repr.contains("FLOW_MAPPING")) { + isEmpty = false; + break; + } + } + + if (isEmpty) { + addIssue(ruleKey, translate(messageKey), mappingNode.key()); + } + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR110LicenseInformationCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR110LicenseInformationCheck.java index 5e92eadb..0ca23989 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR110LicenseInformationCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR110LicenseInformationCheck.java @@ -1,69 +1,14 @@ package apiaddicts.sonar.openapi.checks.format; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.List; -import java.util.Set; @Rule(key = OAR110LicenseInformationCheck.KEY) -public class OAR110LicenseInformationCheck extends BaseCheck { +public class OAR110LicenseInformationCheck extends AbstractInfoObjectMappingCheck { public static final String KEY = "OAR110"; private static final String MESSAGE = "OAR110.error"; - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.INFO, OpenApi3Grammar.INFO, OpenApi31Grammar.INFO); - } - - @Override - public void visitNode(JsonNode node) { - validateInfo(node); - } - - private void validateInfo(JsonNode infoNode) { - if (infoNode == null || infoNode.isMissing()) { - return; - } - - List infoChildren = infoNode.getJsonChildren(); - - for (JsonNode child : infoChildren) { - String propertyName = child.key().getTokenValue(); - if ("license".equals(propertyName)) { - validateLicenseNode(child); - break; - } - } - - } - - private void validateLicenseNode(JsonNode licenseNode) { - if (licenseNode == null || licenseNode.isMissing()) { - return; - } - - List children = licenseNode.getJsonChildren(); - - boolean isEmptyLicense = true; - for (JsonNode child : children) { - if (child.toString().contains("BLOCK_MAPPING") || child.toString().contains("FLOW_MAPPING")) { - isEmptyLicense = false; - break; - } - } - - if (isEmptyLicense) { - addIssue(KEY, translate(MESSAGE), licenseNode.key()); - } + public OAR110LicenseInformationCheck() { + super(KEY, MESSAGE, "license"); } - -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR111ContactInformationCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR111ContactInformationCheck.java index 5688b07f..1548a9b4 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR111ContactInformationCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/format/OAR111ContactInformationCheck.java @@ -1,68 +1,14 @@ package apiaddicts.sonar.openapi.checks.format; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.List; -import java.util.Set; @Rule(key = OAR111ContactInformationCheck.KEY) -public class OAR111ContactInformationCheck extends BaseCheck { +public class OAR111ContactInformationCheck extends AbstractInfoObjectMappingCheck { public static final String KEY = "OAR111"; private static final String MESSAGE = "OAR111.error"; - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.INFO, OpenApi3Grammar.INFO, OpenApi31Grammar.INFO); - } - - @Override - public void visitNode(JsonNode node) { - validateInfo(node); - } - - private void validateInfo(JsonNode infoNode) { - if (infoNode == null || infoNode.isMissing()) { - return; - } - - List infoChildren = infoNode.getJsonChildren(); - - for (JsonNode child : infoChildren) { - String propertyName = child.key().getTokenValue(); - if ("contact".equals(propertyName)) { - validateContactNode(child); - break; - } - } - } - - private void validateContactNode(JsonNode contactNode) { - if (contactNode == null || contactNode.isMissing()) { - return; - } - - List children = contactNode.getJsonChildren(); - - boolean isEmptyContact = true; - for (JsonNode child : children) { - if (child.toString().contains("BLOCK_MAPPING") || child.toString().contains("FLOW_MAPPING")) { - isEmptyContact = false; - break; - } - } - - if (isEmptyContact) { - addIssue(KEY, translate(MESSAGE), contactNode.key()); - } + public OAR111ContactInformationCheck() { + super(KEY, MESSAGE, "contact"); } - -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/AbstractResourcesByVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/AbstractResourcesByVerbCheck.java new file mode 100644 index 00000000..8f421eb5 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/AbstractResourcesByVerbCheck.java @@ -0,0 +1,119 @@ +package apiaddicts.sonar.openapi.checks.operations; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; +import org.sonar.check.RuleProperty; +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.Set; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class AbstractResourcesByVerbCheck extends BaseCheck { + + protected final String ruleKey; + protected final String messageKey; + protected final String verb; + protected final boolean consecutiveCheck; + + @RuleProperty( + key = "words-to-exclude", + description = "Comma-separated list of reserved words that should not appear in the path" + ) + protected String reservedWordsStr; + + protected Set reservedWords; + + protected AbstractResourcesByVerbCheck(String ruleKey, String messageKey, String verb, String reservedWordsStr, boolean consecutiveCheck) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + this.verb = verb; + this.reservedWordsStr = reservedWordsStr; + this.consecutiveCheck = consecutiveCheck; + init(); + } + + private void init() { + reservedWords = Arrays.stream(reservedWordsStr.split(",")) + .map(String::toLowerCase) + .map(String::trim) + .collect(Collectors.toSet()); + } + + @Override + public Set subscribedKinds() { + return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); + } + + @Override + public void visitNode(JsonNode node) { + if (verb.equalsIgnoreCase(node.key().getTokenValue())) { + JsonNode pathNode = findParentPathNode(node); + if (pathNode != null) { + String path = pathNode.key().getTokenValue(); + if (!isCorrect(path)) { + addIssue(ruleKey, formatMessage(path), node.key()); + } + } + } + } + + private JsonNode findParentPathNode(JsonNode node) { + JsonNode parent = (JsonNode) node.getParent(); + while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH + || parent.getType() == OpenApi3Grammar.PATH + || parent.getType() == OpenApi31Grammar.PATH)) { + parent = (JsonNode) parent.getParent(); + } + return parent; + } + + protected boolean isVariable(String part) { + return part.startsWith("{") && part.endsWith("}"); + } + + protected boolean isCorrect(String path) { + String[] parts = Stream.of(path.split("/")) + .filter(p -> !p.trim().isEmpty()) + .toArray(String[]::new); + if (parts.length == 0) return true; + + return consecutiveCheck ? isCorrectConsecutive(parts) : isCorrectNormal(parts); + } + + private boolean isCorrectConsecutive(String[] parts) { + for (int i = 0; i < parts.length - 1; i++) { + if (!isVariable(parts[i]) && !reservedWords.contains(parts[i].toLowerCase()) + && !isVariable(parts[i + 1]) && !reservedWords.contains(parts[i + 1].toLowerCase())) { + return false; + } + } + return true; + } + + private boolean isCorrectNormal(String[] parts) { + for (String part : parts) { + if (isVariable(part)) { + String variableName = part.substring(1, part.length() - 1); + if (reservedWords.contains(variableName.toLowerCase())) { + return false; + } + } else if (reservedWords.contains(part.toLowerCase())) { + return false; + } + } + return true; + } + + protected String formatMessage(String path) { + String[] parts = path.split("/"); + String resource = parts.length > 1 ? parts[1] : ""; + String nextPart = parts.length > 2 ? "/" + parts[2] : ""; + return translate(messageKey, resource + nextPart); + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheck.java index 5501a232..218ea701 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheck.java @@ -1,102 +1,16 @@ package apiaddicts.sonar.openapi.checks.operations; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR103ResourcesByGetVerbCheck.KEY) -public class OAR103ResourcesByGetVerbCheck extends BaseCheck { +public class OAR103ResourcesByGetVerbCheck extends AbstractResourcesByVerbCheck { public static final String KEY = "OAR103"; private static final String MESSAGE = "OAR103.error"; - private static final String WORDS_TO_EXCLUDE = "get,delete"; - - @RuleProperty( - key = "words-to-exclude", - description = "Comma-separated list of reserved words that should not appear in the path", - defaultValue = WORDS_TO_EXCLUDE - ) - private String reservedWordsStr = WORDS_TO_EXCLUDE; - - private Set reservedWords; + private static final String RESERVED_WORDS = "get,delete"; + private static final String GET_VERB = "get"; public OAR103ResourcesByGetVerbCheck() { - init(); - } - - private void init() { - reservedWords = Arrays.stream(reservedWordsStr.split(",")) - .map(String::toLowerCase) - .map(String::trim) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - if ("get".equalsIgnoreCase(node.key().getTokenValue())) { - JsonNode pathNode = findParentPathNode(node); - if (pathNode != null) { - String path = pathNode.key().getTokenValue(); - if (!isCorrect(path)) { - String formattedMessage = formatMessage(path); - addIssue(KEY, formattedMessage, node.key()); - } - } - } - } - - private JsonNode findParentPathNode(JsonNode node) { - JsonNode parent = (JsonNode) node.getParent(); - while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH || parent.getType() == OpenApi3Grammar.PATH || parent.getType() == OpenApi31Grammar.PATH)) { - parent = (JsonNode) parent.getParent(); - } - return parent; - } - - private boolean isCorrect(String path) { - String[] parts = Stream.of(path.split("/")) - .filter(p -> !p.trim().isEmpty()) - .toArray(String[]::new); - if (parts.length == 0) return true; - - for (String part : parts) { - if (isVariable(part)) { - String variableName = part.substring(1, part.length() - 1); - if (reservedWords.contains(variableName.toLowerCase())) { - return false; - } - } else if (reservedWords.contains(part.toLowerCase())) { - return false; - } - } - - return true; - } - - private boolean isVariable(String part) { - return part.startsWith("{") && part.endsWith("}"); - } - - private String formatMessage(String path) { - String[] parts = path.split("/"); - String resource = parts.length > 1 ? parts[1] : ""; - String nextPart = parts.length > 2 ? "/" + parts[2] : ""; - return translate(MESSAGE, resource + nextPart); + super(KEY, MESSAGE, GET_VERB, RESERVED_WORDS, false); } } \ No newline at end of file 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 8c48753a..5fe142b3 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java @@ -1,101 +1,16 @@ package apiaddicts.sonar.openapi.checks.operations; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -import apiaddicts.sonar.openapi.checks.BaseCheck; -import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; -import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; -import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; - -import java.util.Set; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR104ResourcesByPostVerbCheck.KEY) -public class OAR104ResourcesByPostVerbCheck extends BaseCheck { +public class OAR104ResourcesByPostVerbCheck extends AbstractResourcesByVerbCheck { public static final String KEY = "OAR104"; private static final String MESSAGE = "OAR104.error"; private static final String RESERVED_WORDS = "me,search"; - - @RuleProperty( - key = "words-to-exclude", - description = "Comma-separated list of reserved words that should not appear as consecutive static parts", - defaultValue = RESERVED_WORDS - ) - private String reservedWordsStr = RESERVED_WORDS; - - private Set reservedWords; + private static final String POST_VERB = "post"; public OAR104ResourcesByPostVerbCheck() { - init(); - } - - private void init() { - reservedWords = Arrays.stream(reservedWordsStr.split(",")) - .map(String::toLowerCase) - .map(String::trim) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - if ("post".equalsIgnoreCase(node.key().getTokenValue())) { - JsonNode pathNode = findParentPathNode(node); - if (pathNode != null) { - String path = pathNode.key().getTokenValue(); - if (!isCorrect(path)) { - String formattedMessage = formatMessage(path); - addIssue(KEY, formattedMessage, node.key()); - } - } - } - } - - private JsonNode findParentPathNode(JsonNode node) { - JsonNode parent = (JsonNode) node.getParent(); - while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH || parent.getType() == OpenApi3Grammar.PATH || parent.getType() == OpenApi31Grammar.PATH)) { - parent = (JsonNode) parent.getParent(); - } - return parent; - } - - private boolean isCorrect(String path) { - String[] parts = Stream.of(path.split("/")) - .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 reservedWords.contains(part.toLowerCase()); - } - - private String formatMessage(String path) { - String[] parts = path.split("/"); - String resource = parts.length > 1 ? parts[1] : ""; - String nextPart = parts.length > 2 ? "/" + parts[2] : ""; - return translate(MESSAGE, resource + nextPart); + super(KEY, MESSAGE, POST_VERB, RESERVED_WORDS, true); } -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheck.java index 27ce92cd..98e82d46 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheck.java @@ -1,102 +1,16 @@ package apiaddicts.sonar.openapi.checks.operations; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR105ResourcesByPutVerbCheck.KEY) -public class OAR105ResourcesByPutVerbCheck extends BaseCheck { +public class OAR105ResourcesByPutVerbCheck extends AbstractResourcesByVerbCheck { public static final String KEY = "OAR105"; private static final String MESSAGE = "OAR105.error"; - private static final String WORDS_TO_EXCLUDE = "get,delete"; - - @RuleProperty( - key = "words-to-exclude", - description = "Comma-separated list of reserved words that should not appear in the path", - defaultValue = WORDS_TO_EXCLUDE - ) - private String reservedWordsStr = WORDS_TO_EXCLUDE; - - private Set reservedWords; + private static final String RESERVED_WORDS = "get,delete"; + private static final String PUT_VERB = "put"; public OAR105ResourcesByPutVerbCheck() { - init(); - } - - private void init() { - reservedWords = Arrays.stream(reservedWordsStr.split(",")) - .map(String::toLowerCase) - .map(String::trim) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - if ("put".equalsIgnoreCase(node.key().getTokenValue())) { - JsonNode pathNode = findParentPathNode(node); - if (pathNode != null) { - String path = pathNode.key().getTokenValue(); - if (!isCorrect(path)) { - String formattedMessage = formatMessage(path); - addIssue(KEY, formattedMessage, node.key()); - } - } - } - } - - private JsonNode findParentPathNode(JsonNode node) { - JsonNode parent = (JsonNode) node.getParent(); - while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH || parent.getType() == OpenApi3Grammar.PATH || parent.getType() == OpenApi31Grammar.PATH)) { - parent = (JsonNode) parent.getParent(); - } - return parent; - } - - private boolean isCorrect(String path) { - String[] parts = Stream.of(path.split("/")) - .filter(p -> !p.trim().isEmpty()) - .toArray(String[]::new); - if (parts.length == 0) return true; - - for (String part : parts) { - if (isVariable(part)) { - String variableName = part.substring(1, part.length() - 1); - if (reservedWords.contains(variableName.toLowerCase())) { - return false; - } - } else if (reservedWords.contains(part.toLowerCase())) { - return false; - } - } - - return true; - } - - private boolean isVariable(String part) { - return part.startsWith("{") && part.endsWith("}"); - } - - private String formatMessage(String path) { - String[] parts = path.split("/"); - String resource = parts.length > 1 ? parts[1] : ""; - String nextPart = parts.length > 2 ? "/" + parts[2] : ""; - return translate(MESSAGE, resource + nextPart); + super(KEY, MESSAGE, PUT_VERB, RESERVED_WORDS, false); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheck.java index 73a5f6aa..ea19a3e6 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheck.java @@ -1,102 +1,16 @@ package apiaddicts.sonar.openapi.checks.operations; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR106ResourcesByPatchVerbCheck.KEY) -public class OAR106ResourcesByPatchVerbCheck extends BaseCheck { +public class OAR106ResourcesByPatchVerbCheck extends AbstractResourcesByVerbCheck { public static final String KEY = "OAR106"; private static final String MESSAGE = "OAR106.error"; - private static final String WORDS_TO_EXCLUDE = "get,delete"; - - @RuleProperty( - key = "words-to-exclude", - description = "Comma-separated list of reserved words that should not appear in the path", - defaultValue = WORDS_TO_EXCLUDE - ) - private String reservedWordsStr = WORDS_TO_EXCLUDE; - - private Set reservedWords; + private static final String RESERVED_WORDS = "get,delete"; + private static final String PATCH_VERB = "patch"; public OAR106ResourcesByPatchVerbCheck() { - init(); - } - - private void init() { - reservedWords = Arrays.stream(reservedWordsStr.split(",")) - .map(String::toLowerCase) - .map(String::trim) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - if ("patch".equalsIgnoreCase(node.key().getTokenValue())) { - JsonNode pathNode = findParentPathNode(node); - if (pathNode != null) { - String path = pathNode.key().getTokenValue(); - if (!isCorrect(path)) { - String formattedMessage = formatMessage(path); - addIssue(KEY, formattedMessage, node.key()); - } - } - } - } - - private JsonNode findParentPathNode(JsonNode node) { - JsonNode parent = (JsonNode) node.getParent(); - while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH || parent.getType() == OpenApi3Grammar.PATH || parent.getType() == OpenApi31Grammar.PATH)) { - parent = (JsonNode) parent.getParent(); - } - return parent; - } - - private boolean isCorrect(String path) { - String[] parts = Stream.of(path.split("/")) - .filter(p -> !p.trim().isEmpty()) - .toArray(String[]::new); - if (parts.length == 0) return true; - - for (String part : parts) { - if (isVariable(part)) { - String variableName = part.substring(1, part.length() - 1); - if (reservedWords.contains(variableName.toLowerCase())) { - return false; - } - } else if (reservedWords.contains(part.toLowerCase())) { - return false; - } - } - - return true; - } - - private boolean isVariable(String part) { - return part.startsWith("{") && part.endsWith("}"); - } - - private String formatMessage(String path) { - String[] parts = path.split("/"); - String resource = parts.length > 1 ? parts[1] : ""; - String nextPart = parts.length > 2 ? "/" + parts[2] : ""; - return translate(MESSAGE, resource + nextPart); + super(KEY, MESSAGE, PATCH_VERB, RESERVED_WORDS, false); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheck.java index 4d60666d..ef7193fa 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheck.java @@ -1,102 +1,16 @@ package apiaddicts.sonar.openapi.checks.operations; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR107ResourcesByDeleteVerbCheck.KEY) -public class OAR107ResourcesByDeleteVerbCheck extends BaseCheck { +public class OAR107ResourcesByDeleteVerbCheck extends AbstractResourcesByVerbCheck { public static final String KEY = "OAR107"; private static final String MESSAGE = "OAR107.error"; - private static final String WORDS_TO_EXCLUDE = "get,delete"; - - @RuleProperty( - key = "words-to-exclude", - description = "Comma-separated list of reserved words that should not appear in the path", - defaultValue = WORDS_TO_EXCLUDE - ) - private String reservedWordsStr = WORDS_TO_EXCLUDE; - - private Set reservedWords; + private static final String RESERVED_WORDS = "get,delete"; + private static final String DELETE_VERB = "delete"; public OAR107ResourcesByDeleteVerbCheck() { - init(); - } - - private void init() { - reservedWords = Arrays.stream(reservedWordsStr.split(",")) - .map(String::toLowerCase) - .map(String::trim) - .collect(Collectors.toSet()); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - public void visitNode(JsonNode node) { - if ("delete".equalsIgnoreCase(node.key().getTokenValue())) { - JsonNode pathNode = findParentPathNode(node); - if (pathNode != null) { - String path = pathNode.key().getTokenValue(); - if (!isCorrect(path)) { - String formattedMessage = formatMessage(path); - addIssue(KEY, formattedMessage, node.key()); - } - } - } - } - - private JsonNode findParentPathNode(JsonNode node) { - JsonNode parent = (JsonNode) node.getParent(); - while (parent != null && !(parent.getType() == OpenApi2Grammar.PATH || parent.getType() == OpenApi3Grammar.PATH || parent.getType() == OpenApi31Grammar.PATH)) { - parent = (JsonNode) parent.getParent(); - } - return parent; - } - - private boolean isCorrect(String path) { - String[] parts = Stream.of(path.split("/")) - .filter(p -> !p.trim().isEmpty()) - .toArray(String[]::new); - if (parts.length == 0) return true; - - for (String part : parts) { - if (isVariable(part)) { - String variableName = part.substring(1, part.length() - 1); - if (reservedWords.contains(variableName.toLowerCase())) { - return false; - } - } else if (reservedWords.contains(part.toLowerCase())) { - return false; - } - } - - return true; - } - - private boolean isVariable(String part) { - return part.startsWith("{") && part.endsWith("}"); - } - - private String formatMessage(String path) { - String[] parts = path.split("/"); - String resource = parts.length > 1 ? parts[1] : ""; - String nextPart = parts.length > 2 ? "/" + parts[2] : ""; - return translate(MESSAGE, resource + nextPart); + super(KEY, MESSAGE, DELETE_VERB, RESERVED_WORDS, false); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/AbstractPathResponseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/AbstractPathResponseCheck.java new file mode 100644 index 00000000..62772a33 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/AbstractPathResponseCheck.java @@ -0,0 +1,85 @@ +package apiaddicts.sonar.openapi.checks.owasp; + +import apiaddicts.sonar.openapi.checks.security.AbstractPathAwareOperationCheck; +import com.sonar.sslr.api.AstNodeType; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import org.sonar.check.RuleProperty; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class AbstractPathResponseCheck extends AbstractPathAwareOperationCheck { + + protected final String ruleKey; + protected final String messageKey; + protected final String defaultPaths; + protected final String defaultStrategy; + + protected static final String PATH_STRATEGY_EXCLUDE = "/exclude"; + protected static final String PATH_STRATEGY_INCLUDE = "/include"; + + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = "" + ) + protected String pathsStr; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = "/exclude" + ) + protected String pathCheckStrategy; + + protected Set paths; + + protected AbstractPathResponseCheck( + String ruleKey, + String messageKey, + String defaultPaths, + String defaultStrategy + ) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + this.defaultPaths = defaultPaths; + this.defaultStrategy = defaultStrategy; + this.pathsStr = defaultPaths; + this.pathCheckStrategy = defaultStrategy; + } + + @Override + protected void visitFile(JsonNode root) { + paths = parsePaths(pathsStr); + super.visitFile(root); + } + + @Override + protected void handleOperation(JsonNode node, AstNodeType type) { + if (shouldSkipNode(currentPath)) return; + validateOperation(node, currentPath); + } + + protected boolean shouldSkipNode(String path) { + if (PATH_STRATEGY_EXCLUDE.equals(pathCheckStrategy)) { + return paths.contains(path); + } + if (PATH_STRATEGY_INCLUDE.equals(pathCheckStrategy)) { + return !paths.contains(path); + } + return false; + } + + protected Set parsePaths(String pathsStr) { + if (pathsStr == null || pathsStr.trim().isEmpty()) { + return new HashSet<>(); + } + return Arrays.stream(pathsStr.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + } + + protected abstract void validateOperation(JsonNode node, String currentPath); +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java index 5100028a..3ed3cd59 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR070BrokenAccessControlCheck.java @@ -1,54 +1,49 @@ package apiaddicts.sonar.openapi.checks.owasp; -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.parameters.AbstractParameterCheck; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import java.util.Set; @Rule(key = OAR070BrokenAccessControlCheck.KEY) -public class OAR070BrokenAccessControlCheck extends BaseCheck { +public class OAR070BrokenAccessControlCheck extends AbstractParameterCheck { public static final String KEY = "OAR070"; private static final String MESSAGE = "OAR070.error"; @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PARAMETER, OpenApi3Grammar.PARAMETER, OpenApi31Grammar.PARAMETER); - } - - @Override - public void visitNode(JsonNode node) { - visitParameterNode(node); - } + protected void visitParameterNode(JsonNode node) { - public void visitParameterNode(JsonNode node) { JsonNode inNode = node.get("in"); - if (inNode != null && "path".equals(inNode.getTokenValue())) { - JsonNode nameNode = node.get("name"); - JsonNode typeNode = node.get("type"); - JsonNode schemaNode = node.get("schema"); - - boolean isNumericType = typeNode != null && ("integer".equals(typeNode.getTokenValue()) || - "number".equals(typeNode.getTokenValue()) || - "float".equals(typeNode.getTokenValue())); - - if (!isNumericType && schemaNode != null) { - JsonNode schemaTypeNode = schemaNode.get("type"); - isNumericType = schemaTypeNode != null && ("integer".equals(schemaTypeNode.getTokenValue()) || - "number".equals(schemaTypeNode.getTokenValue()) || - "float".equals(schemaTypeNode.getTokenValue())); - typeNode = schemaTypeNode; - } - - if (nameNode != null && isNumericType) { - addIssue(KEY, translate(MESSAGE), typeNode); - } + + if (inNode == null || !"path".equals(inNode.getTokenValue())) { + return; + } + + JsonNode nameNode = node.get("name"); + JsonNode typeNode = node.get("type"); + JsonNode schemaNode = node.get("schema"); + + boolean isNumericType = + typeNode != null && + ("integer".equals(typeNode.getTokenValue()) || + "number".equals(typeNode.getTokenValue()) || + "float".equals(typeNode.getTokenValue())); + + if (!isNumericType && schemaNode != null) { + + JsonNode schemaTypeNode = schemaNode.get("type"); + + isNumericType = + schemaTypeNode != null && + ("integer".equals(schemaTypeNode.getTokenValue()) || + "number".equals(schemaTypeNode.getTokenValue()) || + "float".equals(schemaTypeNode.getTokenValue())); + + typeNode = schemaTypeNode; + } + + if (nameNode != null && isNumericType) { + addIssue(KEY, translate(MESSAGE), typeNode); } } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheck.java index b686b0d5..fd5a827c 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheck.java @@ -1,95 +1,27 @@ package apiaddicts.sonar.openapi.checks.owasp; -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 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 java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; @Rule(key = OAR073RateLimitCheck.KEY) -public class OAR073RateLimitCheck extends BaseCheck { +public class OAR073RateLimitCheck extends AbstractPathResponseCheck { public static final String KEY = "OAR073"; private static final String MESSAGE = "OAR073.error"; - private static final String DEFAULT_PATH = "/status, /health-check"; - private static final String PATH_STRATEGY = "/exclude"; - - private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; - private static final String PATH_STRATEGY_INCLUDE = "/include"; - - @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; + private static final String DEFAULT_PATH_STATUS = "/status"; + private static final String DEFAULT_PATHS = DEFAULT_PATH_STATUS + ", /health-check"; + private static final String DEFAULT_STRATEGY = "/exclude"; - private Set exclusion; - private String currentPath; - - @Override - protected void visitFile(JsonNode root) { - exclusion = parsePaths(pathsStr); - super.visitFile(root); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); + public OAR073RateLimitCheck() { + super(KEY, MESSAGE, DEFAULT_PATHS, DEFAULT_STRATEGY); } @Override - public void visitNode(JsonNode node) { - if (node.getType() == OpenApi2Grammar.PATH || node.getType() == OpenApi3Grammar.PATH || node.getType() == OpenApi31Grammar.PATH) { - currentPath = node.key().getTokenValue(); - } else if (node.getType() == OpenApi2Grammar.OPERATION || node.getType() == OpenApi3Grammar.OPERATION || node.getType() == OpenApi31Grammar.OPERATION) { - if (shouldSkipNode(currentPath)) { - return; - } - visitOperationNode(node); - } - } - - private boolean shouldSkipNode(String currentPath) { - if (pathCheckStrategy.equals(PATH_STRATEGY_EXCLUDE)) { - return exclusion.contains(currentPath); - } else if (pathCheckStrategy.equals(PATH_STRATEGY_INCLUDE)) { - return !exclusion.contains(currentPath); - } - return false; - } - - private void visitOperationNode(JsonNode node) { + protected void validateOperation(JsonNode node, String currentPath) { JsonNode responsesNode = node.get("responses"); - if (responsesNode != null) { - JsonNode node429 = responsesNode.get("429"); - if (node429.isMissing()) { - addIssue(KEY, translate(MESSAGE), responsesNode.key()); - } - } - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")).map(String::trim).collect(Collectors.toSet()); - } else { - return new HashSet<>(); + if (responsesNode != null && responsesNode.get("429").isMissing()) { + addIssue(ruleKey, translate(messageKey), responsesNode.key()); } } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractParameterCheck.java new file mode 100644 index 00000000..c4676a78 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractParameterCheck.java @@ -0,0 +1,30 @@ +package apiaddicts.sonar.openapi.checks.parameters; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.Set; + +public abstract class AbstractParameterCheck extends BaseCheck { + + @Override + public Set subscribedKinds() { + return ImmutableSet.of( + OpenApi2Grammar.PARAMETER, + OpenApi3Grammar.PARAMETER, + OpenApi31Grammar.PARAMETER + ); + } + + @Override + public void visitNode(JsonNode node) { + visitParameterNode(node); + } + + protected abstract void visitParameterNode(JsonNode node); +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java index 461053f5..f53c3d0c 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR060QueryParametersOptionalCheck.java @@ -1,37 +1,23 @@ package apiaddicts.sonar.openapi.checks.parameters; - -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -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 com.google.common.collect.ImmutableSet; import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; -import java.util.Set; - @Rule(key = OAR060QueryParametersOptionalCheck.KEY) -public class OAR060QueryParametersOptionalCheck extends BaseCheck { +public class OAR060QueryParametersOptionalCheck extends AbstractParameterCheck { public static final String KEY = "OAR060"; private static final String MESSAGE = "OAR060.error"; @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PARAMETER, OpenApi3Grammar.PARAMETER, OpenApi31Grammar.PARAMETER); - } + protected void visitParameterNode(JsonNode node) { - @Override - public void visitNode(JsonNode node) { - visitParameterNode(node); - } - - public void visitParameterNode(JsonNode node) { JsonNode inNode = node.get("in"); + if (inNode != null && "query".equals(inNode.getTokenValue())) { + JsonNode requiredNode = node.get("required"); + if (requiredNode != null && "true".equals(requiredNode.getTokenValue())) { addIssue(KEY, translate(MESSAGE), requiredNode); } diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractForbiddenQueryCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractForbiddenQueryCheck.java new file mode 100644 index 00000000..ad11dbc9 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractForbiddenQueryCheck.java @@ -0,0 +1,78 @@ +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.sslr.yaml.grammar.JsonNode; +import org.sonar.check.RuleProperty; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class AbstractForbiddenQueryCheck extends AbstractPathAwareOperationCheck { + + private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; + private static final String PATH_STRATEGY_INCLUDE = "/include"; + + @RuleProperty( + key = "paths", + description = "List of explicit paths to include/exclude from this rule separated by comma", + defaultValue = "/examples" + ) + protected String pathsStr; + + @RuleProperty( + key = "pathValidationStrategy", + description = "Path validation strategy (include/exclude)", + defaultValue = "/include" + ) + protected String pathCheckStrategy; + + protected Set paths = new HashSet<>(); + + protected final String ruleKey; + protected final String messageKey; + + protected AbstractForbiddenQueryCheck(String ruleKey, String messageKey) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + } + + @Override + protected void visitFile(JsonNode root) { + if (pathsStr != null && !pathsStr.trim().isEmpty()) { + paths = Stream.of(pathsStr.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + } + super.visitFile(root); + } + + @Override + protected void handleOperation(JsonNode node, AstNodeType type) { + String operation = node.key().getTokenValue().toLowerCase(); + boolean isMethod = ImmutableSet.of("get", "post", "put", "patch", "delete") + .contains(operation); + + if (shouldExcludePath() || !isMethod) return; + + JsonNode parameters = node.get("parameters"); + if (parameters == null || parameters.isMissing() || parameters.isNull()) return; + + validateParameters(parameters, type == OpenApi2Grammar.OPERATION); + } + + protected abstract void validateParameters(JsonNode parametersNode, boolean isV2); + + private boolean shouldExcludePath() { + if (PATH_STRATEGY_EXCLUDE.equals(pathCheckStrategy)) { + return paths.contains(currentPath); + } + if (PATH_STRATEGY_INCLUDE.equals(pathCheckStrategy)) { + return !paths.contains(currentPath); + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractFormatCheck2.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractFormatCheck2.java deleted file mode 100644 index 22780805..00000000 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractFormatCheck2.java +++ /dev/null @@ -1,34 +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 AbstractFormatCheck2 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 typeNode = node.get("type"); - String type = typeNode.getTokenValue(); - JsonNode formatNode = node.get("format"); - String format = formatNode.isMissing() ? null : formatNode.getTokenValue(); - validate(type, format, typeNode); - } - - public abstract void validate(String type, String format, JsonNode typeNode); -} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPathAwareOperationCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPathAwareOperationCheck.java new file mode 100644 index 00000000..64f34a30 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractPathAwareOperationCheck.java @@ -0,0 +1,44 @@ +package apiaddicts.sonar.openapi.checks.security; + +import apiaddicts.sonar.openapi.checks.BaseCheck; +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 org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.Set; + +public abstract class AbstractPathAwareOperationCheck extends BaseCheck { + + protected String currentPath; + + @Override + public Set subscribedKinds() { + return ImmutableSet.of( + OpenApi2Grammar.PATH, + OpenApi3Grammar.PATH, + OpenApi31Grammar.PATH, + OpenApi2Grammar.OPERATION, + OpenApi3Grammar.OPERATION, + OpenApi31Grammar.OPERATION + ); + } + + @Override + public void visitNode(JsonNode node) { + AstNodeType type = node.getType(); + + if (type == OpenApi2Grammar.PATH + || type == OpenApi3Grammar.PATH + || type == OpenApi31Grammar.PATH) { + currentPath = node.key().getTokenValue(); + return; + } + + handleOperation(node, type); + } + + protected abstract void handleOperation(JsonNode node, AstNodeType type); +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractSecurityResponseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractSecurityResponseCheck.java new file mode 100644 index 00000000..7a7de47e --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractSecurityResponseCheck.java @@ -0,0 +1,83 @@ +package apiaddicts.sonar.openapi.checks.security; + +import com.google.common.collect.ImmutableSet; +import com.sonar.sslr.api.AstNodeType; +import org.sonar.check.RuleProperty; +import apiaddicts.sonar.openapi.checks.BaseCheck; +import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar; +import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class AbstractSecurityResponseCheck extends BaseCheck { + + protected final String ruleKey; + protected final String messageKey; + protected final String defaultResponseCodes; + + @RuleProperty( + key = "expected-codes", + description = "Expected response codes.", + defaultValue = "" + ) + protected String expectedCodesStr; + + protected Set expectedCodes; + protected boolean hasGlobalSecurity = false; + + protected AbstractSecurityResponseCheck(String ruleKey, String messageKey, String defaultResponseCodes) { + this.ruleKey = ruleKey; + this.messageKey = messageKey; + this.defaultResponseCodes = defaultResponseCodes; + this.expectedCodesStr = defaultResponseCodes; + } + + @Override + public Set subscribedKinds() { + return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); + } + + @Override + protected void visitFile(JsonNode root) { + expectedCodes = Arrays.stream(expectedCodesStr.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + hasGlobalSecurity = hasSecurity(root); + } + + @Override + public void visitNode(JsonNode node) { + validateSecurityResponse(node); + } + + private void validateSecurityResponse(JsonNode node) { + JsonNode responsesNode = node.get("responses"); + Set currentCodes = responsesNode.properties().stream() + .map(JsonNode::key) + .map(JsonNode::getTokenValue) + .collect(Collectors.toSet()); + + Set missingCodes = expectedCodes.stream() + .filter(code -> !currentCodes.contains(code)) + .collect(Collectors.toSet()); + + if (hasSecurity(node) || hasGlobalSecurity) { + validateExpectedCodes(missingCodes, responsesNode); + } + } + + private void validateExpectedCodes(Set missingCodes, JsonNode responsesNode) { + missingCodes.stream() + .sorted() + .forEach(code -> addIssue(ruleKey, translate(messageKey, code), responsesNode.key())); + } + + protected boolean hasSecurity(JsonNode node) { + JsonNode security = node.get("security"); + return !security.isMissing() && !security.isNull() && !security.elements().isEmpty(); + } +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractTypedParameterIntegrityCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractTypedParameterIntegrityCheck.java new file mode 100644 index 00000000..c3dfde43 --- /dev/null +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/AbstractTypedParameterIntegrityCheck.java @@ -0,0 +1,53 @@ +package apiaddicts.sonar.openapi.checks.security; + +import apiaddicts.sonar.openapi.checks.BaseCheck; +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 org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; +import java.util.Set; + +public abstract class AbstractTypedParameterIntegrityCheck extends BaseCheck { + + protected final String ruleKey; + protected final String messageKey; + + protected AbstractTypedParameterIntegrityCheck(String ruleKey,String messageKey) { + this.ruleKey=ruleKey; + this.messageKey=messageKey; + } + + @Override + public Set subscribedKinds() { + return ImmutableSet.of( + OpenApi2Grammar.PARAMETER, + OpenApi3Grammar.PARAMETER, + OpenApi31Grammar.PARAMETER + ); + } + + @Override + public void visitNode(JsonNode node) { + if(node.getType() instanceof OpenApi2Grammar) validateSwaggerParameter(node); + else validateOas3Parameter(node); + } + + private void validateOas3Parameter(JsonNode node) { + JsonNode schema=node.get("schema"); + if(schema==null||schema.isMissing()) return; + JsonNode typeNode=schema.get("type"); + if(!isTargetType(typeNode)) return; + validateTypedNode(schema,typeNode); + } + + private void validateSwaggerParameter(JsonNode node) { + JsonNode typeNode=node.get("type"); + if(!isTargetType(typeNode)) return; + validateTypedNode(node,typeNode); + } + + protected abstract boolean isTargetType(JsonNode typeNode); + protected abstract void validateTypedNode(JsonNode container,JsonNode typeNode); +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR035UnauthorizedResponseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR035UnauthorizedResponseCheck.java index 7839639a..bafb3aee 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR035UnauthorizedResponseCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR035UnauthorizedResponseCheck.java @@ -1,70 +1,15 @@ package apiaddicts.sonar.openapi.checks.security; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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.Arrays; -import java.util.Set; -import java.util.stream.Collectors; @Rule(key = OAR035UnauthorizedResponseCheck.KEY) -public class OAR035UnauthorizedResponseCheck extends BaseCheck { - public static final String KEY = "OAR035"; - public static final String MESSAGE = "OAR035.error"; +public class OAR035UnauthorizedResponseCheck extends AbstractSecurityResponseCheck { + public static final String KEY = "OAR035"; + private static final String MESSAGE = "OAR035.error"; private static final String RESPONSE_CODES_STR = "401"; - @RuleProperty( - key = "expected-codes", - description = "Expected response codes.", - defaultValue = RESPONSE_CODES_STR - ) - private String expectedCodesStr = RESPONSE_CODES_STR; - private Set expectedCodes; - - private boolean hasGlobalSecurity = false; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - expectedCodes = Arrays.stream(expectedCodesStr.split(",")).map(String::trim).collect(Collectors.toSet()); - hasGlobalSecurity = hasSecurity(root); - } - - @Override - public void visitNode(JsonNode node) { - validateSecurityResponse(node); - } - - private void validateSecurityResponse(JsonNode node) { - JsonNode responsesNode = node.get("responses"); - Set currentCodes = responsesNode.properties().stream().map(JsonNode::key).map(JsonNode::getTokenValue).collect(Collectors.toSet()); - Set copyExpectedCodes = expectedCodes.stream().collect(Collectors.toSet()); - copyExpectedCodes.removeAll(currentCodes); - if (hasSecurity(node) || hasGlobalSecurity) { - validateExpectedCodes(copyExpectedCodes, responsesNode); - } - } - - private void validateExpectedCodes(Set copyExpectedCodes, JsonNode responsesNode) { - for (String missingCode : copyExpectedCodes.stream().sorted().collect(Collectors.toList())) { - addIssue(KEY, translate(MESSAGE, missingCode), responsesNode.key()); - } - } - - private boolean hasSecurity(JsonNode node) { - JsonNode security = node.get("security"); - return !security.isMissing() && !security.isNull() && !security.elements().isEmpty(); + public OAR035UnauthorizedResponseCheck() { + super(KEY, MESSAGE, RESPONSE_CODES_STR); } -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java index 8711188d..0eb84e65 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR074NumericParameterIntegrityCheck.java @@ -1,81 +1,36 @@ 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 org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import org.sonar.check.Rule; -import apiaddicts.sonar.openapi.checks.BaseCheck; - -import java.util.Set; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; @Rule(key = OAR074NumericParameterIntegrityCheck.KEY) -public class OAR074NumericParameterIntegrityCheck extends BaseCheck { +public class OAR074NumericParameterIntegrityCheck extends AbstractTypedParameterIntegrityCheck { public static final String KEY = "OAR074"; private static final String MESSAGE = "OAR074.error"; - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PARAMETER, OpenApi3Grammar.PARAMETER, OpenApi31Grammar.PARAMETER); + public OAR074NumericParameterIntegrityCheck() { + super(KEY, MESSAGE); } @Override - public void visitNode(JsonNode node) { - if (node.getType() instanceof OpenApi2Grammar) { - visitSwaggerParameterNode(node); - } else { - visitParameterNode(node); - } - } - - public void visitParameterNode(JsonNode node) { - JsonNode schemaNode = node.get("schema"); - - if (schemaNode != null) { - JsonNode typeNode = schemaNode.get("type"); - - boolean isNumericType = typeNode != null && ("integer".equals(typeNode.getTokenValue()) || - "number".equals(typeNode.getTokenValue()) || - "float".equals(typeNode.getTokenValue())); - - if (isNumericType) { - JsonNode minNode = schemaNode.get("minimum"); - JsonNode maxNode = schemaNode.get("maximum"); - JsonNode formatNode = schemaNode.get("format"); - - boolean lacksLengthRestriction = (minNode.isMissing() && !maxNode.isMissing()) || (!minNode.isMissing() && maxNode.isMissing()); - boolean formatAlone = !formatNode.isMissing() && minNode.isMissing() && maxNode.isMissing(); - boolean allMissing = minNode.isMissing() && maxNode.isMissing() && formatNode.isMissing(); - boolean lacksRestriction = lacksLengthRestriction || allMissing; - if (!formatAlone && lacksRestriction) { - addIssue(KEY, translate(MESSAGE), typeNode); - } - } - } + protected boolean isTargetType(JsonNode typeNode) { + if(typeNode == null || typeNode.isMissing()) return false; + String t = typeNode.getTokenValue(); + return "integer".equals(t) || "number".equals(t) || "float".equals(t); } - public void visitSwaggerParameterNode(JsonNode node) { - JsonNode typeNode = node.get("type"); - - boolean isNumericType = typeNode != null && ("integer".equals(typeNode.getTokenValue()) || - "number".equals(typeNode.getTokenValue()) || - "float".equals(typeNode.getTokenValue())); + @Override + protected void validateTypedNode(JsonNode node,JsonNode typeNode) { + JsonNode min = node.get("minimum"); + JsonNode max = node.get("maximum"); + JsonNode format = node.get("format"); - if (isNumericType) { - JsonNode minNode = node.get("minimum"); - JsonNode maxNode = node.get("maximum"); - JsonNode formatNode = node.get("format"); + boolean lacksPair = (min.isMissing() && !max.isMissing()) || (!min.isMissing() && max.isMissing()); + boolean formatAlone = !format.isMissing() && min.isMissing() && max.isMissing(); + boolean allMissing = min.isMissing() && max.isMissing() && format.isMissing(); + boolean invalid = lacksPair || allMissing; - boolean lacksLengthRestriction = (minNode.isMissing() && !maxNode.isMissing()) || (!minNode.isMissing() && maxNode.isMissing()); - boolean formatAlone = !formatNode.isMissing() && minNode.isMissing() && maxNode.isMissing(); - boolean allMissing = minNode.isMissing() && maxNode.isMissing() && formatNode.isMissing(); - boolean lacksRestriction = lacksLengthRestriction || allMissing; - if (!formatAlone && lacksRestriction) { - addIssue(KEY, translate(MESSAGE), typeNode); - } - } + if(!formatAlone && invalid) addIssue(ruleKey,translate(messageKey),typeNode); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java index c8ba2532..5e2a3b8c 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR075StringParameterIntegrityCheck.java @@ -1,87 +1,46 @@ package apiaddicts.sonar.openapi.checks.security; -import apiaddicts.sonar.openapi.checks.BaseCheck; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; import java.util.Arrays; 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 = OAR075StringParameterIntegrityCheck.KEY) -public class OAR075StringParameterIntegrityCheck extends BaseCheck { +public class OAR075StringParameterIntegrityCheck extends AbstractTypedParameterIntegrityCheck { public static final String KEY = "OAR075"; private static final String MESSAGE = "OAR075.error"; - private static final String PARAMETER_INTEGRITY = "minLength,maxLength,enum,format"; + private static final String DEFAULT = "minLength,maxLength,enum,format"; @RuleProperty( key = "parameter_integrity", - description = "String parameters integrity (minLength,maxLength,pattern,enum,format).Comma separated.", - defaultValue = PARAMETER_INTEGRITY + description = "String integrity checks", + defaultValue = DEFAULT ) - private String parameterIntegrityStr = PARAMETER_INTEGRITY; - + private String integrityStr = DEFAULT; - private Set getActiveIntegrityChecks() { - return Arrays.stream(parameterIntegrityStr.split(",")) - .map(String::trim) - .collect(Collectors.toSet()); + public OAR075StringParameterIntegrityCheck() { + super(KEY, MESSAGE); } @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PARAMETER, OpenApi3Grammar.PARAMETER, OpenApi31Grammar.PARAMETER); + protected boolean isTargetType(JsonNode typeNode) { + return typeNode != null && !typeNode.isMissing() && "string".equals(typeNode.getTokenValue()); } @Override - public void visitNode(JsonNode node) { - if (node.getType() instanceof OpenApi2Grammar) { - visitSwaggerParameterNode(node); - } else { - visitParameterNode(node); - } - } - - public void visitParameterNode(JsonNode node) { - JsonNode schemaNode = node.get("schema"); - - if (schemaNode != null) { - JsonNode typeNode = schemaNode.get("type"); - - boolean isStringType = typeNode != null && "string".equals(typeNode.getTokenValue()); - - if (isStringType) { - Set checks = getActiveIntegrityChecks(); - boolean hasChecks = checks.stream() - .allMatch(key -> schemaNode.get(key) != null && !schemaNode.get(key).isMissing()); - - if (!hasChecks) { - addIssue(KEY, translate(MESSAGE), typeNode); - } - } - } - } - - public void visitSwaggerParameterNode(JsonNode node) { - JsonNode typeNode = node.get("type"); - - boolean isStringType = typeNode != null && "string".equals(typeNode.getTokenValue()); - - if (isStringType) { + protected void validateTypedNode(JsonNode node,JsonNode typeNode) { + Set checks = Arrays.stream(integrityStr.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); - Set checks = getActiveIntegrityChecks(); - boolean hasChecks = checks.stream() - .allMatch(key -> node.get(key) != null && !node.get(key).isMissing()); + boolean ok = checks.stream().allMatch(k->{ + JsonNode n = node.get(k); + return n != null && !n.isMissing(); + }); - if (!hasChecks) { - addIssue(KEY, translate(MESSAGE), typeNode); - } - } + if(!ok) addIssue(ruleKey,translate(messageKey),typeNode); } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java index 0afcc5c3..0a6286e5 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR076NumericFormatCheck.java @@ -1,10 +1,13 @@ package apiaddicts.sonar.openapi.checks.security; import org.sonar.check.Rule; + +import apiaddicts.sonar.openapi.checks.format.AbstractFormatCheck; + import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; @Rule(key = OAR076NumericFormatCheck.KEY) -public class OAR076NumericFormatCheck extends AbstractFormatCheck2 { +public class OAR076NumericFormatCheck extends AbstractFormatCheck { public static final String KEY = "OAR076"; private static final String MESSAGE = "OAR076.error"; diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404Check.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404Check.java index b0acc063..015ce89c 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404Check.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404Check.java @@ -1,103 +1,35 @@ package apiaddicts.sonar.openapi.checks.security; -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.AstNodeType; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; +import apiaddicts.sonar.openapi.checks.owasp.AbstractPathResponseCheck; +import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode; @Rule(key = OAR079PathParameter404Check.KEY) -public class OAR079PathParameter404Check extends BaseCheck { +public class OAR079PathParameter404Check extends AbstractPathResponseCheck { public static final String KEY = "OAR079"; private static final String MESSAGE = "OAR079.error"; - private static final String DEFAULT_PATH = "/status"; - private static final String PATH_STRATEGY = "/exclude"; - - private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; - private static final String PATH_STRATEGY_INCLUDE = "/include"; - - @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; + private static final String DEFAULT_PATHS = "/status"; + private static final String DEFAULT_STRATEGY = "/exclude"; - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - private Set paths; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - paths = parsePaths(pathsStr); - super.visitFile(root); + public OAR079PathParameter404Check() { + super(KEY, MESSAGE, DEFAULT_PATHS, DEFAULT_STRATEGY); } @Override - public void visitNode(JsonNode node) { - visitOperationNode(node); - } - - private void visitOperationNode(JsonNode node) { - String currentPath = getCurrentPath(node); - boolean isExcluded = PATH_STRATEGY_EXCLUDE.equals(pathCheckStrategy) && paths.contains(currentPath); - boolean isNotIncluded = PATH_STRATEGY_INCLUDE.equals(pathCheckStrategy) && !paths.contains(currentPath); - - if (isExcluded || isNotIncluded) return; - + protected void validateOperation(JsonNode node, String currentPath) { JsonNode parametersNode = node.get("parameters"); if (parametersNode == null || !parametersNode.isArray()) return; for (JsonNode parameterNode : parametersNode.elements()) { - if (isPathParameter(parameterNode)) { + JsonNode inNode = parameterNode.get("in"); + if (inNode != null && "path".equals(inNode.getTokenValue())) { JsonNode responsesNode = node.get("responses"); if (responsesNode.get("404").isMissing()) { - addIssue(KEY, translate(MESSAGE), responsesNode.key()); + addIssue(ruleKey, translate(messageKey), responsesNode.key()); break; } } } } - - private boolean isPathParameter(JsonNode parameterNode) { - JsonNode inNode = parameterNode.get("in"); - return inNode != null && "path".equals(inNode.getTokenValue()); - } - - private String getCurrentPath(JsonNode node) { - JsonNode pathNode = (JsonNode) node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH); - if (pathNode != null) { - return pathNode.key().getTokenValue(); - } - return ""; - } - - private Set parsePaths(String pathsStr) { - if (!pathsStr.trim().isEmpty()) { - return Arrays.stream(pathsStr.split(",")).map(String::trim).collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR083ForbiddenQueryParamsCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR083ForbiddenQueryParamsCheck.java index 601300ca..8c4aeb29 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR083ForbiddenQueryParamsCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR083ForbiddenQueryParamsCheck.java @@ -1,104 +1,44 @@ package apiaddicts.sonar.openapi.checks.security; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -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.HashSet; import java.util.Set; +import java.util.Arrays; import java.util.stream.Collectors; -import java.util.stream.Stream; @Rule(key = OAR083ForbiddenQueryParamsCheck.KEY) -public class OAR083ForbiddenQueryParamsCheck extends BaseCheck { +public class OAR083ForbiddenQueryParamsCheck extends AbstractForbiddenQueryCheck { public static final String KEY = "OAR083"; private static final String MESSAGE = "OAR083.error"; - private static final String FORBIDDEN_QUERY_PARAMS = "email, password"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; - - private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; - private static final String PATH_STRATEGY_INCLUDE = "/include"; @RuleProperty( key = "forbidden-query-params", description = "List of forbidden query params separated by comma", - defaultValue = FORBIDDEN_QUERY_PARAMS - ) - private String forbiddenQueryParamsStr = FORBIDDEN_QUERY_PARAMS; - - @RuleProperty( - key = "paths", - description = "List of explicit paths to include/exclude from this rule separated by comma", - defaultValue = DEFAULT_PATH + defaultValue = "email, password" ) - private String pathsStr = DEFAULT_PATH; + public String forbiddenQueryParamsStr = "email, password"; - @RuleProperty( - key = "pathValidationStrategy", - description = "Path validation strategy (include/exclude)", - defaultValue = PATH_STRATEGY - ) - private String pathCheckStrategy = PATH_STRATEGY; - - private Set forbiddenQueryParams = new HashSet<>(); - private Set paths; - private String currentPath; - - @Override - protected void visitFile(JsonNode root) { - if (!forbiddenQueryParamsStr.trim().isEmpty()) { - forbiddenQueryParams.addAll(Stream.of(forbiddenQueryParamsStr.split(",")).map(String::trim).collect(Collectors.toSet())); - } - if (!pathsStr.trim().isEmpty()) { - paths = Stream.of(pathsStr.split(",")).map(String::trim).collect(Collectors.toSet()); - } else { - paths = new HashSet<>(); - } - super.visitFile(root); - } + private final Set forbiddenQueryParams = new HashSet<>(); - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION , OpenApi31Grammar.OPERATION); + public OAR083ForbiddenQueryParamsCheck() { + super(KEY, MESSAGE); } @Override - public void visitNode(JsonNode node) { - AstNodeType type = node.getType(); - - if (type == OpenApi2Grammar.PATH || type == OpenApi3Grammar.PATH || type == OpenApi31Grammar.PATH) { - currentPath = node.key().getTokenValue(); - return; + protected void validateParameters(JsonNode parametersNode, boolean isV2) { + if (!forbiddenQueryParamsStr.trim().isEmpty() && forbiddenQueryParams.isEmpty()) { + forbiddenQueryParams.addAll(Arrays.asList(forbiddenQueryParamsStr.split(","))); } - String operationType = node.key().getTokenValue().toLowerCase(); - boolean isMethod = ImmutableSet.of("get", "post", "put", "patch", "delete").contains(operationType); - - if (shouldExcludePath() || !isMethod) return; - - JsonNode params = node.get("parameters"); - if (params == null || params.isMissing() || params.isNull()) return; - - validateParameters(params); - } - - private void validateParameters(JsonNode parametersNode) { - Set queryParams = new HashSet<>(); - parametersNode.elements().forEach(parameterNode -> { - JsonNode in = parameterNode.get("in"); - JsonNode name = parameterNode.get("name"); - if (in != null && "query".equals(in.getTokenValue()) && name != null && !name.isNull()) { - queryParams.add(name.getTokenValue()); - } - }); + Set queryParams = parametersNode.elements().stream() + .map(p -> p.get("name")) + .filter(n -> n != null && !n.isNull()) + .map(JsonNode::getTokenValue) + .collect(Collectors.toSet()); String forbidden = forbiddenQueryParams.stream() .filter(queryParams::contains) @@ -108,13 +48,4 @@ private void validateParameters(JsonNode parametersNode) { addIssue(KEY, translate(MESSAGE, forbidden), parametersNode.key()); } } - - private boolean shouldExcludePath() { - if (PATH_STRATEGY_EXCLUDE.equals(pathCheckStrategy)) { - return paths.contains(currentPath); - } else if (PATH_STRATEGY_INCLUDE.equals(pathCheckStrategy)) { - return !paths.contains(currentPath); - } - return false; - } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR084ForbiddenFormatsInQueryCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR084ForbiddenFormatsInQueryCheck.java index b6d950a4..0715850a 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR084ForbiddenFormatsInQueryCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR084ForbiddenFormatsInQueryCheck.java @@ -1,128 +1,48 @@ package apiaddicts.sonar.openapi.checks.security; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -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.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Arrays; @Rule(key = OAR084ForbiddenFormatsInQueryCheck.KEY) -public class OAR084ForbiddenFormatsInQueryCheck extends BaseCheck { +public class OAR084ForbiddenFormatsInQueryCheck extends AbstractForbiddenQueryCheck { public static final String KEY = "OAR084"; private static final String MESSAGE = "OAR084.error"; - private static final String FORBIDDEN_QUERY_FORMATS = "password"; - private static final String DEFAULT_PATH = "/examples"; - private static final String PATH_STRATEGY = "/include"; - - private static final String PATH_STRATEGY_EXCLUDE = "/exclude"; - private static final String PATH_STRATEGY_INCLUDE = "/include"; @RuleProperty( key = "forbidden-query-formats", - description = "List of forbidden query params separated by comma", - defaultValue = FORBIDDEN_QUERY_FORMATS - ) - private String forbiddenQueryFormatsStr = FORBIDDEN_QUERY_FORMATS; - - @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 + description = "List of forbidden query formats separated by comma", + defaultValue = "password" ) - private String pathCheckStrategy = PATH_STRATEGY; + public String forbiddenQueryFormatsStr = "password"; - private Set forbiddenQueryFormats = new HashSet<>(); - private Set paths; - private String currentPath; + private final Set forbiddenQueryFormats = new HashSet<>(); - @Override - protected void visitFile(JsonNode root) { - if (!forbiddenQueryFormatsStr.trim().isEmpty()) { - forbiddenQueryFormats.addAll(Stream.of(forbiddenQueryFormatsStr.split(",")).map(String::trim).collect(Collectors.toSet())); - } - if (!pathsStr.trim().isEmpty()) { - paths = Stream.of(pathsStr.split(",")).map(String::trim).collect(Collectors.toSet()); - } else { - paths = new HashSet<>(); - } - super.visitFile(root); - } - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH, OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); + public OAR084ForbiddenFormatsInQueryCheck() { + super(KEY, MESSAGE); } @Override - public void visitNode(JsonNode node) { - AstNodeType type = node.getType(); - - if (type == OpenApi2Grammar.PATH || type == OpenApi3Grammar.PATH || type == OpenApi31Grammar.PATH) { - currentPath = node.key().getTokenValue(); - return; + protected void validateParameters(JsonNode parametersNode, boolean isV2) { + if (!forbiddenQueryFormatsStr.trim().isEmpty() && forbiddenQueryFormats.isEmpty()) { + forbiddenQueryFormats.addAll(Arrays.asList(forbiddenQueryFormatsStr.split(","))); } - String op = node.key().getTokenValue().toLowerCase(); - boolean isMethod = ImmutableSet.of("get", "post", "put", "patch", "delete").contains(op); - - if (shouldExcludePath() || !isMethod) return; - - JsonNode params = node.get("parameters"); - if (params == null || params.isMissing() || params.isNull()) return; - - validateParameters(params, type == OpenApi2Grammar.OPERATION); - } - - private void validateParameters(JsonNode params, boolean isV2) { - List violations = new ArrayList<>(); - - params.elements().forEach(param -> { + parametersNode.elements().forEach(param -> { JsonNode in = param.get("in"); if (in == null || !"query".equals(in.getTokenValue())) return; JsonNode formatNode = isV2 ? param.get("format") : param.get("schema").get("format"); - if (formatNode != null && !formatNode.isMissing() && !formatNode.isNull() - && forbiddenQueryFormats.contains(formatNode.getTokenValue())) { - violations.add(formatNode); + if (formatNode != null && !formatNode.isMissing() && !formatNode.isNull() && + forbiddenQueryFormats.contains(formatNode.getTokenValue())) { + addIssue(KEY, translate(MESSAGE, formatNode.getTokenValue()), formatNode); } }); - - if (!violations.isEmpty()) { - String forbiddenStr = violations.stream() - .map(JsonNode::getTokenValue) - .distinct() - .collect(Collectors.joining(", ")); - - violations.forEach(node -> addIssue(KEY, translate(MESSAGE, forbiddenStr), node)); - } - } - - private boolean shouldExcludePath() { - if (PATH_STRATEGY_EXCLUDE.equals(pathCheckStrategy)) { - return paths.contains(currentPath); - } else if (PATH_STRATEGY_INCLUDE.equals(pathCheckStrategy)) { - return !paths.contains(currentPath); - } - return false; } } \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheck.java index 9494a9c0..626c6d1b 100644 --- a/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheck.java +++ b/src/main/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheck.java @@ -1,71 +1,15 @@ package apiaddicts.sonar.openapi.checks.security; -import com.google.common.collect.ImmutableSet; -import com.sonar.sslr.api.AstNodeType; import org.sonar.check.Rule; -import org.sonar.check.RuleProperty; -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.Arrays; -import java.util.Set; -import java.util.stream.Collectors; @Rule(key = OAR096ForbiddenResponseCheck.KEY) -public class OAR096ForbiddenResponseCheck extends BaseCheck { - public static final String KEY = "OAR096"; - public static final String MESSAGE = "OAR096.error"; +public class OAR096ForbiddenResponseCheck extends AbstractSecurityResponseCheck { + public static final String KEY = "OAR096"; + private static final String MESSAGE = "OAR096.error"; private static final String RESPONSE_CODES_STR = "403"; - @RuleProperty( - key = "expected-codes", - description = "Expected response codes.", - defaultValue = RESPONSE_CODES_STR - ) - private String expectedCodesStr = RESPONSE_CODES_STR; - private Set expectedCodes; - - private boolean hasGlobalSecurity = false; - - @Override - public Set subscribedKinds() { - return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION); - } - - @Override - protected void visitFile(JsonNode root) { - expectedCodes = Arrays.stream(expectedCodesStr.split(",")).map(String::trim).collect(Collectors.toSet()); - hasGlobalSecurity = hasSecurity(root); - } - - @Override - public void visitNode(JsonNode node) { - validateSecurityResponse(node); - } - - private void validateSecurityResponse(JsonNode node) { - JsonNode responsesNode = node.get("responses"); - Set currentCodes = responsesNode.properties().stream().map(JsonNode::key).map(JsonNode::getTokenValue).collect(Collectors.toSet()); - Set copyExpectedCodes = expectedCodes.stream().collect(Collectors.toSet()); - copyExpectedCodes.removeAll(currentCodes); - if (hasSecurity(node) || hasGlobalSecurity) { - validateExpectedCodes(copyExpectedCodes, responsesNode); - } - } - - private void validateExpectedCodes(Set copyExpectedCodes, JsonNode responsesNode) { - for (String missingCode : copyExpectedCodes.stream().sorted().collect(Collectors.toList())) { - addIssue(KEY, translate(MESSAGE, missingCode), responsesNode.key()); - } - } - - - private boolean hasSecurity(JsonNode node) { - JsonNode security = node.get("security"); - return !security.isMissing() && !security.isNull() && !security.elements().isEmpty(); + public OAR096ForbiddenResponseCheck() { + super(KEY, MESSAGE, RESPONSE_CODES_STR); } -} +} \ No newline at end of file diff --git a/src/main/java/apiaddicts/sonar/openapi/utils/VerbPathMatcher.java b/src/main/java/apiaddicts/sonar/openapi/utils/VerbPathMatcher.java index a26308f0..4c5825c8 100644 --- a/src/main/java/apiaddicts/sonar/openapi/utils/VerbPathMatcher.java +++ b/src/main/java/apiaddicts/sonar/openapi/utils/VerbPathMatcher.java @@ -20,8 +20,6 @@ public class VerbPathMatcher { public static final String DYNAMIC_OR_ME_PATH_PART_REGEX = "(" + DYNAMIC_PATH_PART_REGEX + "|\\bme\\b)"; - public static final String ONLY_ONE_ME_REGEX = "(?!.*\\bme\\b.*\\bme\\b).*\\bme\\b.*"; - public static final String SLASH = "\\/"; public static final String COLLECTION_PATH = SLASH + STATIC_PATH_PART_REGEX; @@ -74,6 +72,8 @@ public class VerbPathMatcher { public static final String DELETE_2ND_LEVEL = VERB_DELETE + ELEMENT_PATH_2ND + "$"; public static final String DELETE_3RD_LEVEL = VERB_DELETE + ELEMENT_PATH_3RD + "$"; + private static final Pattern ME_WORD_PATTERN = Pattern.compile("\\b" + ME_WORD + "\\b"); + private Map> patternsByVerb; private Map> exclusionsByVerb; @@ -97,8 +97,9 @@ private void processPatterns(String expression) { List patterns = Stream.of(regex).map(rx -> Pattern.compile(rx.trim())).map(p -> new PatternGroup(p, values)).collect(Collectors.toList()); for (String verb : verbs) { verb = verb.trim(); - patternsByVerb.putIfAbsent(verb, new LinkedList<>()); - patternsByVerb.get(verb).addAll(patterns); + patternsByVerb + .computeIfAbsent(verb, v -> new LinkedList<>()) + .addAll(patterns); } } } @@ -143,13 +144,18 @@ private Set strToSet(String str) { } public boolean matches(String path) { - Pattern mePattern = Pattern.compile(ONLY_ONE_ME_REGEX); - Matcher meMatcher = mePattern.matcher(path); - if (meMatcher.find()) { - return pattern.matcher(path).matches() && meMatcher.matches(); - } else { - return pattern.matcher(path).matches(); + if (!hasAtMostOneMe(path)) return false; + + return pattern.matcher(path).matches(); + } + + private boolean hasAtMostOneMe(String path) { + Matcher matcher = ME_WORD_PATTERN.matcher(path); + int count = 0; + while (matcher.find()) { + if (++count > 1) return false; } + return true; } public Set getValues() { diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR061GetMethodCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR061GetMethodCheckTest.java index 4cd38288..eb7d2c1b 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR061GetMethodCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR061GetMethodCheckTest.java @@ -45,6 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(3); + assertParameterProperties("mandatory-response-codes", null, RuleParamType.STRING); assertParameterProperties("paths", "/status, /another", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR062PostMethodCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR062PostMethodCheckTest.java index fb1ee136..c3d1a8b5 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR062PostMethodCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR062PostMethodCheckTest.java @@ -45,6 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(3); + assertParameterProperties("mandatory-response-codes", null, RuleParamType.STRING); assertParameterProperties("paths", "/status, /another", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR063PutMethodCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR063PutMethodCheckTest.java index 6a2db491..a0387f15 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR063PutMethodCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR063PutMethodCheckTest.java @@ -45,6 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(3); + assertParameterProperties("mandatory-response-codes", null, RuleParamType.STRING); assertParameterProperties("paths", "/status, /another", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR064PatchMethodCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR064PatchMethodCheckTest.java index bf753d4d..9df783e6 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR064PatchMethodCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR064PatchMethodCheckTest.java @@ -45,6 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(3); + assertParameterProperties("mandatory-response-codes", null, RuleParamType.STRING); assertParameterProperties("paths", "/status, /another", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR065DeleteMethodCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR065DeleteMethodCheckTest.java index 709a0d1b..4005f3b7 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR065DeleteMethodCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR065DeleteMethodCheckTest.java @@ -45,6 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(3); + assertParameterProperties("mandatory-response-codes", null, RuleParamType.STRING); assertParameterProperties("paths", "/status, /another", RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheckTest.java index 7230b3eb..021538cc 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR103ResourcesByGetVerbCheckTest.java @@ -35,6 +35,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "get,delete", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", null, RuleParamType.STRING); } } \ 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 0ee9516a..ec981f9c 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java @@ -35,6 +35,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "me,search", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", null, RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheckTest.java index 39c2c92d..fcbc8bdc 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR105ResourcesByPutVerbCheckTest.java @@ -35,6 +35,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "get,delete", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", null, RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheckTest.java index f47556ad..fa5dde1b 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR106ResourcesByPatchVerbCheckTest.java @@ -35,6 +35,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "get,delete", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", null, RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheckTest.java index 56db40f6..c54fc046 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR107ResourcesByDeleteVerbCheckTest.java @@ -35,6 +35,6 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("words-to-exclude", "get,delete", RuleParamType.STRING); + assertParameterProperties("words-to-exclude", null, RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheckTest.java index ad5de684..0d9d672b 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/owasp/OAR073RateLimitCheckTest.java @@ -45,7 +45,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(2); - assertParameterProperties("paths", "/status, /health-check", RuleParamType.STRING); + assertParameterProperties("paths", null, RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR035AuthorizationResponsesCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR035AuthorizationResponsesCheckTest.java index b5a210b2..9daca31d 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR035AuthorizationResponsesCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR035AuthorizationResponsesCheckTest.java @@ -40,7 +40,7 @@ public void verifyInV3WithoutAuthorizationResponses() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("expected-codes", "401", RuleParamType.STRING); + assertParameterProperties("expected-codes", null, RuleParamType.STRING); } @Override diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404CheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404CheckTest.java index ef430851..47637492 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404CheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR079PathParameter404CheckTest.java @@ -46,7 +46,7 @@ public void verifyRule() { @Override public void verifyParameters() { assertNumberOfParameters(2); - assertParameterProperties("paths", "/status", RuleParamType.STRING); + assertParameterProperties("paths", null, RuleParamType.STRING); assertParameterProperties("pathValidationStrategy", "/exclude", RuleParamType.STRING); } } \ No newline at end of file diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheckTest.java index c1108897..f921186a 100644 --- a/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheckTest.java +++ b/src/test/java/apiaddicts/sonar/openapi/checks/security/OAR096ForbiddenResponseCheckTest.java @@ -40,7 +40,7 @@ public void verifyInV3WithoutAuthorizationResponses() { @Override public void verifyParameters() { assertNumberOfParameters(1); - assertParameterProperties("expected-codes", "403", RuleParamType.STRING); + assertParameterProperties("expected-codes", null, RuleParamType.STRING); } @Override