parent
92c262561a
commit
be9b9f2dfb
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java; |
||||
|
||||
import java.util.Arrays; |
||||
import org.sonar.api.config.Configuration; |
||||
import org.sonar.api.resources.AbstractLanguage; |
||||
|
||||
/** |
||||
* Java language implementation |
||||
* |
||||
* @since 1.3 |
||||
*/ |
||||
public class Java extends AbstractLanguage { |
||||
|
||||
/** |
||||
* Java key |
||||
*/ |
||||
public static final String KEY = "java"; |
||||
|
||||
/** |
||||
* Java name |
||||
*/ |
||||
public static final String NAME = "Java"; |
||||
|
||||
|
||||
/** |
||||
* Key of the file suffix parameter |
||||
*/ |
||||
public static final String FILE_SUFFIXES_KEY = "sonar.java.file.suffixes"; |
||||
|
||||
/** |
||||
* Default Java fil |
||||
* es knows suffixes |
||||
*/ |
||||
public static final String DEFAULT_FILE_SUFFIXES = ".java,.jav"; |
||||
|
||||
/** |
||||
* Settings of the plugin. |
||||
*/ |
||||
private final Configuration settings; |
||||
|
||||
/** |
||||
* Default constructor |
||||
*/ |
||||
public Java(Configuration settings) { |
||||
super(KEY, NAME); |
||||
this.settings = settings; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* @see org.sonar.api.resources.AbstractLanguage#getFileSuffixes() |
||||
*/ |
||||
@Override |
||||
public String[] getFileSuffixes() { |
||||
String[] suffixes = Arrays.stream(settings.getStringArray(Java.FILE_SUFFIXES_KEY)).filter(s -> s != null && !s.trim().isEmpty()).toArray(String[]::new); |
||||
return suffixes.length > 0 ? suffixes : DEFAULT_FILE_SUFFIXES.split(","); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,200 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java; |
||||
|
||||
import java.io.File; |
||||
import java.util.*; |
||||
import java.util.function.UnaryOperator; |
||||
import java.util.stream.Collectors; |
||||
import javax.annotation.Nullable; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.sonar.api.batch.Phase; |
||||
import org.sonar.api.batch.fs.FileSystem; |
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.Sensor; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.batch.sensor.SensorDescriptor; |
||||
import org.sonar.api.config.Configuration; |
||||
import org.sonar.api.issue.NoSonarFilter; |
||||
import org.sonar.api.rule.RuleKey; |
||||
import org.sonar.api.scan.issue.filter.FilterableIssue; |
||||
import org.sonar.api.scan.issue.filter.IssueFilterChain; |
||||
import org.sonar.java.JavaFrontend; |
||||
import org.sonar.java.Measurer; |
||||
import org.sonar.java.SonarComponents; |
||||
import org.sonar.java.annotations.VisibleForTesting; |
||||
import org.sonar.java.checks.CheckList; |
||||
import org.sonar.java.filters.PostAnalysisIssueFilter; |
||||
import org.sonar.java.jsp.Jasper; |
||||
import org.sonar.java.model.GeneratedFile; |
||||
import org.sonar.java.model.JavaVersionImpl; |
||||
import org.sonar.java.se.SymbolicExecutionVisitor; |
||||
import org.sonar.java.se.checks.SECheck; |
||||
import org.sonar.plugins.java.api.JavaCheck; |
||||
import org.sonar.plugins.java.api.JavaResourceLocator; |
||||
import org.sonar.plugins.java.api.JavaVersion; |
||||
import org.sonarsource.analyzer.commons.BuiltInQualityProfileJsonLoader; |
||||
import org.sonarsource.performance.measure.PerformanceMeasure; |
||||
|
||||
import static org.sonar.api.rules.RuleAnnotationUtils.getRuleKey; |
||||
|
||||
|
||||
@Phase(name = Phase.Name.PRE) |
||||
public class KeyJavaSensor implements Sensor{ |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KeyJavaSensor.class); |
||||
|
||||
private static final String PERFORMANCE_MEASURE_ACTIVATION_PROPERTY = "sonar.java.performance.measure"; |
||||
private static final String PERFORMANCE_MEASURE_FILE_PATH_PROPERTY = "sonar.java.performance.measure.path"; |
||||
private static final String PERFORMANCE_MEASURE_DESTINATION_FILE = "sonar.java.performance.measure.json"; |
||||
static final String SONAR_WAY_PATH = "/org/sonar/l10n/java/rules/java/Sonar_way_profile.json"; |
||||
private final SonarComponents sonarComponents; |
||||
private final FileSystem fs; |
||||
private final JavaResourceLocator javaResourceLocator; |
||||
private final Configuration settings; |
||||
private final NoSonarFilter noSonarFilter; |
||||
@Nullable |
||||
private final Jasper jasper; |
||||
private final PostAnalysisIssueFilter postAnalysisIssueFilter; |
||||
|
||||
public KeyJavaSensor(SonarComponents sonarComponents, FileSystem fs, |
||||
JavaResourceLocator javaResourceLocator, Configuration settings, |
||||
NoSonarFilter noSonarFilter, |
||||
PostAnalysisIssueFilter postAnalysisIssueFilter, @Nullable Jasper jasper) { |
||||
|
||||
this.noSonarFilter = noSonarFilter; |
||||
this.sonarComponents = sonarComponents; |
||||
this.fs = fs; |
||||
this.javaResourceLocator = javaResourceLocator; |
||||
this.settings = settings; |
||||
this.postAnalysisIssueFilter = postAnalysisIssueFilter; |
||||
this.jasper = jasper; |
||||
this.sonarComponents.registerMainCheckClasses(CheckList.REPOSITORY_KEY, CheckList.getJavaChecks()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void describe(SensorDescriptor descriptor) { |
||||
descriptor.onlyOnLanguage(Java.KEY).name("KeyJavaSensor"); |
||||
} |
||||
|
||||
@Override |
||||
public void execute(SensorContext context) { |
||||
PerformanceMeasure.Duration sensorDuration = createPerformanceMeasureReport(context); |
||||
|
||||
sonarComponents.setSensorContext(context); |
||||
|
||||
sonarComponents.setCheckFilter(createCheckFilter(sonarComponents.isAutoScanCheckFiltering())); |
||||
|
||||
Measurer measurer = new Measurer(context, noSonarFilter); |
||||
|
||||
|
||||
|
||||
JavaFrontend frontend = new JavaFrontend(getJavaVersion(), sonarComponents, measurer, javaResourceLocator, postAnalysisIssueFilter, |
||||
insertSymbolicExecutionVisitor(sonarComponents.mainChecks())); |
||||
frontend.scan(getSourceFiles(), getTestFiles(), runJasper(context)); |
||||
|
||||
sensorDuration.stop(); |
||||
} |
||||
|
||||
private UnaryOperator<List<JavaCheck>> createCheckFilter(boolean isAutoScanCheckFiltering) { |
||||
if (isAutoScanCheckFiltering) { |
||||
Set<RuleKey> autoScanCompatibleRules = new HashSet<>(KeyJavaSensor.sonarJavaSonarWayRuleKeys()); |
||||
|
||||
CheckList.getJavaChecksNotWorkingForAutoScan().stream() |
||||
.map(checkClass -> RuleKey.of(CheckList.REPOSITORY_KEY, getRuleKey(checkClass))) |
||||
.forEach(autoScanCompatibleRules::remove); |
||||
|
||||
|
||||
return checks -> checks.stream() |
||||
.filter(check -> sonarComponents.getRuleKey(check).map(autoScanCompatibleRules::contains).orElse(false)) |
||||
.collect(Collectors.toList()); |
||||
} else { |
||||
return UnaryOperator.identity(); |
||||
} |
||||
} |
||||
|
||||
static Set<RuleKey> sonarJavaSonarWayRuleKeys() { |
||||
return BuiltInQualityProfileJsonLoader.loadActiveKeysFromJsonProfile(SONAR_WAY_PATH).stream() |
||||
.map(rule -> RuleKey.of(CheckList.REPOSITORY_KEY, rule)) |
||||
.collect(Collectors.toSet()); |
||||
} |
||||
|
||||
private static PerformanceMeasure.Duration createPerformanceMeasureReport(SensorContext context) { |
||||
return PerformanceMeasure.reportBuilder() |
||||
.activate(context.config().get(PERFORMANCE_MEASURE_ACTIVATION_PROPERTY).filter("true"::equals).isPresent()) |
||||
.toFile(context.config().get(PERFORMANCE_MEASURE_FILE_PATH_PROPERTY) |
||||
.filter(path -> !path.isEmpty()) |
||||
.orElseGet(() -> Optional.ofNullable(context.fileSystem().workDir()) |
||||
.filter(File::exists) |
||||
.map(file -> file.toPath().resolve(PERFORMANCE_MEASURE_DESTINATION_FILE).toString()) |
||||
.orElse(null))) |
||||
.appendMeasurementCost() |
||||
.start("KeyJavaSensor"); |
||||
} |
||||
|
||||
@VisibleForTesting |
||||
static JavaCheck[] insertSymbolicExecutionVisitor(List<JavaCheck> checks) { |
||||
List<SECheck> seChecks = checks.stream() |
||||
.filter(SECheck.class::isInstance) |
||||
.map(SECheck.class::cast) |
||||
.collect(Collectors.toList()); |
||||
if (seChecks.isEmpty()) { |
||||
LOG.info("No rules with 'symbolic-execution' tag were enabled," |
||||
+ " the Symbolic Execution Engine will not run during the analysis."); |
||||
return checks.toArray(new JavaCheck[0]); |
||||
} |
||||
List<JavaCheck> newList = new ArrayList<>(checks); |
||||
// insert an instance of SymbolicExecutionVisitor before the first SECheck
|
||||
newList.add(newList.indexOf(seChecks.get(0)), new SymbolicExecutionVisitor(seChecks)); |
||||
return newList.toArray(new JavaCheck[0]); |
||||
} |
||||
|
||||
private Collection<GeneratedFile> runJasper(SensorContext context) { |
||||
if (sonarComponents.isAutoScan()) { |
||||
// for security reasons, do not run jasper to generate code in autoscan mode
|
||||
return Collections.emptyList(); |
||||
} |
||||
return jasper != null ? jasper.generateFiles(context, sonarComponents.getJavaClasspath()) : Collections.emptyList(); |
||||
} |
||||
|
||||
private Iterable<InputFile> getSourceFiles() { |
||||
return javaFiles(InputFile.Type.MAIN); |
||||
} |
||||
|
||||
private Iterable<InputFile> getTestFiles() { |
||||
return javaFiles(InputFile.Type.TEST); |
||||
} |
||||
|
||||
private Iterable<InputFile> javaFiles(InputFile.Type type) { |
||||
return fs.inputFiles(fs.predicates().and(fs.predicates().hasLanguage(Java.KEY), fs.predicates().hasType(type))); |
||||
} |
||||
|
||||
private JavaVersion getJavaVersion() { |
||||
Optional<String> javaVersionAsString = settings.get(JavaVersion.SOURCE_VERSION); |
||||
if (!javaVersionAsString.isPresent()) { |
||||
return new JavaVersionImpl(); |
||||
} |
||||
String enablePreviewAsString = settings.get(JavaVersion.ENABLE_PREVIEW).orElse("false"); |
||||
|
||||
JavaVersion javaVersion = JavaVersionImpl.fromStrings(javaVersionAsString.get(), enablePreviewAsString); |
||||
if (javaVersion.arePreviewFeaturesEnabled() && javaVersion.asInt() < JavaVersionImpl.MAX_SUPPORTED) { |
||||
LOG.warn("sonar.java.enablePreview is set but will be discarded as the Java version is less than the max" + |
||||
" supported version ({} < {})", javaVersion.asInt(), JavaVersionImpl.MAX_SUPPORTED); |
||||
javaVersion = new JavaVersionImpl(javaVersion.asInt(), false); |
||||
} |
||||
LOG.info("Configured Java source version ({}): {}, preview features enabled ({}): {}", |
||||
JavaVersion.SOURCE_VERSION, javaVersion.asInt(), JavaVersion.ENABLE_PREVIEW, javaVersion.arePreviewFeaturesEnabled()); |
||||
return javaVersion; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
} |
@ -0,0 +1,33 @@ |
||||
{ |
||||
"name": "Sonar way", |
||||
"ruleKeys": [ |
||||
"ABCVarNameChecker", |
||||
"AbsolutePathDetectorChecker", |
||||
"AuthenticationChecker", |
||||
"AvoidSensitiveInfoInLogsCheck", |
||||
"ConfigurationFileChecker", |
||||
"CookieSensitiveParameterCheck", |
||||
"DynamicCodeChecker", |
||||
"DynamicLibraryLoadChecker", |
||||
"ErrorMessageChecker", |
||||
"FileCheck", |
||||
"HashSaltPassWordChecker", |
||||
"HttpInputDataChecker", |
||||
"InputSQLVerifyChecker", |
||||
"Md5PassWordVerifyChecker", |
||||
"OptionsVerifyChecker", |
||||
"PasswordInputTagChecker", |
||||
"PasswordRegexCheck", |
||||
"PathAndKeywordCheck", |
||||
"RedirectUrlChecker", |
||||
"RSAEncryptionChecker", |
||||
"SecurityCookieChecker", |
||||
"SendMessageVerifyChecker", |
||||
"SessionCacheParamsChecker", |
||||
"SessionDateChecker", |
||||
"SystemFunctionChecker", |
||||
"UploadFileVerifyChecker", |
||||
"UpperCycleLimitRuleChecker", |
||||
"UserStatusVerifyChecker" |
||||
] |
||||
} |
Loading…
Reference in new issue