修复:根据测试提供结果,修改Java规则漏报误报

master
RenFengJiang 5 months ago
parent d2271ff4fb
commit d302ca7340
  1. 31
      sonar-keyware-plugins-ConfigurationDetection/src/main/java/com/keyware/sonar/Configuration/rules/checkers/SessionDateChecker.java
  2. 1
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java
  3. 258
      sonar-keyware-plugins-cxx/src/main/java/org/sonar/cxx/squidbridge/AstScanner.java
  4. 26
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/FileCheck.java
  5. 21
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HashSaltPassWordChecker.java
  6. 81
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/HostIdentityChecker.java
  7. 2
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/InputSQLVerifyChecker.java
  8. 20
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/Md5PassWordVerifyChecker.java
  9. 95
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyChecker.java
  10. 89
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaChecker.java
  11. 75
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RSAEncryptionChecker.java
  12. 53
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/RedirectUrlChecker.java
  13. 38
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SecurityCookieChecker.java
  14. 3
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java
  15. 47
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UploadFileVerifyChecker.java
  16. 4
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UpperCycleLimitRuleChecker.java
  17. 36
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java
  18. 16
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.html
  19. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/HostIdentityChecker.json
  20. 16
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.html
  21. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PasswordInputTagJavaChecker.json
  22. 26
      sonar-keyware-plugins-java/src/test/files/FileCheck.java
  23. 20
      sonar-keyware-plugins-java/src/test/files/HashSaltPassWordRule.java
  24. 26
      sonar-keyware-plugins-java/src/test/files/HostIdentityChecker.java
  25. 11
      sonar-keyware-plugins-java/src/test/files/InputSQLVerifyRule.java
  26. 20
      sonar-keyware-plugins-java/src/test/files/Md5PassWordVerifyRule.java
  27. 15
      sonar-keyware-plugins-java/src/test/files/PasswordInputTagJavaChecker.java
  28. 4
      sonar-keyware-plugins-java/src/test/files/RSAEncryptionRule.java
  29. 7
      sonar-keyware-plugins-java/src/test/files/RedirectUrlChecker.java
  30. 17
      sonar-keyware-plugins-java/src/test/files/SecurityCookieRule.java
  31. 1
      sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java
  32. 13
      sonar-keyware-plugins-java/src/test/files/UploadFileVerifyRule.java
  33. 18
      sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java
  34. 21
      sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyOneRule.java
  35. 5
      sonar-keyware-plugins-java/src/test/files/options/OptionsVerifyTwoRule.java
  36. 33
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/HostIdentityCheckerTest.java
  37. 11
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/OptionsVerifyCheckerTest.java
  38. 29
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PasswordInputTagJavaCheckerTest.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("<session-timeout>-1</session-timeout>")) {
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 {

@ -313,6 +313,7 @@ public class CxxSquidSensor implements ProjectSensor {
var squidConfig = createConfiguration();
var scanner = CxxAstScanner.create(squidConfig, visitors.toArray(new SquidAstVisitor[visitors.size()]));
Iterable<InputFile> inputFiles = getInputFiles(context, squidConfig);
scanner.scanInputFiles(inputFiles);

@ -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<G extends Grammar> {
private static final Logger LOG = Loggers.get(AstScanner.class);
private final List<SquidAstVisitor<G>> visitors;
private final Parser<G> parser;
private final SquidAstVisitorContextImpl<G> context;
private final SquidIndex indexer = new SquidIndex();
private final MetricDef[] metrics;
private final MetricDef filesMetric;
protected AstScanner(Builder<G> 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<File> 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<InputFile> 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 <G extends Grammar> Builder<G> builder(SquidAstVisitorContextImpl<G> context) {
return new Builder<>(context);
}
public static class Builder<G extends Grammar> {
private Parser<G> baseParser;
private final List<SquidAstVisitor<G>> visitors = Lists.newArrayList();
private final SquidAstVisitorContextImpl<G> context;
private CommentAnalyser commentAnalyser;
private MetricDef[] metrics;
private MetricDef filesMetric;
public Builder(SquidAstVisitorContextImpl<G> context) {
checkNotNull(context, "context cannot be null");
this.context = context;
}
public Builder<G> setBaseParser(Parser<G> baseParser) {
checkNotNull(baseParser, "baseParser cannot be null");
this.baseParser = baseParser;
return this;
}
public Builder<G> setCommentAnalyser(CommentAnalyser commentAnalyser) {
checkNotNull(commentAnalyser, "commentAnalyser cannot be null");
this.commentAnalyser = commentAnalyser;
return this;
}
public Builder<G> withSquidAstVisitor(SquidAstVisitor<G> visitor) {
checkNotNull(visitor, "visitor cannot be null");
visitor.setContext(context);
visitors.add(visitor);
return this;
}
public Builder<G> withMetrics(MetricDef... metrics) {
for (var metric : metrics) {
checkNotNull(metric, "metrics cannot be null");
}
this.metrics = metrics.clone();
return this;
}
public Builder<G> setFilesMetric(MetricDef filesMetric) {
checkNotNull(filesMetric, "filesMetric cannot be null");
this.filesMetric = filesMetric;
return this;
}
public AstScanner<G> 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);
}
}
}

@ -23,7 +23,7 @@ import java.util.List;
*/
@Rule(key = "FileCheck")
public class FileCheck extends IssuableSubscriptionVisitor {
private static final List<String> FILE_NAME_VARS = Arrays.asList("fileName", "fileExt", "fileSuffix");
private static final List<String> FILE_NAME_VARS = Arrays.asList("fileName", "fileExt", "fileSuffix", "fileEnd");
@Override
public List<Tree.Kind> 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;
}
});
}
}
}

