From d302ca7340dc16dc6d06f559d43c1400636f0045 Mon Sep 17 00:00:00 2001 From: RenFengJiang <1111> Date: Fri, 5 Jul 2024 09:29:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=8F=90=E4=BE=9B=E7=BB=93=E6=9E=9C=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9Java=E8=A7=84=E5=88=99=E6=BC=8F=E6=8A=A5?= =?UTF-8?q?=E8=AF=AF=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rules/checkers/SessionDateChecker.java | 31 ++- .../com/keyware/sonar/cxx/CxxSquidSensor.java | 1 + .../org/sonar/cxx/squidbridge/AstScanner.java | 258 ++++++++++++++++++ .../sonar/java/rules/checkers/FileCheck.java | 26 +- .../checkers/HashSaltPassWordChecker.java | 21 +- .../rules/checkers/HostIdentityChecker.java | 81 ++++++ .../rules/checkers/InputSQLVerifyChecker.java | 2 +- .../checkers/Md5PassWordVerifyChecker.java | 20 +- .../rules/checkers/OptionsVerifyChecker.java | 95 +++++-- .../checkers/PasswordInputTagJavaChecker.java | 89 ++++++ .../rules/checkers/RSAEncryptionChecker.java | 75 +++-- .../rules/checkers/RedirectUrlChecker.java | 53 ++++ .../rules/checkers/SecurityCookieChecker.java | 38 +-- .../checkers/SessionCacheParamsChecker.java | 3 +- .../checkers/UploadFileVerifyChecker.java | 47 +++- .../checkers/UpperCycleLimitRuleChecker.java | 4 +- .../checkers/UserStatusVerifyChecker.java | 36 +-- .../java/rules/java/HostIdentityChecker.html | 16 ++ .../java/rules/java/HostIdentityChecker.json | 13 + .../java/PasswordInputTagJavaChecker.html | 16 ++ .../java/PasswordInputTagJavaChecker.json | 13 + .../src/test/files/FileCheck.java | 26 +- .../src/test/files/HashSaltPassWordRule.java | 20 +- .../src/test/files/HostIdentityChecker.java | 26 ++ .../src/test/files/InputSQLVerifyRule.java | 11 + .../src/test/files/Md5PassWordVerifyRule.java | 22 +- .../files/PasswordInputTagJavaChecker.java | 15 + .../src/test/files/RSAEncryptionRule.java | 4 +- .../src/test/files/RedirectUrlChecker.java | 7 + .../src/test/files/SecurityCookieRule.java | 17 +- .../test/files/SessionCacheParamsChecker.java | 1 + .../src/test/files/UploadFileVerifyRule.java | 13 + .../test/files/UserStatusVerifyChecker.java | 18 ++ .../files/options/OptionsVerifyOneRule.java | 21 +- .../files/options/OptionsVerifyTwoRule.java | 5 +- .../checkers/HostIdentityCheckerTest.java | 33 +++ .../checkers/OptionsVerifyCheckerTest.java | 11 +- .../PasswordInputTagJavaCheckerTest.java | 29 ++ 38 files changed, 1084 insertions(+), 133 deletions(-) create mode 100644 sonar-keyware-plugins-cxx/src/main/java/org/sonar/cxx/squidbridge/AstScanner.java create mode 100644 sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HostIdentityChecker.java create mode 100644 sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaChecker.java create mode 100644 sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.html create mode 100644 sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.json create mode 100644 sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.html create mode 100644 sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.json create mode 100644 sonar-keyware-plugins-java/src/test/files/HostIdentityChecker.java create mode 100644 sonar-keyware-plugins-java/src/test/files/PasswordInputTagJavaChecker.java create mode 100644 sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/HostIdentityCheckerTest.java create mode 100644 sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaCheckerTest.java diff --git a/sonar-keyware-plugins-ConfigurationDetection/src/main/java/com/keyware/sonar/Configuration/rules/checkers/SessionDateChecker.java b/sonar-keyware-plugins-ConfigurationDetection/src/main/java/com/keyware/sonar/Configuration/rules/checkers/SessionDateChecker.java index 6973b04..6b5047d 100644 --- a/sonar-keyware-plugins-ConfigurationDetection/src/main/java/com/keyware/sonar/Configuration/rules/checkers/SessionDateChecker.java +++ b/sonar-keyware-plugins-ConfigurationDetection/src/main/java/com/keyware/sonar/Configuration/rules/checkers/SessionDateChecker.java @@ -9,14 +9,12 @@ package com.keyware.sonar.Configuration.rules.checkers; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.rule.RuleKey; import org.sonar.check.Rule; import org.yaml.snakeyaml.Yaml; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; +import java.io.*; import java.util.Map; import java.util.Scanner; @@ -34,6 +32,31 @@ public class SessionDateChecker implements ConfigCheck { if(!boo){ //文件名称 String filename = inputFile.filename(); + if (filename.endsWith(".xml")) { + // 获取当前输入文件的绝对路径 + File file1 = inputFile.file(); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(file1), "UTF-8")); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("-1")) { + boo = true; + break; + } + } + } catch (Exception e) { + System.out.println("文件未找到: " + e.getMessage()); + return; // 文件未找到时立即返回 + } finally { + if (reader != null) { + try { reader.close(); } catch (IOException e) { e.printStackTrace(); } + } + } + } + + //校验文件后缀 if (filename.endsWith(".properties")) { try { diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java index c4a106c..b5f3b12 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java @@ -313,6 +313,7 @@ public class CxxSquidSensor implements ProjectSensor { var squidConfig = createConfiguration(); var scanner = CxxAstScanner.create(squidConfig, visitors.toArray(new SquidAstVisitor[visitors.size()])); + Iterable inputFiles = getInputFiles(context, squidConfig); scanner.scanInputFiles(inputFiles); diff --git a/sonar-keyware-plugins-cxx/src/main/java/org/sonar/cxx/squidbridge/AstScanner.java b/sonar-keyware-plugins-cxx/src/main/java/org/sonar/cxx/squidbridge/AstScanner.java new file mode 100644 index 0000000..4e840ec --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/main/java/org/sonar/cxx/squidbridge/AstScanner.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package org.sonar.cxx.squidbridge; // cxx: in use + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.sonar.cxx.sslr.api.AstNode; +import com.sonar.cxx.sslr.api.Grammar; +import com.sonar.cxx.sslr.api.RecognitionException; +import com.sonar.cxx.sslr.impl.Parser; +import com.sonar.cxx.sslr.impl.ast.AstWalker; +import java.io.File; +import java.io.InterruptedIOException; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.cxx.squidbridge.api.AnalysisException; +import org.sonar.cxx.squidbridge.api.SourceCodeSearchEngine; +import org.sonar.cxx.squidbridge.api.SourceCodeTreeDecorator; +import org.sonar.cxx.squidbridge.api.SourceProject; +import org.sonar.cxx.squidbridge.indexer.SquidIndex; +import org.sonar.cxx.squidbridge.measures.MetricDef; + +public class AstScanner { + + private static final Logger LOG = Loggers.get(AstScanner.class); + + private final List> visitors; + private final Parser parser; + private final SquidAstVisitorContextImpl context; + + private final SquidIndex indexer = new SquidIndex(); + private final MetricDef[] metrics; + private final MetricDef filesMetric; + + protected AstScanner(Builder builder) { + this.visitors = Lists.newArrayList(builder.visitors); + this.parser = builder.baseParser; + this.context = builder.context; + + this.context.setGrammar(parser.getGrammar()); + this.context.getProject().setSourceCodeIndexer(indexer); + this.context.setCommentAnalyser(builder.commentAnalyser); + this.metrics = builder.metrics; + this.filesMetric = builder.filesMetric; + indexer.index(context.getProject()); + } + + public SourceCodeSearchEngine getIndex() { + return indexer; + } + + public void scanFile(File file) { + scanFiles(java.util.List.of(file)); + } + + public void scanInputFile(InputFile inputFile) { + scanInputFiles(java.util.List.of(inputFile)); + } + + public void scanFiles(Collection files) { + initVisitors(); + + var astWalker = new AstWalker(visitors); + + for (var file : files) { + checkCancel(); + context.setFile(file, filesMetric); + + Exception parseException = null; + AstNode ast = null; + try { + try { + ast = parser.parse(file); + } catch (Exception e) { + parseException = handleParseException(file, e); + } + walkAndVisit(astWalker, ast, parseException); + } catch (Throwable e) { + throw new AnalysisException("Unable to parse file: " + file.getAbsolutePath(), e); + } + } + + destroyVisitors(); + decorateSquidTree(); + } + + public void scanInputFiles(Iterable inputFiles) { + initVisitors(); + + var astWalker = new AstWalker(visitors); + + for (var inputFile : inputFiles) { + var file = new File(inputFile.uri().getPath()); + checkCancel(); + context.setInputFile(inputFile, filesMetric); + + Exception parseException = null; + AstNode ast = null; + try { + try { + ast = parser.parse(inputFile.contents()); + } catch (Exception e) { + parseException = handleParseException(file, e); + } + walkAndVisit(astWalker, ast, parseException); + } catch (Throwable e) { + LOG.error("Unable to parse file: " + file.getAbsolutePath(), e); + } + } + + destroyVisitors(); + decorateSquidTree(); + } + + private static Exception handleParseException(File file, Exception e) { + checkInterrupted(e); + if (e instanceof RecognitionException) { + LOG.error("Unable to parse file: " + file.getAbsolutePath()); + LOG.error(e.getMessage()); + } else { + LOG.error("Unable to parse file: " + file.getAbsolutePath(), e); + } + return e; + } + + private void walkAndVisit(AstWalker astWalker, AstNode ast, @Nullable Exception parseException) throws Throwable { + if (parseException == null) { + astWalker.walkAndVisit(ast); + } else { + // process parse error + for (var visitor : visitors) { + visitor.visitFile(ast); + } + for (var visitor : visitors) { + if (visitor instanceof AstScannerExceptionHandler) { + if (parseException instanceof RecognitionException) { + ((AstScannerExceptionHandler) visitor) + .processRecognitionException((RecognitionException) parseException); + } else { + ((AstScannerExceptionHandler) visitor).processException(parseException); + } + } + } + for (var visitor : visitors) { + visitor.leaveFile(ast); + } + } + context.popTillSourceProject(); + } + + private void initVisitors() { + for (var visitor : visitors) { + visitor.init(); + } + } + + private void destroyVisitors() { + for (var visitor : visitors) { + visitor.destroy(); + } + } + + /** + * Checks if the root cause of the thread is related to an interrupt. + * Note that when such an exception is thrown, the interrupt flag is reset. + */ + private static void checkInterrupted(Exception e) { + Throwable cause = Throwables.getRootCause(e); + if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) { + throw new AnalysisException("Analysis cancelled", e); + } + } + + private static void checkCancel() { + if (Thread.interrupted()) { + throw new AnalysisException("Analysis cancelled"); + } + } + + protected void decorateSquidTree() { + if (metrics != null && metrics.length > 0) { + SourceProject project = context.getProject(); + var decorator = new SourceCodeTreeDecorator(project); + decorator.decorateWith(metrics); + } + } + + public static Builder builder(SquidAstVisitorContextImpl context) { + return new Builder<>(context); + } + + public static class Builder { + + private Parser baseParser; + private final List> visitors = Lists.newArrayList(); + private final SquidAstVisitorContextImpl context; + private CommentAnalyser commentAnalyser; + private MetricDef[] metrics; + private MetricDef filesMetric; + + public Builder(SquidAstVisitorContextImpl context) { + checkNotNull(context, "context cannot be null"); + this.context = context; + } + + public Builder setBaseParser(Parser baseParser) { + checkNotNull(baseParser, "baseParser cannot be null"); + this.baseParser = baseParser; + return this; + } + + public Builder setCommentAnalyser(CommentAnalyser commentAnalyser) { + checkNotNull(commentAnalyser, "commentAnalyser cannot be null"); + this.commentAnalyser = commentAnalyser; + return this; + } + + public Builder withSquidAstVisitor(SquidAstVisitor visitor) { + checkNotNull(visitor, "visitor cannot be null"); + visitor.setContext(context); + visitors.add(visitor); + return this; + } + + public Builder withMetrics(MetricDef... metrics) { + for (var metric : metrics) { + checkNotNull(metric, "metrics cannot be null"); + } + this.metrics = metrics.clone(); + return this; + } + + public Builder setFilesMetric(MetricDef filesMetric) { + checkNotNull(filesMetric, "filesMetric cannot be null"); + this.filesMetric = filesMetric; + return this; + } + + public AstScanner build() { + checkState(baseParser != null, "baseParser must be set"); + checkState(commentAnalyser != null, "commentAnalyser must be set"); + checkState(filesMetric != null, "filesMetric must be set"); + return new AstScanner<>(this); + } + } + +} diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/FileCheck.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/FileCheck.java index 76e2821..d9d0f71 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/FileCheck.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/FileCheck.java @@ -23,7 +23,7 @@ import java.util.List; */ @Rule(key = "FileCheck") public class FileCheck extends IssuableSubscriptionVisitor { - private static final List FILE_NAME_VARS = Arrays.asList("fileName", "fileExt", "fileSuffix"); + private static final List FILE_NAME_VARS = Arrays.asList("fileName", "fileExt", "fileSuffix", "fileEnd"); @Override public List nodesToVisit() { @@ -37,20 +37,22 @@ public class FileCheck extends IssuableSubscriptionVisitor { ExpressionTree condition = ifStatement.condition(); // 在条件中检查文件名、文件扩展名或文件后缀的使用 - checkVariableUsage(condition); + IdenVisitor idenVisitor = new IdenVisitor(); + condition.accept(idenVisitor); + if(idenVisitor.boo){ + reportIssue(tree, "在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为"); + } } } - private void checkVariableUsage(ExpressionTree condition) { - condition.accept(new BaseTreeVisitor() { - @Override - public void visitIdentifier(IdentifierTree tree) { - if (FILE_NAME_VARS.contains(tree.name())) { - // 如果在条件中发现了文件名、文件扩展名或文件后缀的使用,报告问题 - System.out.println("依赖文件的名称或者扩展后缀:"+tree.name()); - reportIssue(tree, "在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为"); - } + class IdenVisitor extends BaseTreeVisitor { + boolean boo = false; + @Override + public void visitIdentifier(IdentifierTree tree) { + if (FILE_NAME_VARS.contains(tree.name())) { + // 如果在条件中发现了文件名、文件扩展名或文件后缀的使用 + boo = true; } - }); + } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HashSaltPassWordChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HashSaltPassWordChecker.java index 86212e3..aa2ef6e 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HashSaltPassWordChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HashSaltPassWordChecker.java @@ -14,6 +14,7 @@ import org.sonar.java.model.expression.MethodInvocationTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.*; +import javax.annotation.Nonnull; import java.util.Collections; import java.util.List; @@ -37,10 +38,13 @@ public class HashSaltPassWordChecker extends IssuableSubscriptionVisitor { } @Override - public void visitNode(Tree tree) { + public void visitNode(@Nonnull Tree tree) { MethodTree node = (MethodTree) tree; var bodyVisitor = new MethodeBodyVisitor(this); - node.block().accept(bodyVisitor); + BlockTree block = node.block(); + if(block != null){ + block.accept(bodyVisitor); + } } @@ -70,7 +74,6 @@ public class HashSaltPassWordChecker extends IssuableSubscriptionVisitor { checker.context.reportIssue(checker, identifierTree, "应使用盐值计算口令"); } } else { - System.out.println(expressionTree1); checker.context.reportIssue(checker, expressionTree1, "应使用盐值计算口令"); } } @@ -88,5 +91,17 @@ public class HashSaltPassWordChecker extends IssuableSubscriptionVisitor { } } } + + @Override + public void visitNewClass(NewClassTree classTree) { + // 判断是否为RedirectView,如果是,则判断是否有参数,如果有参数,则判断参数的类型是否由方法传递进来的 + String name = classTree.identifier().toString(); + if ("PBEKeySpec".equals(name)) { + if (classTree.arguments().size() < 2) { + // 获取第一个参数语法树节点 + checker.context.reportIssue(checker, classTree, "应使用盐值计算口令"); + } + } + } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HostIdentityChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HostIdentityChecker.java new file mode 100644 index 0000000..fa9ba55 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HostIdentityChecker.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import org.sonar.check.Rule; +import org.sonar.java.model.expression.IdentifierTreeImpl; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.*; + +import java.util.Collections; +import java.util.List; + +/** + * 通过用户名口令、数据证书等其他手段对主机身份进行鉴别 + * + * @author RenFengJiang + * @date 2024/7/3 + */ +@Rule(key = "HostIdentityChecker") +public class HostIdentityChecker extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return Collections.singletonList(Tree.Kind.METHOD); + } + @Override + public void visitNode(Tree tree) { + MethodTree methodTree = (MethodTree) tree; + List parameters = methodTree.parameters(); +// 盘带是否是文件上传类 + boolean boo = parameters.stream().anyMatch(type -> "HttpServletRequest".equals(type.type().toString())); + if(boo){ + NodeIf nodeIf = new NodeIf(); + methodTree.block().accept(nodeIf); + if(nodeIf.getNameBoolean() || nodeIf.getPasswordBoolean()){ + context.reportIssue(this,methodTree.simpleName(),"通过用户名口令、数据证书等其他手段对主机身份进行鉴别"); + } + } + + } + + public class NodeIf extends BaseTreeVisitor { + + public boolean nameBoolean = true; + private boolean passwordBoolean = true; + boolean getNameBoolean(){ + return nameBoolean; + } + boolean getPasswordBoolean(){ + return passwordBoolean; + } + @Override + public void visitIfStatement(IfStatementTree tree) { + //获取到if表达式 + ExpressionTree condition = tree.condition(); + if (condition instanceof BinaryExpressionTree) { + BinaryExpressionTree binaryExpressionTree = (BinaryExpressionTree) condition; + //判断是否进行if判断 + if ("username".equals(binaryExpressionTree.leftOperand().toString())) { + nameBoolean = false; + } else if ("password".equals(binaryExpressionTree.rightOperand().toString())) { + passwordBoolean = false; + } + } + if (condition instanceof IdentifierTreeImpl) { + IdentifierTreeImpl identifierTreeImpl = (IdentifierTreeImpl) condition; + //判断是否进行if判断 + if ("username".equals(identifierTreeImpl.name())) { + nameBoolean = false; + } else if ("password".equals(identifierTreeImpl.name())) { + passwordBoolean = false; + } + } + } + } +} diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/InputSQLVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/InputSQLVerifyChecker.java index 0861c89..1bd95d9 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/InputSQLVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/InputSQLVerifyChecker.java @@ -63,7 +63,7 @@ public class InputSQLVerifyChecker extends IssuableSubscriptionVisitor { ExpressionTree expressionTree = tree.methodSelect(); if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; - if ("prepareStatement".equals(memberSelectExpressionTree.identifier().name())) { + if ("prepareStatement".equals(memberSelectExpressionTree.identifier().name()) || "executeQuery".equals(memberSelectExpressionTree.identifier().name())) { Arguments arguments = tree.arguments(); for (ExpressionTree argument : arguments) { if (argument.is(Tree.Kind.IDENTIFIER)) { diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/Md5PassWordVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/Md5PassWordVerifyChecker.java index fd67e8c..b26de3d 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/Md5PassWordVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/Md5PassWordVerifyChecker.java @@ -36,7 +36,10 @@ public class Md5PassWordVerifyChecker extends IssuableSubscriptionVisitor { public void visitNode(Tree tree) { MethodTree node = (MethodTree) tree; var bodyVisitor = new MethodeBodyVisitor(this); - node.block().accept(bodyVisitor); + BlockTree block = node.block(); + if(block != null){ + block.accept(bodyVisitor); + } } @@ -63,11 +66,9 @@ public class Md5PassWordVerifyChecker extends IssuableSubscriptionVisitor { IdentifierTree identifierTree = (IdentifierTree) argument; String name = identifierTree.name(); if (!name.equals(strPassWord)) { - System.out.println(identifierTree); checker.context.reportIssue(checker, identifierTree, "应使用单向不可逆的加密算法"); } } else { - System.out.println(argument); checker.context.reportIssue(checker, argument, "应使用单向不可逆的加密算法"); } } @@ -84,6 +85,19 @@ public class Md5PassWordVerifyChecker extends IssuableSubscriptionVisitor { strPassWord = variableTree.simpleName().name(); } } + }else if ("Cipher".equals(memberSelectExpressionTree.expression().toString()) && "getInstance".equals(memberSelectExpressionTree.identifier().name())) { + // 获取参数列表 + List arguments = tree.arguments(); + for (ExpressionTree argument : arguments) { + if (argument.is(Tree.Kind.STRING_LITERAL)) { + LiteralTree literalTree = (LiteralTree) argument; + String c = ((LiteralTree) argument).token().text(); + // 对参数进行判断判断是否符合要求 + if (literalTree.token().text().startsWith("\"AES")) { + checker.context.reportIssue(checker, argument, "应使用单向不可逆的加密算法"); + } + } + } } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyChecker.java index 24203f7..c986d5b 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyChecker.java @@ -8,6 +8,7 @@ package com.keyware.sonar.java.rules.checkers; import org.sonar.check.Rule; +import org.sonar.java.model.expression.IdentifierTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.ModuleScannerContext; import org.sonar.plugins.java.api.internal.EndOfAnalysis; @@ -25,39 +26,59 @@ import java.util.List; * @date 2024/1/22 */ @Rule(key = "OptionsVerifyChecker") -public class OptionsVerifyChecker extends IssuableSubscriptionVisitor implements EndOfAnalysis { +public class OptionsVerifyChecker extends IssuableSubscriptionVisitor { - private boolean boo = false; @Override public List nodesToVisit() { - return Collections.singletonList(Tree.Kind.METHOD_INVOCATION); + return Collections.singletonList(Tree.Kind.METHOD); } @Override public void visitNode(Tree tree) { - if(!boo) { - MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; - ExpressionTree expressionTree = methodInvocationTree.methodSelect(); - if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { - MemberSelectExpressionTree selectExpressionTree = (MemberSelectExpressionTree) expressionTree; - if("addHeader".equals(selectExpressionTree.identifier().name()) || "setHeader".equals(selectExpressionTree.identifier().name())) { - Arguments arguments = methodInvocationTree.arguments(); - boolean one = false; - boolean two = true; - for (ExpressionTree argument : arguments) { - if(argument.is(Tree.Kind.STRING_LITERAL)){ - String literalValue = ((LiteralTree) argument).value(); - if ("\"X-Frame-Options\"".equals(literalValue)) { - one = true; - } else if("\"DENY\"".equals(literalValue)){ - two = false; + MethodTree node = (MethodTree) tree; + List parameters = node.parameters(); + //盘带是否是文件上传类 + boolean booleanRestorn = parameters.stream().anyMatch(type -> "HttpServletResponse".equals(type.type().toString())); + if (booleanRestorn) { + MethodCall methodCall = new MethodCall(); + node.block().accept(methodCall); + if (methodCall.getBooleanInner()) { + context.reportIssue(this, node.simpleName(), "应设置X-Frame-Options的值为deny"); + } + } + DeclarationVerdict declarationVerdict = new DeclarationVerdict(node,this); + node.block().accept(declarationVerdict); + } + + private class MethodCall extends BaseTreeVisitor { + private boolean booleanInner = true; + + public boolean getBooleanInner() { + return booleanInner; + } + + public void visitMethodInvocation(MethodInvocationTree tree) { + if (booleanInner) { + ExpressionTree expressionTree = tree.methodSelect(); + if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { + MemberSelectExpressionTree selectExpressionTree = (MemberSelectExpressionTree) expressionTree; + if ("addHeader".equals(selectExpressionTree.identifier().name()) || "setHeader".equals(selectExpressionTree.identifier().name())) { + Arguments arguments = tree.arguments(); + boolean one = false; + boolean two = false; + for (ExpressionTree argument : arguments) { + if (argument.is(Tree.Kind.STRING_LITERAL)) { + String literalValue = ((LiteralTree) argument).value(); + if ("\"X-Frame-Options\"".equals(literalValue)) { + one = true; + } else if ("\"DENY\"".equals(literalValue) ||"\"deny\"".equals(literalValue)) { + two = true; + } } } - } - if(one){ - if(two){ - boo = true; + if (one && two) { + booleanInner = false; } } } @@ -65,10 +86,30 @@ public class OptionsVerifyChecker extends IssuableSubscriptionVisitor implements } } - @Override - public void endOfAnalysis(ModuleScannerContext context) { - if(boo){ - context.addIssueOnProject(this, "应设置X-Frame-Options的值为deny"); + public class DeclarationVerdict extends BaseTreeVisitor { + + private MethodTree node; + private OptionsVerifyChecker checker; + public DeclarationVerdict( MethodTree node,OptionsVerifyChecker checker){ + this.node = node; + this.checker = checker; + } + + + @Override + public void visitVariable(VariableTree tree) { + IdentifierTree identifierTree = tree.simpleName(); + TypeTree type = tree.type(); + if(type instanceof IdentifierTreeImpl){ + IdentifierTreeImpl fierTree = (IdentifierTreeImpl) type; + if("HttpServletResponse".equals(fierTree.name())){ + MethodCall methodCall = new MethodCall(); + node.block().accept(methodCall); + if (methodCall.getBooleanInner()) { + context.reportIssue(checker, identifierTree, "应设置X-Frame-Options的值为deny"); + } + } + } } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaChecker.java new file mode 100644 index 0000000..ced94c3 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaChecker.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import org.sonar.check.Rule; +import org.sonar.java.model.expression.NewClassTreeImpl; +import org.sonar.java.model.statement.BlockTreeImpl; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.tree.*; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * 用户输入口令时应对口令域进行掩饰。用户输入的每一个字符都应该以星号形式回显。 + * + * @author RenFengJiang + * @date 2024/7/3 + */ +@Rule(key = "PasswordInputTagJavaChecker") +public class PasswordInputTagJavaChecker extends IssuableSubscriptionVisitor { + @Override + public List nodesToVisit() { + /** + * Tree.Kind.METHOD:方法节点 + * Tree.Kind.BLOCK:方法的代码块节点 + * Tree.Kind.METHOD_INVOCATION: 方法的调用节点 + */ + return Collections.singletonList(Tree.Kind.BLOCK); + } + + @Override + public void visitNode(Tree tree) { + BlockTreeImpl node = (BlockTreeImpl) tree; + MethodeBodyVisitor methodeBodyVisitor = new MethodeBodyVisitor(this, node); + node.accept(methodeBodyVisitor); + } + + static class MethodeBodyVisitor extends BaseTreeVisitor { + private BlockTreeImpl blockTree; + private PasswordInputTagJavaChecker checker; + public MethodeBodyVisitor(PasswordInputTagJavaChecker checker, BlockTreeImpl blockTree){ + this.checker = checker; + this.blockTree = blockTree; + } + + @Override + public void visitNewClass(NewClassTree tree) { + String name = tree.identifier().toString(); + if("JTextField".equals(name)){ + MethodeCall methodeCall = new MethodeCall(); + blockTree.accept(methodeCall); + if(methodeCall.getEchoBoolean()){ + checker.context.reportIssue(checker, tree, "Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显"); + } + } + + } + } + + static class MethodeCall extends BaseTreeVisitor { + //是否存在JTextField输入 + private boolean echoBoolean = true; + + public boolean getEchoBoolean() { + return echoBoolean; + } + + @Override + public void visitMethodInvocation(MethodInvocationTree tree) { + //获取到方法调用 + ExpressionTree expressionTree = tree.methodSelect(); + if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { + MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; + if ("setEchoChar".equals(memberSelectExpressionTree.identifier().name())) { + echoBoolean = false; + } + } + } + } + +} diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RSAEncryptionChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RSAEncryptionChecker.java index e0c6bda..4aa8039 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RSAEncryptionChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RSAEncryptionChecker.java @@ -7,9 +7,13 @@ package com.keyware.sonar.java.rules.checkers; import org.sonar.check.Rule; +import org.sonar.java.model.expression.IdentifierTreeImpl; +import org.sonar.java.model.expression.LiteralTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.*; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -22,6 +26,7 @@ import java.util.List; @Rule(key = "RSAEncryptionChecker") public class RSAEncryptionChecker extends IssuableSubscriptionVisitor { + private List nameLists = new ArrayList(); @Override public List nodesToVisit() { /** @@ -29,30 +34,68 @@ public class RSAEncryptionChecker extends IssuableSubscriptionVisitor { * Tree.Kind.BLOCK:方法的代码块节点 * Tree.Kind.METHOD_INVOCATION: 方法的调用节点 */ - return Collections.singletonList(Tree.Kind.METHOD_INVOCATION); + return Arrays.asList(Tree.Kind.METHOD_INVOCATION, Tree.Kind.CLASS); } @Override public void visitNode(Tree tree) { - MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; - ExpressionTree expressionTree = methodInvocationTree.methodSelect(); - // 获取到方法调用 - if (expressionTree instanceof MemberSelectExpressionTree) { - MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; - // 判断是否符合标准 - if ("Cipher".equals(memberSelectExpressionTree.expression().toString()) && "getInstance".equals(memberSelectExpressionTree.identifier().name())) { - // 获取参数列表 - List arguments = methodInvocationTree.arguments(); - for (ExpressionTree argument : arguments) { - if (argument.is(Tree.Kind.STRING_LITERAL)) { - LiteralTree literalTree = (LiteralTree) argument; - // 对参数进行判断判断是否符合要求 - if (!(literalTree.value().startsWith("\"RSA") && literalTree.value().contains("OAEPWithSHA-256AndMGF1Padding"))) { - context.reportIssue(this, tree, "使用RSA最优加密填充"); + + if (tree.is(Tree.Kind.CLASS)) { + MethodBOdyVisitor methodBOdyVisitor = new MethodBOdyVisitor(); + tree.accept(methodBOdyVisitor); + nameLists = methodBOdyVisitor.getNameLists(); + }else if(tree.is(Tree.Kind.METHOD_INVOCATION)){ + + MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; + ExpressionTree expressionTree = methodInvocationTree.methodSelect(); + // 获取到方法调用 + if (expressionTree instanceof MemberSelectExpressionTree) { + MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; + // 判断是否符合标准 + String a = memberSelectExpressionTree.expression().toString(); + String b = memberSelectExpressionTree.identifier().name(); + if ("Cipher".equals(memberSelectExpressionTree.expression().toString()) && "getInstance".equals(memberSelectExpressionTree.identifier().name())) { + // 获取参数列表 + List arguments = methodInvocationTree.arguments(); + for (ExpressionTree argument : arguments) { + if (argument.is(Tree.Kind.STRING_LITERAL)) { + LiteralTree literalTree = (LiteralTree) argument; + String c = ((LiteralTree) argument).token().text(); + // 对参数进行判断判断是否符合要求 + if (!literalTree.token().text().startsWith("\"RSA")) { + context.reportIssue(this, argument, "使用RSA最优加密填充"); + } + }else if( !nameLists.equals(argument.toString())){ + context.reportIssue(this, argument, "使用RSA最优加密填充"); } } } } } + + } + + static class MethodBOdyVisitor extends BaseTreeVisitor { + + private List nameLists = new ArrayList(); + public MethodBOdyVisitor() { + + } + + public List getNameLists() { + return nameLists; + } + + @Override + public void visitVariable(VariableTree tree) { + IdentifierTree identifierTree = tree.simpleName(); + ExpressionTree initializer = tree.initializer(); + if(initializer instanceof LiteralTreeImpl){ + LiteralTreeImpl literalTree = (LiteralTreeImpl) initializer; + if(literalTree.value().startsWith("\"RSA") ){ + nameLists.add(identifierTree.name()); + } + } + } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RedirectUrlChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RedirectUrlChecker.java index fbd5f5c..3a97cb3 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RedirectUrlChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RedirectUrlChecker.java @@ -11,6 +11,7 @@ import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.*; import javax.annotation.Nonnull; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -43,6 +44,15 @@ public class RedirectUrlChecker extends IssuableSubscriptionVisitor { checkByStringType(block, parameters); } } + if(!parameters.isEmpty()){ + for (VariableTree parameter : parameters) { + if("HttpServletResponse".equals(parameter.type().toString())){ + String name = parameter.simpleName().name(); + MethodeBodyVisitor methodeBodyVisitor = new MethodeBodyVisitor(this, name); + methodTree.block().accept(methodeBodyVisitor); + } + } + } } private void checkByStringType(BlockTree block, List methodParameters) { @@ -75,6 +85,49 @@ public class RedirectUrlChecker extends IssuableSubscriptionVisitor { } + //判断HttpServletResponse类型重定向 + static class MethodeBodyVisitor extends BaseTreeVisitor { + private final RedirectUrlChecker checker; + private String name ; + private List lists = new ArrayList(); + MethodeBodyVisitor(RedirectUrlChecker checker,String name){ + this.checker = checker; + this.name = name; + } + + @Override + public void visitIfStatement(IfStatementTree tree) { + ExpressionTree condition = tree.condition(); + condition.accept(new BaseTreeVisitor(){ + @Override + public void visitIdentifier(IdentifierTree tree) { + lists.add(tree.name()); + } + }); + } + + @Override + public void visitMethodInvocation(MethodInvocationTree tree) { + ExpressionTree expressionTree = tree.methodSelect(); + if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { + MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; + if ("sendRedirect".equals(memberSelectExpressionTree.identifier().name()) && name.equals(memberSelectExpressionTree.expression().toString())) { + Arguments arguments = tree.arguments(); + for (ExpressionTree argument : arguments) { + if (argument.is(Tree.Kind.IDENTIFIER)) { + IdentifierTree identifierTree = (IdentifierTree) argument; + String paramName = identifierTree.name(); + // 判断使用的sql是否存在集合中 + if (!lists.contains(paramName)) { + checker.context.reportIssue(checker, argument, "在重定向前对输入数据进行验证"); + } + } + } + } + } + } + } + /** * 判断方法是否为public方法 * diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SecurityCookieChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SecurityCookieChecker.java index 7896e7c..da2693d 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SecurityCookieChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SecurityCookieChecker.java @@ -42,27 +42,29 @@ public class SecurityCookieChecker extends IssuableSubscriptionVisitor { // 盘带是否是文件上传类 boolean boo = parameters.stream().anyMatch(type -> "HttpServletResponse".equals(type.type().toString())); if(boo){ - var bodyVisitor = new MethodBOdyVisitor(this); + var bodyVisitor = new MethodBOdyVisitor(); node.block().accept(bodyVisitor); - if(bodyVisitor.booHttp){ - if(!(bodyVisitor.booAge && bodyVisitor.booCure)){ - context.reportIssue(this,node.simpleName(),"设置HTTPS会话中cookie的安全属性"); - } + if(bodyVisitor.booCure){ + context.reportIssue(this,node.simpleName(),"设置HTTPS会话中cookie的安全属性"); } +// if(bodyVisitor.booHttp){ +// if(!(bodyVisitor.booAge && bodyVisitor.booCure)){ +// context.reportIssue(this,node.simpleName(),"设置HTTPS会话中cookie的安全属性"); +// } +// } } } static class MethodBOdyVisitor extends BaseTreeVisitor{ - private final SecurityCookieChecker checker; // 判断是否是https请求 - private boolean booCure = false; + private boolean booCure = true; // 设置cooker时长 - private boolean booAge = false; +// private boolean booAge = false; // 设置访问 - private boolean booHttp = false; - public MethodBOdyVisitor(SecurityCookieChecker checker ){ - this.checker = checker; +// private boolean booHttp = false; + public MethodBOdyVisitor( ){ + } @Override @@ -71,15 +73,15 @@ public class SecurityCookieChecker extends IssuableSubscriptionVisitor { if(expressionTree instanceof MemberSelectExpressionTree){ MemberSelectExpressionTree memberSelectExpressionTree = (MemberSelectExpressionTree) expressionTree; switch (memberSelectExpressionTree.identifier().name()){ - case "setHttpOnly": - booHttp = true; - break; +// case "setHttpOnly": +// booHttp = true; +// break; case "setSecure": - booCure = true; - break; - case "setMaxAge": - booAge = true; + booCure = false; break; +// case "setMaxAge": +// booAge = true; +// break; } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java index e3b508b..ba6dd46 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java @@ -29,7 +29,8 @@ public class SessionCacheParamsChecker extends IssuableSubscriptionVisitor { "userid", "token", "url", - "userpassword" + "userpassword", + "price" ); private static final String requestType = "HttpServletRequest"; diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UploadFileVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UploadFileVerifyChecker.java index 4bdb220..e6b1e1b 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UploadFileVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UploadFileVerifyChecker.java @@ -7,11 +7,11 @@ package com.keyware.sonar.java.rules.checkers; import org.sonar.check.Rule; +import org.sonar.java.model.expression.IdentifierTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.tree.*; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * 上传文件检查规则 @@ -37,6 +37,9 @@ public class UploadFileVerifyChecker extends IssuableSubscriptionVisitor { @Override public void visitNode(Tree tree) { MethodTree node = (MethodTree) tree; + DeclarationVerdict declarationVerdict = new DeclarationVerdict(node,this); + ((MethodTree) tree).block().accept(declarationVerdict); + List parameters = node.parameters(); //盘带是否是文件上传类 boolean boo = parameters.stream().anyMatch(type -> "MultipartFile".equals(type.type().toString())); @@ -139,10 +142,10 @@ public class UploadFileVerifyChecker extends IssuableSubscriptionVisitor { public class NodeIf extends BaseTreeVisitor { - private String name; + private Object name; public boolean boo = true; - public NodeIf(String name) { + public NodeIf(Object name) { this.name = name; } @@ -159,6 +162,42 @@ public class UploadFileVerifyChecker extends IssuableSubscriptionVisitor { boo = false; } } + if (condition instanceof IdentifierTreeImpl) { + IdentifierTreeImpl identifierTreeImpl = (IdentifierTreeImpl) condition; + //判断是否进行if判断 + if (name.equals(identifierTreeImpl.name())) { + boo = false; + } else if (name.equals(identifierTreeImpl.name())) { + boo = false; + } + } + } + } + + public class DeclarationVerdict extends BaseTreeVisitor { + + private MethodTree node; + private UploadFileVerifyChecker checker; + public DeclarationVerdict( MethodTree node,UploadFileVerifyChecker checker){ + this.node = node; + this.checker = checker; + } + + + @Override + public void visitVariable(VariableTree tree) { + IdentifierTree identifierTree = tree.simpleName(); + TypeTree type = tree.type(); + if(type instanceof IdentifierTreeImpl){ + IdentifierTreeImpl fierTree = (IdentifierTreeImpl) type; + if("Fileltem".equals(fierTree.name())){ + NodeIf nodeIf = new NodeIf(identifierTree.name()); + node.block().accept(nodeIf); + if (nodeIf.boo) { + context.reportIssue(checker, identifierTree, value); + } + } + } } } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UpperCycleLimitRuleChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UpperCycleLimitRuleChecker.java index 4c57bf1..391dbc2 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UpperCycleLimitRuleChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UpperCycleLimitRuleChecker.java @@ -32,7 +32,9 @@ public class UpperCycleLimitRuleChecker extends IssuableSubscriptionVisitor { MethodTree methodTree = (MethodTree) tree; List args = methodTree.parameters(); BlockTree blockTree = methodTree.block(); - blockTree.accept(new MethodBlockVisitor(this, args)); + if(blockTree != null){ + blockTree.accept(new MethodBlockVisitor(this, args)); + } } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java index 4d2a456..8f19681 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java @@ -32,15 +32,15 @@ public class UserStatusVerifyChecker extends IssuableSubscriptionVisitor { MethodTree methodTree = (MethodTree) tree; //判断是否是doFilter或doFilter方法 boolean boo = verifyMethod(methodTree); - if(boo){ + if (boo) { //内部实现类 VisitMethod visitMethod = new VisitMethod(this); //调用内部实现类 methodTree.block().accept(visitMethod); //判断是否满足4个条件 - if(visitMethod.booOne && visitMethod.booTwo && visitMethod.booThree && visitMethod.booFour){ + if (visitMethod.booOne && visitMethod.booTwo && visitMethod.booThree && visitMethod.booFour) { - }else{ + } else { System.out.println(methodTree.simpleName()); context.reportIssue(this, methodTree.simpleName(), "对用户进行身份鉴别并建立一个新的会话时让原来的会话失效"); } @@ -50,15 +50,15 @@ public class UserStatusVerifyChecker extends IssuableSubscriptionVisitor { public static boolean verifyMethod(MethodTree methodTree) { //preHandle - if ("doFilter".equals(methodTree.simpleName().name()) || "preHandle".equals(methodTree.simpleName().name())) { - //获取参数 - List parameters = methodTree.parameters(); - for (VariableTree variable : parameters) { - if (variable.type().symbolType().name().endsWith("ServletRequest")) { - return true; - } + // if ("doFilter".equals(methodTree.simpleName().name()) || "preHandle".equals(methodTree.simpleName().name())) { + //获取参数 + List parameters = methodTree.parameters(); + for (VariableTree variable : parameters) { + if (variable.type().symbolType().name().endsWith("ServletRequest")) { + return true; } } + //} return false; } @@ -79,30 +79,30 @@ public class UserStatusVerifyChecker extends IssuableSubscriptionVisitor { @Override public void visitMethodInvocation(MethodInvocationTree tree) { ExpressionTree expressionTree = tree.methodSelect(); - if(expressionTree.is(Tree.Kind.MEMBER_SELECT)){ + if (expressionTree.is(Tree.Kind.MEMBER_SELECT)) { //判断是否调用指定的方法 MemberSelectExpressionTree selectExpressionTree = (MemberSelectExpressionTree) expressionTree; - if("getParameter".equals(selectExpressionTree.identifier().name()) || "getSession".equals(selectExpressionTree.identifier().name())){ + if ("getParameter".equals(selectExpressionTree.identifier().name()) || "getSession".equals(selectExpressionTree.identifier().name())) { //获取到调用方法的参数 Arguments arguments = tree.arguments(); //调用判断参数的方法 verifyParam(arguments); - }else if ("invalidate".equals(selectExpressionTree.identifier().name())){ + } else if ("invalidate".equals(selectExpressionTree.identifier().name())) { booFour = true; } } } - public void verifyParam(Arguments arguments){ + public void verifyParam(Arguments arguments) { for (ExpressionTree argument : arguments) { - if(argument.is(Tree.Kind.STRING_LITERAL)){ + if (argument.is(Tree.Kind.STRING_LITERAL)) { LiteralTree literalTree = (LiteralTree) argument; String strName = literalTree.value().replace("\"", "").toLowerCase(); - if("username".equals(strName)){ + if ("username".equals(strName)) { booOne = true; - }else if("password".equals(strName)){ + } else if ("password".equals(strName)) { booTwo = true; - }else if("false".equals(strName)){ + } else if ("false".equals(strName)) { booThree = true; } } diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.html b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.html new file mode 100644 index 0000000..2427f4b --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.html @@ -0,0 +1,16 @@ + + +

通过用户名口令、数据证书等其他手段对主机身份进行鉴别

+

通过用户名口令、数据证书等其他手段对主机身份进行鉴别

+
+
+
+

合规解决方案

+
+
+
diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.json b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.json new file mode 100644 index 0000000..d1e405d --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.json @@ -0,0 +1,13 @@ +{ + "title": "通过用户名口令、数据证书等其他手段对主机身份进行鉴别", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "28suo" + ], + "defaultSeverity": "Minor" +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.html b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.html new file mode 100644 index 0000000..f6ec286 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.html @@ -0,0 +1,16 @@ + + +

Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显

+

Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显

+
+
+
+

合规解决方案

+
+
+
diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.json b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.json new file mode 100644 index 0000000..0b27eb4 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.json @@ -0,0 +1,13 @@ +{ + "title": "Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "15min" + }, + "tags": [ + "28suo" + ], + "defaultSeverity": "Major" +} diff --git a/sonar-keyware-plugins-java/src/test/files/FileCheck.java b/sonar-keyware-plugins-java/src/test/files/FileCheck.java index 47d46e5..888aa99 100644 --- a/sonar-keyware-plugins-java/src/test/files/FileCheck.java +++ b/sonar-keyware-plugins-java/src/test/files/FileCheck.java @@ -1,22 +1,38 @@ -public class FileCheck{ +public class FileCheck { - public String FileName(){ + public String FileName() { String fileName = ""; String fileExt = ""; String fileSuffix = ""; - if(fileName.endsWith("png") ){// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} + if (fileName.endsWith("png")) {// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} } - if(fileExt.equals("jpg") ){// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} + if (fileExt.equals("jpg")) {// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} } - if(fileSuffix.equals("jpg")){// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} + if (fileSuffix.equals("jpg")) {// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} } return null; } + public void imageBeauty(HttpServletRequest request) { //处理图像文件 + DiskFileltemFactory factory = new DiskFileltemFactory(); + ServletFileUpload upload = new ServletFileUpload(factory); + List items = upload.parseRequest(request); + Iterator iter = items.iterator(); + while (iter.hasNext()) { + FileItem item = iter.next(); + String fileName = item.getName(); + String fileEnd = fileName.substring(fileName.lastlndexOf(".") + 1).toLowerCase(); + //依赖文件扩展名进行验证 + if (fileEnd != null && fileEnd.matches(regex)) {// Noncompliant {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}} + //对文件的相关操作 + } + } + } + } diff --git a/sonar-keyware-plugins-java/src/test/files/HashSaltPassWordRule.java b/sonar-keyware-plugins-java/src/test/files/HashSaltPassWordRule.java index 5068c1f..145f000 100644 --- a/sonar-keyware-plugins-java/src/test/files/HashSaltPassWordRule.java +++ b/sonar-keyware-plugins-java/src/test/files/HashSaltPassWordRule.java @@ -1,7 +1,7 @@ public class HashSaltPassWordRule { - public static void cs(Student student){ + public static void cs(Student student) { // 结合盐值和口令进行散列计算 // String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt()); @@ -29,4 +29,22 @@ public class HashSaltPassWordRule { } } + public class Example { + private String encrypt(String password, KeySpec key) { //加密函数,返回加密后的字符串 + MessageDigest md = MessageDigest.getInstance("SHA-256"); + } + + public void storePassword(String password) { + byte[] salt = new byte[length]; + Random random = new SecureRandom(); + random.nextBytes(salt);//随机生成盐值 + // 使用盐值生成密钥KeySpec key = new PBEKeySpec(password.toCharArray(),salt,iterationCount); +// KeySpec keyspec = new PBEKeySpec(ssword.toCharArray(),salt,iterationCount);//生成密钥 + KeySpec keyspec = new PBEKeySpec(ssword.toCharArray());// Noncompliant {{应使用盐值计算口令}} + + //仅使用单向加密,还是容易被攻击者用彩虹表等方式破解口令 + String encryptedPassword = encrypt(password, key); + //将 encryptedPassword 存放到数据库 + } + } } diff --git a/sonar-keyware-plugins-java/src/test/files/HostIdentityChecker.java b/sonar-keyware-plugins-java/src/test/files/HostIdentityChecker.java new file mode 100644 index 0000000..8ff0234 --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/files/HostIdentityChecker.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +import java.net.*; + +public class Example { + private boolean trusted; + + public void getTrust(HttpServletRequest request) {// Noncompliant {{通过用户名口令、数据证书等其他手段对主机身份进行鉴别}} + String ip = request.getRemoteAddr(); + InetAddress address = InetAddress.getByName(ip); + //攻击者可通过DNS欺骗绕过依赖域名的主机身份鉴别 + if (address.getCanonicalHostName().endsWith("trustme.com")) { + trusted = true; + } + +// String username = request.getParameter("username"); +// String password = request.getParameter("password"); +// if (username != null &.&.password != null){ +// } + } +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/InputSQLVerifyRule.java b/sonar-keyware-plugins-java/src/test/files/InputSQLVerifyRule.java index f073695..fa2b13d 100644 --- a/sonar-keyware-plugins-java/src/test/files/InputSQLVerifyRule.java +++ b/sonar-keyware-plugins-java/src/test/files/InputSQLVerifyRule.java @@ -37,4 +37,15 @@ public class InputSQLVerifyRile { e.printStackTrace(); } } + + public class Example{ + public ResultSet getUserData(ServletRequest req,Connection con) throws SQLException{ + String owner = req.getParameter("owner"); + //采用拼接字符串的方式形成SQL语句,没有对用户输入数据owner进行验证 + String query ="SELECT * FROM user_data WHERE userid ="+ owner +""; + Statement statement = con.createStatement(); + ResultSet results = statement.executeQuery(query);// Noncompliant {{使用sql语句前应对其进行验证}} + return results; + } + } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/Md5PassWordVerifyRule.java b/sonar-keyware-plugins-java/src/test/files/Md5PassWordVerifyRule.java index 5315022..8c2c7e5 100644 --- a/sonar-keyware-plugins-java/src/test/files/Md5PassWordVerifyRule.java +++ b/sonar-keyware-plugins-java/src/test/files/Md5PassWordVerifyRule.java @@ -1,5 +1,5 @@ -public class Md5PassWordVerifyRule{ - public static void cs(Student student){ +public class Md5PassWordVerifyRule { + public static void cs(Student student) { // 结合盐值和口令进行散列计算 // String password = DigestUtils.md5Hex(str); @@ -26,4 +26,20 @@ public class Md5PassWordVerifyRule{ } } -} \ No newline at end of file +} + + +public class Example { + + public String encrypt(String str) { + Cipher cipher = Cipher.getInstance("AES");// Noncompliant {{应使用单向不可逆的加密算法}} + //使用双向可逆的 AES算法 + } + public void storePassword(Connection con, String id, String password) { + PreparedStatement ps = con.prepareStatement("UPDATE user SET password =? WHERE id =?"); + String psw = encrypt(password);//加密 password + ps.setString(1, psw);//存储加密后的 psw + ps.setString(2, id); + ResultSet results = ps.executeQuery(); + } +} diff --git a/sonar-keyware-plugins-java/src/test/files/PasswordInputTagJavaChecker.java b/sonar-keyware-plugins-java/src/test/files/PasswordInputTagJavaChecker.java new file mode 100644 index 0000000..6f89a5a --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/files/PasswordInputTagJavaChecker.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +import javax.swing.*; +public class SevneTeen{ + public void exampleFun(){ + //口令域使用明文输入的JTextField 控件 + JTextField passwordfield = new JTextField();// Noncompliant {{Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显}} +// passwordfield.setEchoChar("*");//设置回显的符号为*’ + } +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/RSAEncryptionRule.java b/sonar-keyware-plugins-java/src/test/files/RSAEncryptionRule.java index 9d8186e..23bd914 100644 --- a/sonar-keyware-plugins-java/src/test/files/RSAEncryptionRule.java +++ b/sonar-keyware-plugins-java/src/test/files/RSAEncryptionRule.java @@ -5,6 +5,8 @@ import java.security.*; public class RSAEncryptionRule { + private static final String ALGORITHM = "AES"; + public static void main(String[] args) throws Exception { // 生成RSA密钥对 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); @@ -16,7 +18,7 @@ public class RSAEncryptionRule { // 待加密的数据 String message = "Hello, RSA!"; - + Cipher cipher = Cipher.getInstance(ALGORITHM);// Noncompliant {{使用RSA最优加密填充}} // 使用公钥进行加密 Cipher encryptCipher = Cipher.getInstance("OAEPWithAndPadding");// Noncompliant {{使用RSA最优加密填充}} encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); diff --git a/sonar-keyware-plugins-java/src/test/files/RedirectUrlChecker.java b/sonar-keyware-plugins-java/src/test/files/RedirectUrlChecker.java index 3ff2969..643b8c5 100644 --- a/sonar-keyware-plugins-java/src/test/files/RedirectUrlChecker.java +++ b/sonar-keyware-plugins-java/src/test/files/RedirectUrlChecker.java @@ -6,6 +6,13 @@ import org.springframework.web.servlet.view.RedirectView; @Controller public class RedirectUrlChecker { + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String url = request.getParameter("url"); + //未经验证的 url可能是恶意的 + response.sendRedirect(url);// Noncompliant {{在重定向前对输入数据进行验证}} + return; + } @GetMapping("/old-url") public RedirectView redirectOldUrl(String url) { // Compliant,因为重定向的路径不是由方法传递进来的 RedirectView redirectView = new RedirectView(); diff --git a/sonar-keyware-plugins-java/src/test/files/SecurityCookieRule.java b/sonar-keyware-plugins-java/src/test/files/SecurityCookieRule.java index e4fa328..6d33575 100644 --- a/sonar-keyware-plugins-java/src/test/files/SecurityCookieRule.java +++ b/sonar-keyware-plugins-java/src/test/files/SecurityCookieRule.java @@ -10,10 +10,10 @@ public class SecurityCookieRule { Cookie cookie = new Cookie("cookieName", "cookieValue"); // 设置HttpOnly属性(防止通过JavaScript访问) - cookie.setHttpOnly(true); +// cookie.setHttpOnly(true); // 设置Secure属性(表示该Cookie只能通过HTTPS连接传输) - cookie.setSecure(true); +// cookie.setSecure(true); // 设置其他属性,比如过期时间等 // cookie.setMaxAge(3600); // 有效期为1小时 @@ -21,4 +21,17 @@ public class SecurityCookieRule { // 将Cookie添加到HTTP响应头中 response.addCookie(cookie); } +} + +public class Example{ + private String encrypt(String plaintext){ //加密函数,返回加密后的字符串 + Cipher cipher = Cipher.getInstance("AES"); + } + public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{// Noncompliant {{设置HTTPS会话中cookie的安全属性}} + String userlD= request.getParameter("userlD"); + String id = encrypt(userlD);//加密敏感信息 userlD + Cookie cookieID = new Cookie("userlD",id); + response.addCookie(cookieID);//没有设置 cookieID的 secure属性 + + } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java b/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java index 21332be..3e9c897 100644 --- a/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java +++ b/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java @@ -12,6 +12,7 @@ public class SessionCacheParamsChecker { public void doGet(HttpServletRequest request, HttpServletResponse response) { // 直接从request获取参数 + String prices = request.getParameter("price"); // Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} String param = request.getParameter("userId"); // Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} request.getParameter("userpassword");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} request.getParameter("token");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} diff --git a/sonar-keyware-plugins-java/src/test/files/UploadFileVerifyRule.java b/sonar-keyware-plugins-java/src/test/files/UploadFileVerifyRule.java index c06ede9..f0cfb47 100644 --- a/sonar-keyware-plugins-java/src/test/files/UploadFileVerifyRule.java +++ b/sonar-keyware-plugins-java/src/test/files/UploadFileVerifyRule.java @@ -50,4 +50,17 @@ public class UploadFileVerifyRule { return filename.substring(filename.lastIndexOf(".") + 1); } } + + public class Example{ + public void exampleFun(HttpServletRequest request){ + DiskFileltemFactory factory = new DiskFileltemFactory(); + ServletFileUpload upload = new ServletFileUpload(factory); + List items = upload.parseRequest(request); + Iterator iter = items.iterator(); + while(iter.hasNext()){ + Fileltem item = iter.next(); // Noncompliant {{程序设计时,应以“白名单”方式限制允许用户上传的文件的类型}} + //对文件的相关操作,但没有判断文件类型 + } + } + } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java b/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java index cbe3d82..69ab9dd 100644 --- a/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java +++ b/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java @@ -69,4 +69,22 @@ public class UserStatusVerifyChecker { } } } + + public class Example { + private boolean userExists(String username, String password) { //判断用户名口令是否正确 + } + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// Noncompliant {{对用户进行身份鉴别并建立一个新的会话时让原来的会话失效}} + String username = request.getParameter("username"); + String password = request.getParameter("password"); + if (username != null && password != null) { + + } + //通过用户名口令进行身份鉴别 + if (userExists(username, password)) { + //没有建立新的会话并让原来的会话失效 + HttpSession session = request.getSession(true); + } + } + } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyOneRule.java b/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyOneRule.java index bf67dab..8a383ec 100644 --- a/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyOneRule.java +++ b/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyOneRule.java @@ -1,4 +1,3 @@ - import org.springframework.web.WebApplicationInitializer; import org.springframework.web.filter.OncePerRequestFilter; @@ -10,7 +9,7 @@ import java.io.IOException; public class OptionsVerifyOneRule extends OncePerRequestFilter { @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Noncompliant {{应设置X-Frame-Options的值为deny}} response.setHeader("X-Frame-Options", "SAMEORIGIN"); // 或者使用SAMEORIGIN,ALLOW-FROM等其他策略 filterChain.doFilter(request, response); } @@ -24,4 +23,22 @@ class WebConfig implements WebApplicationInitializer { // ...其他配置... FilterRegistration.Dynamic registration = servletContext.addFilter("xFrameOptionsFilter", new OptionsVerifyOneRule()); } +} + +public class OptionsVerifyTwoRule implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse res = (HttpServletResponse) response;// Noncompliant {{应设置X-Frame-Options的值为deny}} + res.addHeader("X-Frame-Options", "iframe"); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyTwoRule.java b/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyTwoRule.java index 1de99bf..a503e90 100644 --- a/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyTwoRule.java +++ b/sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyTwoRule.java @@ -9,9 +9,8 @@ public class OptionsVerifyTwoRule implements Filter { public void init(FilterConfig filterConfig) throws ServletException {} @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - HttpServletResponse res = (HttpServletResponse) response; + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse res = (HttpServletResponse) response;// Noncompliant {{应设置X-Frame-Options的值为deny}} res.addHeader("X-Frame-Options", "iframe"); chain.doFilter(request, response); } diff --git a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/HostIdentityCheckerTest.java b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/HostIdentityCheckerTest.java new file mode 100644 index 0000000..ba1b27d --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/HostIdentityCheckerTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import com.keyware.sonar.java.utils.FilesUtils; +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +/** + * 通过用户名口令、数据证书等其他手段对主机身份进行鉴别 + * + * @author RenFengJiang + * @date 2024/7/3 + */ +public class HostIdentityCheckerTest { + + @Test + void detected() { + HostIdentityChecker rule = new HostIdentityChecker(); + + + CheckVerifier.newVerifier() + .onFile("src/test/files/HostIdentityChecker.java") + .withCheck(rule) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +} diff --git a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyCheckerTest.java b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyCheckerTest.java index 60a3d64..b5f3dd1 100644 --- a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyCheckerTest.java +++ b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyCheckerTest.java @@ -21,19 +21,14 @@ import java.util.Collection; * @date 2024/1/22 */ public class OptionsVerifyCheckerTest { - Collection lists = new ArrayList(){ - { - add("src/test/files/options/OptionsVerifyOneRule.java"); - add("src/test/files/options/OptionsVerifyTwoRule.java"); - } - }; + @Test void detected() { CheckVerifier.newVerifier() - .onFiles(lists) + .onFiles("src/test/files/options/OptionsVerifyOneRule.java") .withCheck(new OptionsVerifyChecker()) .withClassPath(FilesUtils.getClassPath("target/test-jars")) - .verifyIssueOnProject("应设置X-Frame-Options的值为deny"); + .verifyIssues(); } } diff --git a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaCheckerTest.java b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaCheckerTest.java new file mode 100644 index 0000000..f28414f --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaCheckerTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import com.keyware.sonar.java.utils.FilesUtils; +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +/** + * Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显 + * + * @author RenFengJiang + * @date 2024/7/3 + */ +public class PasswordInputTagJavaCheckerTest { + @Test + public void test(){ + CheckVerifier.newVerifier() + .onFile("src/test/files/PasswordInputTagJavaChecker.java") + .withCheck(new PasswordInputTagJavaChecker()) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +}