diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/Java.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/Java.java new file mode 100644 index 0000000..00e07d0 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/Java.java @@ -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(","); + } + +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/KeyJavaSensor.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/KeyJavaSensor.java new file mode 100644 index 0000000..c7d6123 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/KeyJavaSensor.java @@ -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> createCheckFilter(boolean isAutoScanCheckFiltering) { + if (isAutoScanCheckFiltering) { + Set 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 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 checks) { + List 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 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 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 getSourceFiles() { + return javaFiles(InputFile.Type.MAIN); + } + + private Iterable getTestFiles() { + return javaFiles(InputFile.Type.TEST); + } + + private Iterable javaFiles(InputFile.Type type) { + return fs.inputFiles(fs.predicates().and(fs.predicates().hasLanguage(Java.KEY), fs.predicates().hasType(type))); + } + + private JavaVersion getJavaVersion() { + Optional 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(); + } +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json new file mode 100644 index 0000000..b368eb1 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json @@ -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" + ] +}