@ -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, "应使用盐值计算口令");
}
}
}
}
}

@ -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<Tree.Kind> nodesToVisit() {
return Collections.singletonList(Tree.Kind.METHOD);
}
@Override
public void visitNode(Tree tree) {
MethodTree methodTree = (MethodTree) tree;
List<VariableTree> 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;
}
}
}
}
}

@ -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)) {

@ -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<ExpressionTree> 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, "应使用单向不可逆的加密算法");
}
}
}
}
}
}

@ -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<Tree.Kind> 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<VariableTree> 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");
}
}
}
}
}
}

@ -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<Tree.Kind> 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;
}
}
}
}
}

@ -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<Tree.Kind> 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<ExpressionTree> 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<ExpressionTree> 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());
}
}
}
}
}

@ -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<? extends VariableTree> 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<String>();
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方法
*

@ -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;
}
}
}

@ -29,7 +29,8 @@ public class SessionCacheParamsChecker extends IssuableSubscriptionVisitor {
"userid",
"token",
"url",
"userpassword"
"userpassword",
"price"
);
private static final String requestType = "HttpServletRequest";

@ -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<VariableTree> 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);
}
}
}
}
}
}

@ -32,7 +32,9 @@ public class UpperCycleLimitRuleChecker extends IssuableSubscriptionVisitor {
MethodTree methodTree = (MethodTree) tree;
List<VariableTree> args = methodTree.parameters();
BlockTree blockTree = methodTree.block();
blockTree.accept(new MethodBlockVisitor(this, args));
if(blockTree != null){
blockTree.accept(new MethodBlockVisitor(this, args));
}
}

@ -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<VariableTree> 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<VariableTree> 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;
}
}

@ -0,0 +1,16 @@
<!--
~ Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
~ 项目名称:信息安全性设计准则检查插件
~ 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件
~ 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
-->
<p>通过用户名口令、数据证书等其他手段对主机身份进行鉴别</p>
<h2>通过用户名口令、数据证书等其他手段对主机身份进行鉴别</h2>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "通过用户名口令、数据证书等其他手段对主机身份进行鉴别",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Minor"
}

@ -0,0 +1,16 @@
<!--
~ Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
~ 项目名称:信息安全性设计准则检查插件
~ 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件
~ 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
-->
<h2>Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显</h2>
<p>Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显</p>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "Java用户输入口令时应对口令域进行掩饰,用户输入的每一个字符都应该以星号形式回显",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "15min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Major"
}

@ -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<FileItem> items = upload.parseRequest(request);
Iterator<Fileltem> 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 {{在服务器端不允许仅仅依赖文件的名称或者扩展后缀决定软件的行为,应依赖文件的内容决定软件的行为}}
//对文件的相关操作
}
}
}
}

@ -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 存放到数据库
}
}
}

@ -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){
// }
}
}

@ -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;
}
}
}

@ -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);
@ -27,3 +27,19 @@ public class Md5PassWordVerifyRule{
}
}
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();
}
}

@ -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("*");//设置回显的符号为*’
}
}

@ -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("OAEPWith<digest>And<mgf>Padding");// Noncompliant {{使用RSA最优加密填充}}
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

@ -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();

@ -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小时
@ -22,3 +22,16 @@ public class SecurityCookieRule {
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属性
}
}

@ -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等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}}

@ -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<FileItem> items = upload.parseRequest(request);
Iterator<Fileltem> iter = items.iterator();
while(iter.hasNext()){
Fileltem item = iter.next(); // Noncompliant {{程序设计时,应以“白名单”方式限制允许用户上传的文件的类型}}
//对文件的相关操作,但没有判断文件类型
}
}
}
}

@ -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);
}
}
}
}

@ -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);
}
@ -25,3 +24,21 @@ 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() {
}
}

@ -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);
}

@ -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();
}
}

@ -21,19 +21,14 @@ import java.util.Collection;
* @date 2024/1/22
*/
public class OptionsVerifyCheckerTest {
Collection<String> 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();
}
}

@ -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();
}
}
Loading…
Cancel
Save