From 39bd58671844c9d75c4aac36dec8e82f3cc6978e Mon Sep 17 00:00:00 2001 From: Etienne Cadic Date: Thu, 10 Feb 2022 12:05:47 +0100 Subject: [PATCH] :sparkles: provide ios support --- README.md | 13 +++ .../LicenseCheckRulesDefinition.java | 5 +- .../licensecheck/LicenseCheckSensor.java | 8 +- .../licensecheck/ValidateLicenses.java | 2 + .../gradle/GradleDependencyScanner.java | 23 +--- .../swift/SwiftDependencyScanner.java | 110 ++++++++++++++++++ .../licensecheck/utils/LicenseUtils.java | 33 ++++++ .../LicenseCheckRulesDefinitionTest.java | 2 +- 8 files changed, 171 insertions(+), 25 deletions(-) create mode 100644 src/main/java/at/porscheinformatik/sonarqube/licensecheck/swift/SwiftDependencyScanner.java create mode 100644 src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/LicenseUtils.java diff --git a/README.md b/README.md index 8f170426..e7cb409f 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,18 @@ Note: Please check above link for instructions or follow as mentioned below > gradle sonarqube +### IOS (Cocoapods, Carthage & Swift Package Manager) + +Use custom LicenseFinder ruby gem https://github.com/etiennecadicidean/LicenseFinder to generate license report for ios +projects + +``` +mkdir -p build/reports/license_finder/ +license_finder report -p --format json -q > build/reports/license_finder/swift-license-details.json +``` + +Then run sonarqube analysis + ## Features ### Analysis @@ -118,6 +130,7 @@ Currently supported formats are: * Maven POM files - all dependencies with scope "compile" and "runtime" are checked * NPM package.json files - all dependencies (except "devDependencies") are checked * Note that transitive dependencies are _not_ scanned unless `licensecheck.npm.resolvetransitive` is set to `true`. +* Swift dependencies (Reports must be generated with `License_finder tool`) ### Project Dashboard diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java index ae7a009b..8355c8ea 100644 --- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java @@ -12,11 +12,13 @@ public final class LicenseCheckRulesDefinition implements RulesDefinition public static final String LANG_JS = "js"; public static final String LANG_GROOVY = "grvy"; public static final String LANG_KOTLIN = "kotlin"; - + public static final String LANG_SWIFT = "swift"; + public static final String RULE_REPO_KEY = "licensecheck"; public static final String RULE_REPO_KEY_JS = "licensecheck-js"; public static final String RULE_REPO_KEY_GROOVY = "licensecheck-groovy"; public static final String RULE_REPO_KEY_KOTLIN = "licensecheck-kotlin"; + public static final String RULE_REPO_KEY_SWIFT = "licensecheck-swift"; public static final String RULE_UNLISTED_KEY = "licensecheck.unlisted"; public static final String RULE_NOT_ALLOWED_LICENSE_KEY = "licensecheck.notallowedlicense"; @@ -29,6 +31,7 @@ public void define(Context context) context.createRepository(RULE_REPO_KEY_JS, LANG_JS), context.createRepository(RULE_REPO_KEY_GROOVY, LANG_GROOVY), context.createRepository(RULE_REPO_KEY_KOTLIN, LANG_KOTLIN), + context.createRepository(RULE_REPO_KEY_SWIFT, LANG_SWIFT), }; for (NewRepository repo : repos) diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java index c197c1c1..671f72e4 100644 --- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java @@ -3,6 +3,7 @@ import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY_GROOVY; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY_JS; +import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY_SWIFT; import java.util.Set; import java.util.TreeSet; @@ -22,6 +23,7 @@ import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import at.porscheinformatik.sonarqube.licensecheck.maven.MavenDependencyScanner; import at.porscheinformatik.sonarqube.licensecheck.npm.PackageJsonDependencyScanner; +import at.porscheinformatik.sonarqube.licensecheck.swift.SwiftDependencyScanner; public class LicenseCheckSensor implements Sensor { @@ -43,7 +45,9 @@ public LicenseCheckSensor(FileSystem fs, Configuration configuration, ValidateLi new PackageJsonDependencyScanner(licenseMappingService, configuration.getBoolean(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS).orElse(false)), new MavenDependencyScanner(licenseMappingService), - new GradleDependencyScanner(licenseMappingService)}; + new GradleDependencyScanner(licenseMappingService), + new SwiftDependencyScanner(licenseMappingService), + }; } private static void saveDependencies(SensorContext sensorContext, Set dependencies) @@ -114,7 +118,7 @@ private static void saveMeasures(SensorContext sensorContext, Set licen public void describe(SensorDescriptor descriptor) { descriptor.name("License Check") - .createIssuesForRuleRepositories(RULE_REPO_KEY, RULE_REPO_KEY_JS, RULE_REPO_KEY_GROOVY); + .createIssuesForRuleRepositories(RULE_REPO_KEY, RULE_REPO_KEY_JS, RULE_REPO_KEY_GROOVY, RULE_REPO_KEY_SWIFT); } @Override diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java index fc9735a8..3c89d2c6 100644 --- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java @@ -251,6 +251,8 @@ private static String getRepoKey(Dependency dependency) return LicenseCheckRulesDefinition.RULE_REPO_KEY_JS; case LicenseCheckRulesDefinition.LANG_GROOVY: return LicenseCheckRulesDefinition.RULE_REPO_KEY_GROOVY; + case LicenseCheckRulesDefinition.LANG_SWIFT: + return LicenseCheckRulesDefinition.RULE_REPO_KEY_SWIFT; case LicenseCheckRulesDefinition.LANG_JAVA: default: return LicenseCheckRulesDefinition.RULE_REPO_KEY; diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java index 7e40eba8..4a0384ac 100644 --- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java @@ -16,7 +16,6 @@ import javax.json.JsonObject; import javax.json.JsonReader; -import org.codehaus.plexus.util.StringUtils; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -25,6 +24,7 @@ import at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition; import at.porscheinformatik.sonarqube.licensecheck.Scanner; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; +import at.porscheinformatik.sonarqube.licensecheck.utils.LicenseUtils; public class GradleDependencyScanner implements Scanner { @@ -56,7 +56,7 @@ public Set scan(SensorContext context) return readLicenseDetailsJson(licenseDetailsJsonFile) .stream() - .map(d -> matchLicense(defaultLicenseMap, d)) + .map(d -> LicenseUtils.matchLicense(defaultLicenseMap, d)) .peek(d -> d.setInputComponent(context.module())) .collect(Collectors.toSet()); } @@ -123,23 +123,4 @@ private String getModuleLicense(JsonArray arrModuleLicenses) } return moduleLicense; } - - private Dependency matchLicense(Map licenseMap, Dependency dependency) - { - if (StringUtils.isBlank(dependency.getLicense())) - { - LOGGER.info("Dependency '{}' has no license set.", dependency.getName()); - return dependency; - } - - for (Map.Entry entry : licenseMap.entrySet()) - { - if (entry.getKey().matcher(dependency.getLicense()).matches()) - { - dependency.setLicense(entry.getValue()); - break; - } - } - return dependency; - } } diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/swift/SwiftDependencyScanner.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/swift/SwiftDependencyScanner.java new file mode 100644 index 00000000..39c84b8e --- /dev/null +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/swift/SwiftDependencyScanner.java @@ -0,0 +1,110 @@ +package at.porscheinformatik.sonarqube.licensecheck.swift; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; + +import org.codehaus.plexus.util.StringUtils; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import at.porscheinformatik.sonarqube.licensecheck.Dependency; +import at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition; +import at.porscheinformatik.sonarqube.licensecheck.Scanner; +import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; +import at.porscheinformatik.sonarqube.licensecheck.utils.LicenseUtils; + +/* + * This Swift dependency scanner is based on reports produced by LicenseFinder opensource tool + */ +public class SwiftDependencyScanner implements Scanner { + private static final Logger LOGGER = Loggers.get(SwiftDependencyScanner.class); + private final LicenseMappingService licenseMappingService; + + public SwiftDependencyScanner(LicenseMappingService licenseMappingService) { + this.licenseMappingService = licenseMappingService; + } + + @Override + public Set scan(SensorContext context) { + File moduleDir = context.fileSystem().baseDir(); + + Map defaultLicenseMap = licenseMappingService.getLicenseMap(); + + File licenseDetailsJsonFile = new File(moduleDir, "build" + File.separator + "reports" + File.separator + + "license_finder" + File.separator + "swift-license-details.json"); + + if (!licenseDetailsJsonFile.exists()) { + LOGGER.info("No license-details.json file found in {} - skipping Swift dependency scan", + licenseDetailsJsonFile.getPath()); + return Collections.emptySet(); + } + + return readLicenseDetailsJson(licenseDetailsJsonFile) + .stream() + .map(d -> LicenseUtils.matchLicense(defaultLicenseMap, d)) + .peek(d -> d.setInputComponent(context.module())) + .collect(Collectors.toSet()); + } + + private Set readLicenseDetailsJson(File licenseDetailsJsonFile) { + final Set dependencySet = new HashSet<>(); + + try (InputStream fis = new FileInputStream(licenseDetailsJsonFile); + JsonReader jsonReader = Json.createReader(fis)) { + JsonObject jo = jsonReader.readObject(); + JsonArray arr = jo.getJsonArray("dependencies"); + prepareDependencySet(dependencySet, arr); + return dependencySet; + } catch (Exception e) { + LOGGER.error("Problems reading Swift license file {}: {}", + licenseDetailsJsonFile.getPath(), e.getMessage()); + } + return dependencySet; + } + + private void prepareDependencySet(Set dependencySet, JsonArray arr) { + for (javax.json.JsonValue entry : arr) { + JsonObject jsonDepObj = entry.asJsonObject(); + String moduleLicense = getModuleLicenseFromJsonObject(jsonDepObj); + String moduleLicenseUrl = null; + + Dependency dep = new Dependency(jsonDepObj.getString("name", null), + jsonDepObj.getString("version", null), moduleLicense, LicenseCheckRulesDefinition.LANG_SWIFT); + dep.setPomPath(moduleLicenseUrl); + dependencySet.add(dep); + } + } + + private String getModuleLicenseFromJsonObject(JsonObject jsonDepObj) { + String moduleLicense = null; + JsonArray arrModuleLicenses = jsonDepObj.getJsonArray("licenses"); + if (arrModuleLicenses != null) { + moduleLicense = getModuleLicense(arrModuleLicenses); + } + return moduleLicense; + } + + private String getModuleLicense(JsonArray arrModuleLicenses) { + if (arrModuleLicenses != null && !arrModuleLicenses.isEmpty()) { + if (arrModuleLicenses.getString(0).equals("unknown")) + return null; + return arrModuleLicenses.getString(0); + } else { + return null; + } + } + +} \ No newline at end of file diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/LicenseUtils.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/LicenseUtils.java new file mode 100644 index 00000000..4dc3b351 --- /dev/null +++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/LicenseUtils.java @@ -0,0 +1,33 @@ +package at.porscheinformatik.sonarqube.licensecheck.utils; + +import java.util.Map; +import java.util.regex.Pattern; + +import org.sonar.api.internal.apachecommons.lang.StringUtils; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import at.porscheinformatik.sonarqube.licensecheck.Dependency; + +public class LicenseUtils { + + private LicenseUtils() {} + + private static final Logger LOGGER = Loggers.get(LicenseUtils.class); + + static public Dependency matchLicense(Map licenseMap, Dependency dependency) { + if (StringUtils.isBlank(dependency.getLicense())) { + LOGGER.info("Dependency '{}' has no license set.", dependency.getName()); + return dependency; + } + + for (Map.Entry entry : licenseMap.entrySet()) { + if (entry.getKey().matcher(dependency.getLicense()).matches()) { + dependency.setLicense(entry.getValue()); + break; + } + } + return dependency; + } + +} diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java index 49ba446c..8d02777e 100644 --- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java +++ b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java @@ -15,7 +15,7 @@ public void define() new LicenseCheckRulesDefinition().define(context); - assertThat(context.repositories().size(), is(4)); + assertThat(context.repositories().size(), is(5)); for (RulesDefinition.Repository repository : context.repositories()) { assertThat(repository.rules().size(), is(2));