Repository: porscheinformatik/sonarqube-licensecheck Branch: master Commit: 998fe713a3a4 Files: 87 Total size: 315.3 KB Directory structure: gitextract_055ceoix/ ├── .babelrc ├── .editorconfig ├── .github/ │ └── workflows/ │ ├── build.yml │ ├── release.yml │ └── verify.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docker-compose.yml ├── jsconfig.json ├── mise.toml ├── package.json ├── pom.xml ├── renovate.json ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── at/ │ │ │ └── porscheinformatik/ │ │ │ └── sonarqube/ │ │ │ └── licensecheck/ │ │ │ ├── Dependency.java │ │ │ ├── LicenseCheckMetrics.java │ │ │ ├── LicenseCheckPageDefinition.java │ │ │ ├── LicenseCheckPlugin.java │ │ │ ├── LicenseCheckPropertyKeys.java │ │ │ ├── LicenseCheckRulesDefinition.java │ │ │ ├── LicenseCheckSensor.java │ │ │ ├── Scanner.java │ │ │ ├── ValidateLicenses.java │ │ │ ├── dependencymapping/ │ │ │ │ ├── DependencyMapping.java │ │ │ │ └── DependencyMappingService.java │ │ │ ├── gradle/ │ │ │ │ └── GradleDependencyScanner.java │ │ │ ├── license/ │ │ │ │ ├── License.java │ │ │ │ └── LicenseService.java │ │ │ ├── licensemapping/ │ │ │ │ ├── LicenseMapping.java │ │ │ │ └── LicenseMappingService.java │ │ │ ├── maven/ │ │ │ │ ├── DirectoryFinder.java │ │ │ │ ├── LicenseFinder.java │ │ │ │ ├── MavenDependencyScanner.java │ │ │ │ ├── Setting.java │ │ │ │ ├── SettingsXmlHandler.java │ │ │ │ └── SettingsXmlParser.java │ │ │ ├── npm/ │ │ │ │ └── PackageJsonDependencyScanner.java │ │ │ ├── projectlicense/ │ │ │ │ ├── ProjectLicense.java │ │ │ │ └── ProjectLicenseService.java │ │ │ └── utils/ │ │ │ └── IOUtils.java │ │ ├── resources/ │ │ │ └── at/ │ │ │ └── porscheinformatik/ │ │ │ └── sonarqube/ │ │ │ └── licensecheck/ │ │ │ ├── license/ │ │ │ │ └── spdx_license_list.json │ │ │ └── licensemapping/ │ │ │ └── default_license_mapping.json │ │ └── web/ │ │ ├── configuration/ │ │ │ ├── configuration.jsx │ │ │ ├── dependency-mappings-page.jsx │ │ │ ├── license-mappings-page.jsx │ │ │ ├── licenses-page.jsx │ │ │ ├── project-licenses-page.jsx │ │ │ └── sonar-api.js │ │ ├── configuration.js │ │ ├── dashboard/ │ │ │ ├── dashboard.jsx │ │ │ ├── dependencies.jsx │ │ │ ├── excel-builder.js │ │ │ └── licenses.jsx │ │ ├── dashboard.js │ │ ├── property_keys.js │ │ └── shared/ │ │ └── styles.css │ └── test/ │ ├── java/ │ │ └── at/ │ │ └── porscheinformatik/ │ │ └── sonarqube/ │ │ └── licensecheck/ │ │ ├── DependencyTest.java │ │ ├── LicenseCheckMetricsTest.java │ │ ├── LicenseCheckPageDefinitionTest.java │ │ ├── LicenseCheckPluginTest.java │ │ ├── LicenseCheckRulesDefinitionTest.java │ │ ├── LicenseCheckSensorTest.java │ │ ├── LicenseTest.java │ │ ├── PackageJsonDependencyScannerTest.java │ │ ├── ValidateLicensesTest.java │ │ ├── dependencymapping/ │ │ │ └── DependencyMappingServiceTest.java │ │ ├── gradle/ │ │ │ └── GradleDependencyScannerTest.java │ │ ├── licensemapping/ │ │ │ ├── LicenseMappingServiceTest.java │ │ │ └── LicenseMappingTest.java │ │ ├── maven/ │ │ │ ├── DependencyMappingScannerTest.java │ │ │ └── DirectoryFinderTest.java │ │ ├── projectlicense/ │ │ │ └── ProjectLicenseTest.java │ │ └── utils/ │ │ └── IOUtilsTest.java │ └── resources/ │ ├── build/ │ │ ├── my-reports/ │ │ │ └── license-details.json │ │ └── reports/ │ │ └── dependency-license/ │ │ └── license-details.json │ ├── deprecated_multilicense_project/ │ │ └── package.json │ ├── deprecated_project/ │ │ └── package.json │ ├── example/ │ │ └── package.json │ ├── example_nested/ │ │ └── example/ │ │ └── package.json │ ├── no_package_json/ │ │ └── .gitkeep │ ├── spdc_license_list.json │ └── test.pom ├── test-server.js └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["@babel/preset-env", "@babel/preset-react"] } ================================================ FILE: .editorconfig ================================================ # EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true [*] charset = utf-8 insert_final_newline = true indent_style = space indent_size = 2 [*.java] indent_size = 4 curly_bracket_next_line = true ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches: "**" jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup java uses: actions/setup-java@v5 with: java-version: 17 distribution: temurin - name: Build with SonarCloud env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | mvn -B clean mvn -B jacoco:prepare-agent install jacoco:report mvn -B -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=porscheinformatik sonar:sonar ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: ["v*"] release: types: [created] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Set Release Version run: | mvn -B clean mvn -B versions:set -DnewVersion=${GITHUB_REF:11} -DgenerateBackupPoms=false - name: Build run: mvn -B install - name: Release uses: softprops/action-gh-release@v2.5.0 if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: prerelease: contains(github.ref, 'beta') files: target/sonarqube-licensecheck-plugin-*.jar ================================================ FILE: .github/workflows/verify.yml ================================================ name: Verify on: pull_request: branches: [master] jobs: build: runs-on: ubuntu-latest strategy: matrix: java: [17, 21] steps: - uses: actions/checkout@v6 - name: Setup java uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: temurin - name: Build with Maven run: | mvn -B clean mvn -B verify ================================================ FILE: .gitignore ================================================ .classpath .project .factorypath .settings/ target/ node_modules/ node/ .idea/ *.iml .vscode/ .yarn/ docker/plugins/*.jar .env ================================================ FILE: .prettierignore ================================================ pnpm-lock.yaml ================================================ FILE: .prettierrc ================================================ { "plugins": ["prettier-plugin-java"], "printWidth": 100 } ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [7.1.0](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v7.0.3...v7.1.0) - 2026-02-20 ### Bug Fixes - Improve layout and scrolling behavior of the license dashboard (License Check page could not be scrolled after update to SonarQube Community 26.1.0) (#464, #467) ### Other Changes - Update dependencies - Switch from yarn to pnpm for package management ## [7.0.3](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v7.0.2...v7.0.3) - 2025-07-09 ### Bug Fixes - fix: skip artifacts with "data" classifier (#456, #457) Some Maven dependencies have an additional artifact with "data" classifiers which leads to the issue that in some cases the license cannot be found correctly. ### Other Changes - chore: fix dependencies for echoes-react and bump minor versions in package.json (#455) ## [7.0.2](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v7.0.1...v7.0.2) - 2025-06-23 ### Bug Fixes - Also check maven.repo.local is set via MAVEN_OPTS in License finding (#451) ## [7.0.1](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v7.0.0...v7.0.1) - 2025-06-23 ### Bug Fixes - When maven.repo.local is set via MAVEN_OPTS it is not considered (#449) ## [7.0.0](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v6.0.1...v7.0.0) - 2025-05-15 ### BREAKING CHANGES This version is not compatible with SonarQube < 2025.0 / 25.x ### Features - Update to SonarQube 2025.x/25.x in (#435) - Switch UI from Vue to React (#435) ### Bug Fixes - Fix #409 - Switched back to baseDir by @tgwbean in #416 ### Other Changes - Update dependencies ## [6.0.1](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v6.0.0...v6.0.1) - 2024-01-26 ### Bug Fixes - Dependency mapping "overwrite" should default to true (#413) ## [6.0.0](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v5.1.1...v6.0.0) - 2023-12-15 ### BREAKING CHANGES This version is not compatible with SonarQube < 9.5 ### Features - Compatibility with Sonar 10.x (#375) - Support for Scala (#352) - Feature to import SPDX license list (fa68e04422bdd12d05e78c72ee0a49224f6b8741) - Resolve node_modules relative to package.json (#380) - Make report path configurable in Gradle scanner (#397) Other Changes: - Use Prettier for code formatting #399 ## [5.1.1](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v5.1.0...v5.1.1) - 2022-11-04 ### Bug Fixes - Fix Kotlin seems to not be supported on 5.1.0 (#350, #351) - Improving NPM scan resilience (#347) ### Other Changes - Fix some critical and major code smells (#353) - New and shiny README (#343) ## [5.1.0](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v5.0.1...v5.1.0) - 2022-08-25 ### Bug Fixes - Rule repository for TypeScript was not registered (#315) - Show correct measures for branches and pull requests (#325) ### Other Changes - Add defined order to settings (#342) ## [5.0.1](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v5.0.0..v5.0.1) - 2022-02-24 ### Bug Fixes - Fix: rule repository for Kotlin was not registered (#311, #310) - Fix: project licenses page call (#291, #296) ## [5.0.0](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v4.0.2..v5.0.0) - 2021-12-20 ### BREAKING CHANGES - This version uses a different format for storing the settings. The old settings will be migrated (and deleted) on the first start of SonarQube with the new version. You cannot go back to a previous version! With this change you can edit the settings via the default SonarQube UI or via the custom License Check settings. This change was necessary to remove the dependency on the internal SonarQube API. [#240, #244] ### Features - Create issues on relevant dependency files (#285) - License and dependency mapping now available for all dependency mechanisms (Groovy, Maven, NPM). Dependency mapping has now an attribute to toggle forced mapping. [#257] - Support for JavaScript and Groovy projects (without any Java files) (#247, #241, #182) ### Bug Fixes - Status in license report should be "Allowed" and "Disallowed" not true/false (#262) ### Other Changes - Dependency updates and increase test coverage ## [4.0.2](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v4.0.1..v4.0.2) - 2021-05-04 ### Bug Fixes - Create license with status disallowed (#230), fixes #209 ### Other Changes - Update SonarQube API to 8.8 (#229) ## [4.0.1](https://github.com/porscheinformatik/sonarqube-licensecheck/compare/v4.0.0..v4.0.1) - 2021-05-04 ### Bug Fixes - Icons in UI are missing #217 ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015-2018 Porsche Informatik Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # SonarQube License-Check [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=at.porscheinformatik.sonarqube.licensecheck:sonarqube-licensecheck-plugin&metric=alert_status)](https://sonarcloud.io/dashboard?id=at.porscheinformatik.sonarqube.licensecheck:sonarqube-licensecheck-plugin) This [SonarQube](http://www.sonarqube.org/) plugin ensures that projects use dependencies with compliant licenses. All dependencies and licenses can be viewed per projects and exported to Excel 2003 XML Format. This enables a simple governance of dependencies and licenses for the whole organization. ## License This software is licensed under the [Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) ## Table of Contents - [Features](#features) - [Compatibility](#compatibility) - [Installation](#installation) - [General Configuration](#configuration) - [General Configuration via Administration Tab](#configuration-via-administration-tab) - [General Configuration via License Menu](#configuration-via-license-menu) - [Activation rules in Quality Profile](#activation-rules-in-quality-profile) - [Execution](#execution) - [Supported Languages](#supported-languages) - [Supported Project Types](#supported-project-types) - [Maven](#maven) - [NPM](#npm) - [Gradle](#gradle) - [Configuration via Sonar API](#configuration-via-sonar-api) ## Features ### Analysis The plugin scans for dependencies defined in your project including all transitive dependencies. Currently, supported formats are: - Maven POM files - all dependencies with scope "compile" and "runtime" are checked - Gradle projects which use JK1 plugin - 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`. ![Transitive](docs/Administration_General_Settings_License_Check_2.png) ### Project Dashboard The plugin contains a project dashboard showing a list of dependencies with version and a list of all used licences. Each table shows the status of the license (allowed, not allowed, not found). You can also export the data to Excel. ![Project Dashboard](docs/License_Check_dashboard.png) ## Compatibility This plugin is compatible: - 7.x is compatible with SonarQube 25.x / 2025 LTS and 26.x / 2026 LTS version - 6.x is compatible with SonarQube 9 LTS (>= 9.5) and 10.x - 5.x is compatible with SonarQube 8.9 LTS and < 10 (9.x is compatible) - 4.x is compatible with SonarQube 8.x For all changes see [CHANGELOG.md](CHANGELOG.md) ## Installation Put the pre-built jar-file (from release downloads) in the directory `$SONARQUBE_HOME/extensions/plugins` and restart the server to install the plugin. Activate the rules of this plugin ("License is not allowed", "Dependency has unknown license") in your SonarQube quality profiles - otherwise the plugin is not executed. ## Configuration After booting the SonarQube Server with the License-Check Plugin be found in the tab Administration or also in the Configuration -> LicenseCheck drop down menu. ### Configuration via Administration Tab - Within the General Settings and License Check you find the settings for the plugin. - Within the general settings the plugin can be manually enabled or disabled. By default, it is enabled. - Under "Dependency Mapping" you can map a dependency name/key (with regex) to a license, e.g. `^asm:asm$` to "BSD-3-Clause" - Under "License Mapping" you can map a license name (with regex) to a license, e.g. `.*Apache.*2.*` to "Apache-2.0". ![License Configuration1](docs/Administration_General_Settings_License_Check_1.png) - Under "Licenses" you can allow or disallow licenses globally and add/edit the list of known licenses. ![License Configuration2](docs/Administration_General_Settings_License_Check_3.png) - Under "Project Licenses" you can allow and disallow licenses for a specific project. ![License Configuration2](docs/Administration_General_Settings_License_Check_2.png) ### Configuration via License Menu Administration -> Configuration(dropdown) -> License Check ![alternative License Configuration1](docs/1-nice-General%20Settings%20-%20Administration.png) - Under "Licenses" you can allow or disallow licenses globally and add/edit the list of known licenses. ![alternative License Configuration2](docs/2-nice-License%20Check%20-%20Administration.png) ![alternative License Configuration3](docs/3-nice-License%20Check%20-%20Administration.png) - Under "Project Licenses" you can allow and disallow licenses for a specific project. ![alternative License Configuration4](docs/4-nice-License%20Check%20-%20Administration.png) ![alternative License Configuration5](docs/5-nice-License%20Check%20-%20Administration.png) - Under "Dependency Mapping" you can map a dependency name/key (with regex) to a license, e.g. `^asm:asm$` to "BSD-3-Clause" ![alternative License Configuration6](docs/6-nice-License%20Check%20-%20Administration.png) ![alternative License Configuration7](docs/7-nice-License%20Check%20-%20Administration.png) - Under "License Mappings" you can map a license name (with regex) to a license, e.g. `.*Apache.*2.*` to "Apache-2.0". ![alternative License Configuration8](docs/8-nice-License%20Check%20-%20Administration.png) ![alternative License Configuration9](docs/9-nice-License%20Check%20-%20Administration.png) ### Activation rules in Quality Profile You have to activate the new rules in a (new) quality profile, for each supported language (Groovy, Kotlin, Java, JavaScript, TypeScript) And you have to use this profile for your project. 1. Step 1 ![activate 1](docs/profile/activate_profile1.png) 2. Step 2 ![activate 2](docs/profile/activate_profile2.png) 3. Step 3 ![activate 3](docs/profile/activate_profile3.png) 4. Step 4 ![activate 4](docs/profile/activate_profile4.png) 5. Step 5 ![activate 5](docs/profile/activate_profile5.png) 6. Step 6 ![activate 6](docs/profile/activate_profile6.png) 7. Step 7 ![activate 7](docs/profile/activate_profile7.png) ## Execution When a project is analyzed using the `mvn sonar:sonar` in command line the extension is started automatically. Please make sure to have all dependencies installed before launching the SonarQube analysis. So your complete build should look something like this: mvn -B org.jacoco:jacoco-maven-plugin:prepare-agent install org.jacoco:jacoco-maven-plugin:report mvn -B sonar:sonar ## Supported Languages Groovy, Kotlin, Java, JavaScript, TypeScript ## Supported Project Types ### Maven + NPM When using Maven and a Javascript Package Manager, define the `sonar.sources` property to point to the files which contain dependency information. ```xml ... pom.xml,package.json ... ``` ### Maven Maven works if your project/module has a `pom.xml` on its root level (running with Maven, Gradle or SonarScanner). ### NPM NPM works if your project/module has a `package.json` on its root level (running with Maven, Gradle or SonarScanner). ### Gradle Gradle project should use JK1 plugin https://github.com/jk1/Gradle-License-Report Note: Please check above link for instructions or follow as mentioned below **Step1:** Update `build.gradle` file with following code for using JK1 plugin import com.github.jk1.license.filter.LicenseBundleNormalizer import com.github.jk1.license.render.JsonReportRenderer plugins { id 'com.github.jk1.dependency-license-report' version '1.13' } licenseReport { allowedLicensesFile = new File("$projectDir/src/main/resources/licenses/allowed-licenses.json") renderers = new JsonReportRenderer('license-details.json', false) filters = [new LicenseBundleNormalizer()] } **Step 2:** Update `build.gradle` file with following code for using SonarQube plugin plugins { id 'org.sonarqube' version "3.0" } jar { enabled = true } sonarqube { properties { property "sonar.host.url", "http://localhost:9000" } } **Step 3:** run following command to generate your report `license-details.json` in `build/reports/dependency-license` > gradle generateLicenseReport **Step 4:** run following command for SonarQube > gradle sonarqube ### Configuration via Sonar API You can also use the [Sonar API](https://docs.sonarqube.org/latest/extend/web-api/) to configure the plugin. #### Plugin Activation - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.activation" ``` - Enable ``` curl -X POST -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/set?key=licensecheck.activation&value=true" ``` - Disable ``` curl -X POST -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/set?key=licensecheck.activation&value=false" ``` #### Global License Settings - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.license-set" ``` #### Project License Settings - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.project-license-set" ``` #### License Mapping - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.license-mapping" ``` #### Dependency Mapping - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.dep-mapping" ``` #### NPM Transitive setting - Get the setting ``` curl -X GET -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/values?keys=licensecheck.npm.resolvetransitive" ``` - Enable ``` curl -X POST -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/set?key=licensecheck.npm.resolvetransitive&value=true" ``` - Disable ``` curl -X POST -v -u USERNAME:PASSWORD "http://localhost:9000/api/settings/set?key=licensecheck.npm.resolvetransitive&value=false" ``` ================================================ FILE: docker-compose.yml ================================================ version: "3" services: sonarqube: image: sonarqube:25.3.0.104237-community depends_on: - db environment: SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar SONAR_JDBC_USERNAME: sonar SONAR_JDBC_PASSWORD: sonar volumes: - sonarqube_data:/opt/sonarqube/data - ./target/sonarqube-licensecheck-plugin-7.0.0-SNAPSHOT.jar:/opt/sonarqube/extensions/plugins/sonarqube-licensecheck-plugin-7.0.0-SNAPSHOT.jar - sonarqube_logs:/opt/sonarqube/logs ports: - "9100:9000" db: image: postgres:17-alpine environment: POSTGRES_USER: sonar POSTGRES_PASSWORD: sonar volumes: - postgresql:/var/lib/postgresql - postgresql_data:/var/lib/postgresql/data volumes: sonarqube_data: sonarqube_logs: postgresql: postgresql_data: ================================================ FILE: jsconfig.json ================================================ { "include": ["./src/main/web/**/*"] } ================================================ FILE: mise.toml ================================================ [tools] java = "temurin-21" maven = "3" ================================================ FILE: package.json ================================================ { "name": "sonarqube-licensecheck", "version": "7.0.0", "description": "License Check Plugin for SonarQube", "private": true, "scripts": { "build": "webpack --mode production", "dev": "webpack --mode development --watch", "test-server": "node test-server.js", "generate-icons": "vsvg -s ./svg-icons -t ./src/compiled-icons", "prettier": "prettier --write '**/*.{js,jsx,java,html,css,json,md,yml,yaml}'", "prettier-check": "prettier --check '**/*.{js,jsx,java,html,css,json,md,yml,yaml}'" }, "author": "Christian Köberl", "license": "Apache-2.0", "dependencies": { "@emotion/react": "^11.14.0", "@sonarsource/echoes-react": "^1.12.0", "file-saverjs": "^1.3.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-intl": "^6.8.9", "react-router-dom": "^6.30.1" }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.27.1", "@types/react": "^19.2.14", "babel-loader": "^9.2.1", "buble": "0.20.0", "buble-loader": "0.5.1", "concurrently": "9.2.1", "css-loader": "^7.1.4", "http-proxy": "1.18.1", "prettier": "3.8.1", "prettier-plugin-java": "2.8.1", "style-loader": "^4.0.0", "webpack": "^5.99.9", "webpack-cli": "^6.0.1" }, "packageManager": "pnpm@10.29.3" } ================================================ FILE: pom.xml ================================================ 4.0.0 at.porscheinformatik.sonarqube.licensecheck sonarqube-licensecheck-plugin 7.0.0-SNAPSHOT SonarQube License Check Plugin sonar-plugin Checks Maven and NPM dependencies against a defined set of allowed licenses and reports any unknown or not allowed licenses as an issue. https://github.com/porscheinformatik/sonarqube-licensecheck Porsche Informatik https://www.porscheinformatik.at derkoe Christian Köberl Stephanie Kaschnitz github https://github.com/porscheinformatik/sonarqube-licensecheck/issues Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 UTF-8 src/main,pom.xml,package.json https://github.com/porscheinformatik/sonarqube-licensecheck/blob/master/LICENSE 11.0.0.2664 25.1.0.102122 17 org.glassfish javax.json 1.1.4 junit junit 4.13.2 test org.hamcrest hamcrest 3.0 test org.hamcrest hamcrest-library 3.0 test org.mockito mockito-core 5.17.0 test org.apache.commons commons-lang3 3.18.0 test org.sonarsource.api.plugin sonar-plugin-api ${sonar.apiVersion} provided org.sonarsource.sonarqube sonar-plugin-api-impl ${sonar.apiImplVersion} test xpp3 xpp3 1.1.4c org.apache.maven maven-model 3.9.9 org.apache.maven.shared maven-invoker 3.3.0 scm:git:https://github.com/porscheinformatik/sonarqube-licensecheck.git https://github.com/porscheinformatik/sonarqube-licensecheck.git https://github.com/porscheinformatik/sonarqube-licensecheck HEAD org.apache.maven.plugins maven-enforcer-plugin 3.5.0 enforce-maven enforce 3.7 org.sonarsource.sonar-packaging-maven-plugin sonar-packaging-maven-plugin 1.23.0.740 true licensecheck at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPlugin org.apache.maven.plugins maven-compiler-plugin 3.14.0 ${java.version} ${java.version} org.apache.maven.plugins maven-release-plugin 3.1.1 maven-surefire-plugin 3.5.3 ${maven.home} org.jacoco jacoco-maven-plugin 0.8.13 prepare-agent prepare-agent report report com.github.eirslett frontend-maven-plugin 1.15.1 install-node-and-corepack install-node-and-corepack generate-resources pnpm install corepack pnpm install pnpm run prettier-check corepack pnpm run prettier-check test pnpm run build corepack pnpm run build v24.13.1 maven-resources-plugin 3.3.1 copy-resources install copy-resources ${project.basedir}/docker/plugins ${project.build.directory} ${project.artifactId}-${project.version}.jar ================================================ FILE: renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:base", "group:allNonMajor"], "labels": ["dependencies"], "packageRules": [ { "description": "org.sonarsource.sonarqube packages", "packagePatterns": ["^org.sonarsource.sonarqube:"], "groupName": "org.sonarsource.sonarqube", "enabled": false }, { "groupName": "Docker", "matchDatasources": ["docker"] }, { "groupName": "Maven non-major dependencies", "matchDatasources": ["maven"], "matchUpdateTypes": ["minor", "patch"] }, { "groupName": "NPM non-major dependencies", "matchDatasources": ["npm"], "matchUpdateTypes": ["minor", "patch"] } ] } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/Dependency.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.LANG_JAVA; import java.io.StringWriter; import java.util.Collection; import java.util.Objects; import java.util.TreeSet; import javax.json.Json; import javax.json.stream.JsonGenerator; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.TextRange; public class Dependency implements Comparable { private String name; private String version; private String license; private String lang; private Status status; private String pomPath; private InputComponent inputComponent; private TextRange textRange; public Dependency(String name, String version, String license, String lang) { super(); this.name = name; this.version = version; this.license = license; this.lang = lang; } public Dependency(String name, String version, String license) { this(name, version, license, LANG_JAVA); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getLicense() { return license; } public void setLicense(String license) { this.license = license; } public String getLang() { return lang; } public void setLang(String lang) { this.lang = lang; } public void setStatus(final Status status) { this.status = status; } public Status getStatus() { return status; } public String getPomPath() { return pomPath; } public void setPomPath(String pomPath) { this.pomPath = pomPath; } public InputComponent getInputComponent() { return inputComponent; } public void setInputComponent(InputComponent inputComponent) { this.inputComponent = inputComponent; } public TextRange getTextRange() { return textRange; } public void setTextRange(TextRange textRange) { this.textRange = textRange; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Dependency that = (Dependency) o; return ( Objects.equals(name, that.name) && Objects.equals(version, that.version) && Objects.equals(license, that.license) ); } @Override public int hashCode() { return Objects.hash(name, version, license); } @Override public String toString() { return "{name:" + name + ", version:" + version + ", license:" + license + "}"; } @Override public int compareTo(Dependency o) { if ((o == null) || (o.name == null)) { return 1; } else if (this.name == null) { return -1; } return this.name.compareTo(o.name); } public static String createString(Collection dependencies) { TreeSet sortedDependencies = new TreeSet<>(dependencies); StringWriter jsonString = new StringWriter(); JsonGenerator generator = Json.createGenerator(jsonString); generator.writeStartArray(); for (Dependency dependency : sortedDependencies) { String license = dependency.getLicense(); generator.writeStartObject(); generator.write("name", dependency.getName()); generator.write("version", dependency.getVersion()); generator.write("license", license != null ? license : " "); generator.write("lang", dependency.getLang()); generator.writeEnd(); } generator.writeEnd(); generator.close(); return jsonString.toString(); } public enum Status { Allowed, Forbidden, Unknown, } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetrics.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import java.util.Arrays; import java.util.List; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metrics; public class LicenseCheckMetrics implements Metrics { public static final String LICENSE_CHECK_DEPENDENCY_KEY = "licensecheck.dependency"; private static final String LICENSE_CHECK_LICENSE_KEY = "licensecheck.license"; public static final String DEPENDENCIES = "Dependencies"; public static final Metric DEPENDENCY = new Metric.Builder( LICENSE_CHECK_DEPENDENCY_KEY, "License Check - Dependencies", Metric.ValueType.DATA ) .setDescription("Used Dependencies") .setDirection(Metric.DIRECTION_WORST) .setDomain(CoreMetrics.DOMAIN_GENERAL) .create(); public static final Metric LICENSE = new Metric.Builder( LICENSE_CHECK_LICENSE_KEY, "License Check - Licenses", Metric.ValueType.DATA ) .setDescription("Used Libraries") .setDirection(Metric.DIRECTION_WORST) .setDomain(CoreMetrics.DOMAIN_GENERAL) .create(); public static final Metric NO_LICENSES = new Metric.Builder( "licensecheck.no_licenses", "Number of Licenses", Metric.ValueType.INT ) .setDescription("Number of licences of all used dependencies") .setDomain(DEPENDENCIES) .create(); public static final Metric NO_LICENSES_FORBIDDEN = new Metric.Builder( "licensecheck.no_licenses_forbidden", "Number of Forbidden Licenses", Metric.ValueType.INT ) .setDescription("Number of forbidden licences (of all used dependencies)") .setDomain(DEPENDENCIES) .create(); public static final Metric NO_DEPENDENCIES = new Metric.Builder( "licensecheck.no_dependencies", "Number of Dependencies", Metric.ValueType.INT ) .setDescription("Number of used dependencies") .setDomain(DEPENDENCIES) .create(); public static final Metric NO_DEPENDENCIES_WITH_FORBIDDEN_LICENSE = new Metric.Builder( "licensecheck.no_dependencies_forbidden", "Dependencies with Forbidden License", Metric.ValueType.INT ) .setDescription("Number of used dependencies with forbidden licenses") .setDomain(DEPENDENCIES) .create(); public static final Metric NO_DEPENDENCIES_WITH_UNKNOWN_LICENSE = new Metric.Builder( "licensecheck.no_dependencies_unknown", "Dependencies with Unknown License", Metric.ValueType.INT ) .setDescription("Number of used dependencies with unknown licenses") .setDomain(DEPENDENCIES) .create(); @Override public List getMetrics() { return Arrays.asList( DEPENDENCY, LICENSE, NO_DEPENDENCIES, NO_LICENSES, NO_LICENSES_FORBIDDEN, NO_DEPENDENCIES_WITH_FORBIDDEN_LICENSE, NO_DEPENDENCIES_WITH_UNKNOWN_LICENSE ); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinition.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import org.sonar.api.web.page.Context; import org.sonar.api.web.page.Page; import org.sonar.api.web.page.PageDefinition; public class LicenseCheckPageDefinition implements PageDefinition { @Override public void define(Context context) { context.addPage( Page.builder("licensecheck/configuration") .setName("License Check") .setAdmin(true) .build() ); context.addPage( Page.builder("licensecheck/dashboard") .setName("License Check") .setScope(Page.Scope.COMPONENT) .setComponentQualifiers(Page.Qualifier.PROJECT) .build() ); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingService; import at.porscheinformatik.sonarqube.licensecheck.license.License; import at.porscheinformatik.sonarqube.licensecheck.license.LicenseService; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense; import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService; import java.util.Arrays; import java.util.List; import org.sonar.api.Plugin; import org.sonar.api.PropertyType; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.config.PropertyFieldDefinition; public class LicenseCheckPlugin implements Plugin { private static final String LICENSE_ID_DESCRIPTION = "The identifier of the license (e.g. GPL-3.0)"; public static final String LICENSE_IDENTIFIER = "License Identifier"; @Override public void define(Context context) { context.addExtensions(getExtensions()); } private List getExtensions() { return Arrays.asList( ValidateLicenses.class, LicenseCheckSensor.class, LicenseCheckMetrics.class, LicenseCheckPageDefinition.class, LicenseCheckRulesDefinition.class, LicenseService.class, DependencyMappingService.class, LicenseMappingService.class, ProjectLicenseService.class, PropertyDefinition.builder(LicenseCheckPropertyKeys.LICENSE_SET) .category(LicenseCheckPropertyKeys.CATEGORY) .type(PropertyType.PROPERTY_SET) .name("Licenses") .description("List of known licenses with allowed status") .fields( PropertyFieldDefinition.build(License.FIELD_ID) .name("Identifier") .description(LICENSE_ID_DESCRIPTION) .type(PropertyType.STRING) .build(), PropertyFieldDefinition.build(License.FIELD_NAME) .name("Name") .description( "The name of the license (e.g. GNU General Public License v3.0 only)." ) .type(PropertyType.STRING) .build(), PropertyFieldDefinition.build(License.FIELD_ALLOWED) .name("Allowed") .description("If the license is allowed to use") .type(PropertyType.BOOLEAN) .build() ) .index(3) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.DEPENDENCY_MAPPING) .category(LicenseCheckPropertyKeys.CATEGORY) .type(PropertyType.PROPERTY_SET) .name("Dependency Mapping") .description("Maps a dependency name/key (with regex) to a license") .fields( PropertyFieldDefinition.build(DependencyMapping.FIELD_KEY) .name("Dependency") .description("A regular expression to match against the dependency key.") .type(PropertyType.REGULAR_EXPRESSION) .build(), PropertyFieldDefinition.build(DependencyMapping.FIELD_LICENSE) .name(LICENSE_IDENTIFIER) .description(LICENSE_ID_DESCRIPTION) .type(PropertyType.STRING) .build(), PropertyFieldDefinition.build(DependencyMapping.FIELD_OVERWRITE) .name("Overwrite License") .description("Overwrite the license defined by the dependency.") .type(PropertyType.BOOLEAN) .build() ) .index(5) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.LICENSE_MAPPING) .category(LicenseCheckPropertyKeys.CATEGORY) .type(PropertyType.PROPERTY_SET) .name("License Mapping") .description("Maps a license name (with regex) to a license") .fields( PropertyFieldDefinition.build(LicenseMapping.FIELD_REGEX) .name("License name") .description("A regular expression to match against the license name.") .type(PropertyType.REGULAR_EXPRESSION) .build(), PropertyFieldDefinition.build(LicenseMapping.FIELD_LICENSE) .name(LICENSE_IDENTIFIER) .description(LICENSE_ID_DESCRIPTION) .type(PropertyType.STRING) .build() ) .index(4) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.PROJECT_LICENSE_SET) .category(LicenseCheckPropertyKeys.CATEGORY) .type(PropertyType.PROPERTY_SET) .name("Project Licenses") .description("Allow/disallow licenses for specific projects.") .fields( PropertyFieldDefinition.build(ProjectLicense.FIELD_PROJECT_KEY) .name("Project key") .description("The project key") .type(PropertyType.REGULAR_EXPRESSION) .build(), PropertyFieldDefinition.build(ProjectLicense.FIELD_LICENSE) .name(LICENSE_IDENTIFIER) .description(LICENSE_ID_DESCRIPTION) .type(PropertyType.STRING) .build(), PropertyFieldDefinition.build(ProjectLicense.FIELD_ALLOWED) .name("Allowed") .description("If the license is allowed to use") .type(PropertyType.BOOLEAN) .build() ) .index(6) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS) .category(LicenseCheckPropertyKeys.CATEGORY) .name("NPM Transitive Dependencies") .description("Scan transitive dependencies for NPM packages") .type(PropertyType.BOOLEAN) .defaultValue("false") .index(2) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.ACTIVATION_KEY) .category(LicenseCheckPropertyKeys.CATEGORY) .name("Activate") .description("Activate license check") .type(PropertyType.BOOLEAN) .defaultValue("true") .index(1) .build(), PropertyDefinition.builder(LicenseCheckPropertyKeys.GRADLE_JSON_REPORT_PATH) .category(LicenseCheckPropertyKeys.CATEGORY) .name("Gradle report path") .description("Path to search for the license report in gradle scanner") .type(PropertyType.STRING) .index(7) .build() ); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; public class LicenseCheckPropertyKeys { /** * Category for all settings. */ public static final String CATEGORY = "License Check"; /** * Config key for activating/deactivating the plugin */ public static final String ACTIVATION_KEY = "licensecheck.activation"; /** * Config key for the list of licenses */ public static final String LICENSE_SET = "licensecheck.license-set"; /** * Config key for the list of project specific allowed/disallowed licenses */ public static final String PROJECT_LICENSE_SET = "licensecheck.project-license-set"; /** * Config key the Maven license name mapping */ public static final String LICENSE_MAPPING = "licensecheck.license-mapping"; /** * Config key the Maven dependency to license mapping */ public static final String DEPENDENCY_MAPPING = "licensecheck.dep-mapping"; /** * Config key to enable/disable transitive dependencies for NPM */ public static final String NPM_RESOLVE_TRANSITIVE_DEPS = "licensecheck.npm.resolvetransitive"; /** * Config key to configure the path to look for the license report in the gradle scanner. */ public static final String GRADLE_JSON_REPORT_PATH = "licenseCheck.gradle-json-report-path"; private LicenseCheckPropertyKeys() {} } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RulesDefinition; /** * Repository for the rules used in the plugin */ public final class LicenseCheckRulesDefinition implements RulesDefinition { public static final String LANG_JAVA = "java"; public static final String LANG_JS = "js"; public static final String LANG_TS = "ts"; public static final String LANG_GROOVY = "grvy"; public static final String LANG_KOTLIN = "kotlin"; public static final String LANG_SCALA = "scala"; 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_TS = "licensecheck-ts"; 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_SCALA = "licensecheck-scala"; public static final String RULE_UNLISTED_KEY = "licensecheck.unlisted"; public static final String RULE_NOT_ALLOWED_LICENSE_KEY = "licensecheck.notallowedlicense"; @Override public void define(Context context) { NewRepository[] repos = new NewRepository[] { context.createRepository(RULE_REPO_KEY, LANG_JAVA), context.createRepository(RULE_REPO_KEY_JS, LANG_JS), context.createRepository(RULE_REPO_KEY_TS, LANG_TS), context.createRepository(RULE_REPO_KEY_GROOVY, LANG_GROOVY), context.createRepository(RULE_REPO_KEY_KOTLIN, LANG_KOTLIN), context.createRepository(RULE_REPO_KEY_SCALA, LANG_SCALA), }; for (NewRepository repo : repos) { repo.setName("License Check"); repo .createRule(RULE_UNLISTED_KEY) .setName("Dependency has unknown license [license-check]") .setHtmlDescription("The dependencies license could not be determined!") .setSeverity(Severity.BLOCKER); repo .createRule(RULE_NOT_ALLOWED_LICENSE_KEY) .setName("License is not allowed [license-check]") .setHtmlDescription( "Violation because the license of the dependency is not allowed." ) .setSeverity(Severity.BLOCKER); repo.done(); } } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; 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_KOTLIN; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY_TS; import static java.util.function.Predicate.not; import at.porscheinformatik.sonarqube.licensecheck.gradle.GradleDependencyScanner; import at.porscheinformatik.sonarqube.licensecheck.license.License; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import at.porscheinformatik.sonarqube.licensecheck.maven.MavenDependencyScanner; import at.porscheinformatik.sonarqube.licensecheck.npm.PackageJsonDependencyScanner; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.scanner.fs.InputProject; public class LicenseCheckSensor implements Sensor { private static final Logger LOGGER = LoggerFactory.getLogger(LicenseCheckSensor.class); private static final Set AGGREGATED_LICENSES = ConcurrentHashMap.newKeySet(); private static final Set AGGREGATED_DEPENDENCIES = ConcurrentHashMap.newKeySet(); private final Configuration configuration; private final ValidateLicenses validateLicenses; private final Scanner[] scanners; public LicenseCheckSensor( Configuration configuration, ValidateLicenses validateLicenses, LicenseMappingService licenseMappingService ) { this.configuration = configuration; this.validateLicenses = validateLicenses; this.scanners = new Scanner[] { new PackageJsonDependencyScanner( licenseMappingService, configuration .getBoolean(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS) .orElse(false) ), new MavenDependencyScanner(licenseMappingService), new GradleDependencyScanner(licenseMappingService), }; } private static void saveDependencies( SensorContext sensorContext, Set dependencies ) { LOGGER.debug( "Saving dependencies for module {}: {}", sensorContext.project(), dependencies ); if (!dependencies.isEmpty()) { sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.DEPENDENCY) .withValue(Dependency.createString(dependencies)) .on(sensorContext.project()) .save(); } } private static void saveLicenses(SensorContext sensorContext, Set licenses) { LOGGER.debug("Saving licenses for project {}: {}", sensorContext.project(), licenses); if (!licenses.isEmpty()) { sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.LICENSE) .withValue(License.createJsonString(licenses)) .on(sensorContext.project()) .save(); } } private static void saveMeasures( SensorContext sensorContext, Set licenses, Set dependencies ) { sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.NO_LICENSES) .withValue(licenses.size()) .on(sensorContext.project()) .save(); sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.NO_LICENSES_FORBIDDEN) .withValue((int) licenses.stream().filter(not(License::getAllowed)).count()) .on(sensorContext.project()) .save(); sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.NO_DEPENDENCIES) .withValue(dependencies.size()) .on(sensorContext.project()) .save(); sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.NO_DEPENDENCIES_WITH_FORBIDDEN_LICENSE) .withValue( (int) dependencies .stream() .filter(d -> Dependency.Status.Forbidden.equals(d.getStatus())) .count() ) .on(sensorContext.project()) .save(); sensorContext .newMeasure() .forMetric(LicenseCheckMetrics.NO_DEPENDENCIES_WITH_UNKNOWN_LICENSE) .withValue( (int) dependencies .stream() .filter(d -> Dependency.Status.Unknown.equals(d.getStatus())) .count() ) .on(sensorContext.project()) .save(); } @Override public void describe(SensorDescriptor descriptor) { descriptor .name("License Check") .createIssuesForRuleRepositories( RULE_REPO_KEY, RULE_REPO_KEY_JS, RULE_REPO_KEY_TS, RULE_REPO_KEY_GROOVY, RULE_REPO_KEY_KOTLIN ); } @Override public void execute(SensorContext context) { if (!configuration.getBoolean(LicenseCheckPropertyKeys.ACTIVATION_KEY).orElse(true)) { LOGGER.info("Scanner is set to inactive. No scan possible."); return; } Set dependencies = new TreeSet<>(); for (Scanner scanner : scanners) { dependencies.addAll(scanner.scan(context)); } InputProject project = context.project(); Set validatedDependencies = validateLicenses.validateLicenses( dependencies, context ); Set usedLicenses = validateLicenses.getUsedLicenses( validatedDependencies, project ); AGGREGATED_LICENSES.addAll(usedLicenses); AGGREGATED_DEPENDENCIES.addAll(validatedDependencies); // root module? if (context.project().key().equals(context.module().key())) { saveDependencies(context, AGGREGATED_DEPENDENCIES); saveLicenses(context, AGGREGATED_LICENSES); saveMeasures(context, AGGREGATED_LICENSES, AGGREGATED_DEPENDENCIES); } } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/Scanner.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import java.util.Set; import org.sonar.api.batch.sensor.SensorContext; public interface Scanner { Set scan(SensorContext context); } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingService; import at.porscheinformatik.sonarqube.licensecheck.license.License; import at.porscheinformatik.sonarqube.licensecheck.license.LicenseService; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; import org.codehaus.plexus.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.batch.sensor.issue.NewIssueLocation; import org.sonar.api.rule.RuleKey; import org.sonar.api.scanner.ScannerSide; import org.sonar.api.scanner.fs.InputProject; @ScannerSide public class ValidateLicenses { private static final Logger LOGGER = LoggerFactory.getLogger(ValidateLicenses.class); private final LicenseService licenseService; private final DependencyMappingService dependencyMappingService; public ValidateLicenses( LicenseService licenseService, DependencyMappingService dependencyMappingService ) { super(); this.licenseService = licenseService; this.dependencyMappingService = dependencyMappingService; } public Set validateLicenses(Set dependencies, SensorContext context) { List licenses = licenseService.getLicenses(context.project()); List dependencyMappings = dependencyMappingService.getDependencyMappings(); for (Dependency dependency : dependencies) { License license = findLicense(licenses, dependency); if (license == null) { mapDependencyToLicense(dependencyMappings, dependency); } overrideLicenseForDependency(dependencyMappings, dependency); if (!isLicensesValid(context, licenses, dependency)) { dependency.setStatus(Dependency.Status.Unknown); } else { dependency.setStatus(Dependency.Status.Allowed); } } return dependencies; } public Set getUsedLicenses(Set dependencies, InputProject project) { List licenses = licenseService.getLicenses(project); Set usedLicenseList = new TreeSet<>(); for (Dependency dependency : dependencies) { for (License license : licenses) { if (license.getIdentifier().equals(dependency.getLicense())) { usedLicenseList.add(license); } } } return usedLicenseList; } private License findLicense(List licenses, Dependency dependency) { for (License license : licenses) { if (license.getIdentifier().equals(dependency.getLicense())) { return license; } } return null; } private static void mapDependencyToLicense( List dependencyMappings, Dependency dependency ) { dependencyMappings.forEach(dependencyMapping -> { if (StringUtils.isBlank(dependency.getLicense())) { String matchString = dependencyMapping.getKey(); if (dependency.getName().matches(matchString)) { dependency.setLicense(dependencyMapping.getLicense()); } } }); } private static void overrideLicenseForDependency( List dependencyMappings, Dependency dependency ) { dependencyMappings .stream() .filter(DependencyMapping::getOverwrite) .forEach(dependencyMapping -> { String matchString = dependencyMapping.getKey(); if (dependency.getName().matches(matchString)) { dependency.setLicense(dependencyMapping.getLicense()); } }); } private static boolean isLicensesValid( SensorContext context, List licenses, Dependency dependency ) { if (StringUtils.isBlank(dependency.getLicense())) { licenseNotFoundIssue(context, dependency); return false; } if (checkSpdxLicense(dependency.getLicense(), licenses)) { return true; } List licensesContainingDependency = licenses .stream() .filter(l -> dependency.getLicense().contains(l.getIdentifier())) .collect(Collectors.toList()); String[] andLicenses = dependency .getLicense() .replace("(", "") .replace(")", "") .split(" AND "); if (licensesContainingDependency.size() != andLicenses.length) { licenseNotFoundIssue(context, dependency); } else { StringBuilder notAllowedLicense = new StringBuilder(); for (License element : licensesContainingDependency) { if (!element.getAllowed()) { notAllowedLicense.append(element.getName()).append(" "); } } licenseNotAllowedIssue(context, dependency, notAllowedLicense.toString()); } return false; } private static boolean checkSpdxLicense(String spdxLicenseString, List licenses) { if (spdxLicenseString.contains(" OR ")) { return checkSpdxLicenseWithOr(spdxLicenseString, licenses); } else if (spdxLicenseString.contains(" AND ")) { return checkSpdxLicenseWithAnd(spdxLicenseString, licenses); } return licenses .stream() .filter(l -> l.getIdentifier().equals(spdxLicenseString)) .anyMatch(License::getAllowed); } private static boolean checkSpdxLicenseWithOr( String spdxLicenseString, List licenses ) { String[] orLicenses = spdxLicenseString.replace("(", "").replace(")", "").split(" OR "); return licenses .stream() .filter(l -> ValidateLicenses.contains(orLicenses, l.getIdentifier())) .anyMatch(License::getAllowed); } private static boolean checkSpdxLicenseWithAnd( String spdxLicenseString, List licenses ) { String[] andLicenses = spdxLicenseString.replace("(", "").replace(")", "").split(" AND "); long count = andLicenses.length; List foundLicenses = licenses .stream() .filter(l -> ValidateLicenses.contains(andLicenses, l.getIdentifier())) .collect(Collectors.toList()); long allowedLicenseCount = foundLicenses.stream().filter(License::getAllowed).count(); return count == allowedLicenseCount; } private static void licenseNotAllowedIssue( SensorContext context, Dependency dependency, String notAllowedLicense ) { LOGGER.info( "Dependency " + dependency.getName() + " uses a not allowed license " + notAllowedLicense ); dependency.setStatus(Dependency.Status.Forbidden); createIssue( context, dependency, LicenseCheckRulesDefinition.RULE_NOT_ALLOWED_LICENSE_KEY, "Dependency " + dependency.getName() + " uses a not allowed license " + dependency.getLicense() ); } private static void licenseNotFoundIssue(SensorContext context, Dependency dependency) { String message = String.format( "No License found for Dependency %s (license from source: %s)", dependency.getName(), dependency.getLicense() ); LOGGER.info(message); createIssue(context, dependency, LicenseCheckRulesDefinition.RULE_UNLISTED_KEY, message); } private static void createIssue( SensorContext context, Dependency dependency, String rule, String message ) { NewIssue issue = context.newIssue().forRule(RuleKey.of(getRepoKey(dependency), rule)); NewIssueLocation location = issue.newLocation().on(dependency.getInputComponent()); if (dependency.getTextRange() != null) { location = location.at(dependency.getTextRange()); } issue.at(location.message(message)).save(); } private static String getRepoKey(Dependency dependency) { switch (dependency.getLang()) { case LicenseCheckRulesDefinition.LANG_JS: return LicenseCheckRulesDefinition.RULE_REPO_KEY_JS; case LicenseCheckRulesDefinition.LANG_TS: return LicenseCheckRulesDefinition.RULE_REPO_KEY_TS; case LicenseCheckRulesDefinition.LANG_GROOVY: return LicenseCheckRulesDefinition.RULE_REPO_KEY_GROOVY; case LicenseCheckRulesDefinition.LANG_SCALA: return LicenseCheckRulesDefinition.RULE_REPO_KEY_SCALA; case LicenseCheckRulesDefinition.LANG_KOTLIN: return LicenseCheckRulesDefinition.RULE_REPO_KEY_KOTLIN; case LicenseCheckRulesDefinition.LANG_JAVA: default: return LicenseCheckRulesDefinition.RULE_REPO_KEY; } } private static boolean contains(String[] items, String valueToFind) { for (String item : items) { if (item != null && item.equals(valueToFind)) { return true; } } return false; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMapping.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.dependencymapping; import java.util.Objects; public class DependencyMapping implements Comparable { public static final String FIELD_KEY = "key"; public static final String FIELD_LICENSE = "license"; public static final String FIELD_OVERWRITE = "overwrite"; private String key; private String license; private Boolean overwrite; public DependencyMapping(String key, String license, Boolean overwrite) { super(); this.key = key; this.license = license; this.overwrite = overwrite; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getLicense() { return license; } public void setLicense(String license) { this.license = license; } public Boolean getOverwrite() { return overwrite; } public void setOverwrite(Boolean overwrite) { this.overwrite = overwrite; } @Override public String toString() { return "{key:" + key + ", license:" + license + "}"; } @Override public int compareTo(DependencyMapping o) { if (o == null) { return 1; } if (this.license.compareTo(o.license) == 0) { return this.key.compareTo(o.key); } else { return this.license.compareTo(o.license); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DependencyMapping that = (DependencyMapping) o; return Objects.equals(key, that.key) && Objects.equals(license, that.license); } @Override public int hashCode() { return Objects.hash(key, license); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.dependencymapping; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.DEPENDENCY_MAPPING; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_KEY; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_OVERWRITE; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.sonar.api.config.Configuration; import org.sonar.api.scanner.ScannerSide; import org.sonar.api.server.ServerSide; @ServerSide @ScannerSide public class DependencyMappingService { private final Configuration configuration; public DependencyMappingService(Configuration configuration) { super(); this.configuration = configuration; } public List getDependencyMappings() { return Arrays.stream(configuration.getStringArray(DEPENDENCY_MAPPING)) .map(idx -> { String idxProp = "." + idx + "."; String key = configuration .get(DEPENDENCY_MAPPING + idxProp + FIELD_KEY) .orElse(null); String license = configuration .get(DEPENDENCY_MAPPING + idxProp + FIELD_LICENSE) .orElse(null); Boolean overwrite = configuration .getBoolean(DEPENDENCY_MAPPING + idxProp + FIELD_OVERWRITE) .orElse(Boolean.TRUE); return new DependencyMapping(key, license, overwrite); }) .collect(Collectors.toList()); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.gradle; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.GRADLE_JSON_REPORT_PATH; 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 java.io.File; import java.io.FileInputStream; import java.io.IOException; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.sensor.SensorContext; public class GradleDependencyScanner implements Scanner { public static final String JSON_REPORT_PATH_DEFAULT = "build" + File.separator + "reports" + File.separator + "dependency-license" + File.separator + "license-details.json"; private static final Logger LOGGER = LoggerFactory.getLogger(GradleDependencyScanner.class); private final LicenseMappingService licenseMappingService; public GradleDependencyScanner(LicenseMappingService licenseMappingService) { this.licenseMappingService = licenseMappingService; } @Override public Set scan(SensorContext context) { Map defaultLicenseMap = licenseMappingService.getLicenseMap(); String pathDef = context .config() .get(GRADLE_JSON_REPORT_PATH) .orElse(JSON_REPORT_PATH_DEFAULT); LOGGER.debug("Searching for license file at {}", pathDef); File licenseDetailsJsonFile = context .fileSystem() .baseDir() .toPath() .resolve(pathDef) .toFile() .getAbsoluteFile(); if (!licenseDetailsJsonFile.exists()) { LOGGER.info( "No license-details.json file found in {} - skipping Gradle dependency scan", licenseDetailsJsonFile.getPath() ); return Collections.emptySet(); } return readLicenseDetailsJson(licenseDetailsJsonFile) .stream() .map(d -> matchLicense(defaultLicenseMap, d)) .peek(d -> d.setInputComponent(context.module())) .collect(Collectors.toSet()); } private Set readLicenseDetailsJson(File licenseDetailsJsonFile) { Set dependencySet = new HashSet<>(); try ( InputStream fis = new FileInputStream(licenseDetailsJsonFile); JsonReader jsonReader = Json.createReader(fis); ) { JsonArray arr = jsonReader.readObject().getJsonArray("dependencies"); prepareDependencySet(dependencySet, arr); return dependencySet; } catch (IOException e) { LOGGER.error( "Problems reading Gradle 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(); JsonArray arrModuleUrls = jsonDepObj.getJsonArray("moduleUrls"); String moduleLicense = getModuleLicenseFromJsonObject(jsonDepObj); String moduleLicenseUrl = null; if (arrModuleUrls != null) { moduleLicenseUrl = arrModuleUrls.getString(0, null); } Dependency dep = new Dependency( jsonDepObj.getString("moduleName", null), jsonDepObj.getString("moduleVersion", null), moduleLicense, LicenseCheckRulesDefinition.LANG_JAVA ); dep.setPomPath(moduleLicenseUrl); dependencySet.add(dep); } } private String getModuleLicenseFromJsonObject(JsonObject jsonDepObj) { String moduleLicense = null; JsonArray arrModuleLicenses = jsonDepObj.getJsonArray("moduleLicenses"); if (arrModuleLicenses != null && !arrModuleLicenses.isEmpty()) { moduleLicense = getModuleLicense(arrModuleLicenses); } return moduleLicense; } private String getModuleLicense(JsonArray arrModuleLicenses) { String moduleLicense = null; JsonObject firstJsonObj = arrModuleLicenses.getJsonObject(0); if (firstJsonObj != null) { moduleLicense = firstJsonObj.getString("moduleLicense", null); if (moduleLicense == null) { moduleLicense = firstJsonObj.getString("moduleLicenseUrl", null); } } 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; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/License.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.license; import java.io.StringReader; import java.io.StringWriter; import java.util.*; import javax.json.*; import javax.json.stream.JsonGenerator; import org.codehaus.plexus.util.StringUtils; public class License implements Comparable { public static final String FIELD_NAME = "name"; public static final String FIELD_ID = "id"; public static final String FIELD_ALLOWED = "allowed"; public static final String STATUS = "status"; private String name; private String identifier; private Boolean allowed; public License(String name, String identifier, Boolean allowed) { super(); this.name = name; this.identifier = identifier; this.allowed = Objects.requireNonNull(allowed); } public License(String name, String identifier, String allowed) { this(name, identifier, Boolean.parseBoolean(allowed)); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } public Boolean getAllowed() { return allowed; } public void setAllowed(Boolean allowed) { this.allowed = allowed; } @Override public String toString() { return "{name:" + name + ", identifier:" + identifier + ", status:" + allowed + "}"; } public static List fromString(String serializedLicensesString) { if (serializedLicensesString == null) { return new ArrayList<>(); } if (serializedLicensesString.startsWith("[")) { List licenses = new ArrayList<>(); try ( JsonReader jsonReader = Json.createReader( new StringReader(serializedLicensesString) ); ) { JsonArray licensesJson = jsonReader.readArray(); for (JsonObject licenseJson : licensesJson.getValuesAs(JsonObject.class)) { licenses.add( new License( licenseJson.getString("name"), licenseJson.getString("identifier"), licenseJson.getString(STATUS) ) ); } } return licenses; } else if (serializedLicensesString.startsWith("{")) { return readLegacyJson(serializedLicensesString); } else { return readLegacySeparated(serializedLicensesString); } } /** * @param serializedLicensesString setting string * @return a list with licences * @deprecated remove with later release */ @Deprecated private static List readLegacySeparated(String serializedLicensesString) { List licenses = new ArrayList<>(); if (StringUtils.isNotEmpty(serializedLicensesString)) { String[] parts = serializedLicensesString.split(";"); for (String licenseString : parts) { String[] subParts = licenseString.split("~"); String name = subParts.length > 0 ? subParts[0] : null; String identifier = subParts.length > 1 ? subParts[1] : null; String status = subParts.length > 2 ? subParts[2] : null; licenses.add(new License(name, identifier, status)); } } return licenses; } /** * @param serializedLicensesString setting string * @return a list with licences * @deprecated remove with later release */ @Deprecated private static List readLegacyJson(String serializedLicensesString) { List licenses = new ArrayList<>(); try ( JsonReader jsonReader = Json.createReader(new StringReader(serializedLicensesString)); ) { JsonObject licensesJson = jsonReader.readObject(); for (Map.Entry licenseJson : licensesJson.entrySet()) { JsonObject value = (JsonObject) licenseJson.getValue(); licenses.add( new License( value.getString("name"), licenseJson.getKey(), value.getString(STATUS) ) ); } } return licenses; } public static String createJsonString(Collection licenses) { TreeSet licenseSet = new TreeSet<>(licenses); StringWriter jsonString = new StringWriter(); JsonGenerator generator = Json.createGenerator(jsonString); generator.writeStartArray(); for (License license : licenseSet) { generator.writeStartObject(); generator.write("name", license.getName()); generator.write("identifier", license.getIdentifier()); generator.write(STATUS, license.getAllowed().toString()); generator.writeEnd(); } generator.writeEnd(); generator.close(); return jsonString.toString(); } @Override public int compareTo(License o) { if (o == null) { return 1; } if (this.identifier.compareTo(o.identifier) == 0) { if (this.name.compareTo(o.name) == 0) { return this.allowed.compareTo(o.allowed); } else { return this.name.compareTo(o.name); } } else { return this.identifier.compareTo(o.identifier); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } License license = (License) o; return ( Objects.equals(name, license.name) && Objects.equals(identifier, license.identifier) && Objects.equals(allowed, license.allowed) ); } @Override public int hashCode() { return Objects.hash(name, identifier, allowed); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.license; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_SET; import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ALLOWED; import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ID; import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_NAME; import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense; import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.sonar.api.config.Configuration; import org.sonar.api.scanner.ScannerSide; import org.sonar.api.scanner.fs.InputProject; import org.sonar.api.server.ServerSide; @ServerSide @ScannerSide public class LicenseService { private final Configuration configuration; private final ProjectLicenseService projectLicenseService; public LicenseService( Configuration configuration, ProjectLicenseService projectLicenseService ) { super(); this.configuration = configuration; this.projectLicenseService = projectLicenseService; } public List getLicenses(InputProject module) { List globalLicenses = getLicenses(); if (module == null) { return globalLicenses; } Collection projectLicenses = projectLicenseService.getProjectLicenseList( module.key() ); for (License license : globalLicenses) { for (ProjectLicense projectLicense : projectLicenses) { if (license.getIdentifier().equals(projectLicense.getLicense())) { license.setAllowed(projectLicense.getAllowed()); // override the status of the globalLicenses } } } return globalLicenses; } public List getLicenses() { return Arrays.stream(configuration.getStringArray(LICENSE_SET)) .map(idx -> { String idxProp = "." + idx + "."; String name = configuration.get(LICENSE_SET + idxProp + FIELD_NAME).orElse(null); String identifier = configuration .get(LICENSE_SET + idxProp + FIELD_ID) .orElse(null); Boolean allowed = configuration .getBoolean(LICENSE_SET + idxProp + FIELD_ALLOWED) .orElse(Boolean.FALSE); return new License(name, identifier, allowed.toString()); }) .collect(Collectors.toList()); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMapping.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.licensemapping; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.regex.Pattern; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; import org.codehaus.plexus.util.StringUtils; public class LicenseMapping implements Comparable { public static final String FIELD_LICENSE = "license"; public static final String FIELD_REGEX = "regex"; private final Pattern regex; private final String license; public LicenseMapping(String regex, String license) { super(); this.regex = Pattern.compile(regex); this.license = license; } public Pattern getRegex() { return regex; } public String getLicense() { return license; } @Override public int compareTo(LicenseMapping o) { if (o == null) { return 1; } else if (this.license.compareTo(o.license) == 0) { return this.regex.toString().compareTo(o.regex.toString()); } else { return this.license.compareTo(o.license); } } public static List fromString(String licenseMappingString) { List licensMappings = new ArrayList<>(); if (licenseMappingString != null && licenseMappingString.startsWith("[")) { try ( JsonReader jsonReader = Json.createReader(new StringReader(licenseMappingString)); ) { JsonArray licensesJson = jsonReader.readArray(); for (int i = 0; i < licensesJson.size(); i++) { JsonObject licenseJson = licensesJson.getJsonObject(i); String regex; try { regex = licenseJson.getString(FIELD_REGEX); } catch (NullPointerException e) { regex = licenseJson.getString("licenseNameRegEx", null); } if (regex != null) { licensMappings.add( new LicenseMapping(regex, licenseJson.getString(FIELD_LICENSE)) ); } } } } else if (StringUtils.isNotEmpty(licenseMappingString)) { // deprecated - remove with later release String[] licenseMappingEntries = licenseMappingString.split(";"); for (String licenseMappingEntry : licenseMappingEntries) { String[] licenseMappingEntryParts = licenseMappingEntry.split("~"); licensMappings.add( new LicenseMapping(licenseMappingEntryParts[0], licenseMappingEntryParts[1]) ); } } return licensMappings; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LicenseMapping that = (LicenseMapping) o; return ( Objects.equals(regex.pattern(), that.regex.pattern()) && Objects.equals(license, that.license) ); } @Override public int hashCode() { return Objects.hash(regex.pattern(), license); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.licensemapping; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_MAPPING; import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_REGEX; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.codehaus.plexus.util.StringUtils; import org.sonar.api.config.Configuration; import org.sonar.api.scanner.ScannerSide; import org.sonar.api.server.ServerSide; @ServerSide @ScannerSide public class LicenseMappingService { private final Configuration configuration; /** Holding license map on scanner side */ private static Map LICENSE_MAP; public LicenseMappingService(Configuration configuration) { super(); this.configuration = configuration; } public List getLicenseMappingList() { return Arrays.stream(configuration.getStringArray(LICENSE_MAPPING)) .map(idx -> { String idxProp = "." + idx + "."; String licenseRegex = configuration .get(LICENSE_MAPPING + idxProp + FIELD_REGEX) .orElse(""); String licenseId = configuration .get(LICENSE_MAPPING + idxProp + FIELD_LICENSE) .orElse(null); return new LicenseMapping(licenseRegex, licenseId); }) .collect(Collectors.toList()); } public Map getLicenseMap() { if (LICENSE_MAP != null) { return LICENSE_MAP; } LICENSE_MAP = new HashMap<>(); for (LicenseMapping license : getLicenseMappingList()) { LICENSE_MAP.put(license.getRegex(), license.getLicense()); } return LICENSE_MAP; } public String mapLicense(String licenseName) { if (StringUtils.isBlank(licenseName)) { return licenseName; } Map licenseMap = getLicenseMap(); for (Map.Entry entry : licenseMap.entrySet()) { if (entry.getKey().matcher(licenseName).matches()) { return entry.getValue(); } } return licenseName; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinder.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import at.porscheinformatik.sonarqube.licensecheck.Dependency; import java.io.File; import org.apache.maven.shared.utils.cli.CommandLineUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class DirectoryFinder { private static final Logger LOGGER = LoggerFactory.getLogger(DirectoryFinder.class); private DirectoryFinder() {} public static File getPomPath(Dependency findPathOfDependency, File mavenRepositoryDir) { String[] ids = findPathOfDependency.getName().split(":"); String groupId = ids[0]; String artifactId = ids[1]; String tmp = groupId.replace(".", "/") + "/" + artifactId + "/" + findPathOfDependency.getVersion() + "/" + artifactId + "-" + findPathOfDependency.getVersion() + ".pom"; return new File(mavenRepositoryDir, tmp); } public static File getMavenRepositoryDir(String userSettings, String globalSettings) { if (System.getProperty("maven.repo.local") != null) { return new File(System.getProperty("maven.repo.local")); } String mavenOpts = System.getenv("MAVEN_OPTS"); if (mavenOpts != null) { try { String[] opts = CommandLineUtils.translateCommandline(mavenOpts); for (String opt : opts) { if (opt.startsWith("-Dmaven.repo.local=")) { String repoPath = opt.substring("-Dmaven.repo.local=".length()); return new File(repoPath); } } } catch (Exception e) { LOGGER.warn("Could not parse MAVEN_OPTS: " + mavenOpts, e); // ignore } } File mavenConfFile = new File(System.getProperty("user.home"), ".m2/settings.xml"); if (userSettings != null) { mavenConfFile = new File(userSettings); } if (mavenConfFile.exists() && mavenConfFile.isFile()) { File localRepositoryPath = SettingsXmlParser.parseXmlFile( mavenConfFile ).getLocalRepositoryPath(); if (localRepositoryPath != null) { return localRepositoryPath; } } mavenConfFile = new File(System.getenv("MAVEN_HOME"), "conf/settings.xml"); if (globalSettings != null) { mavenConfFile = new File(globalSettings); } if (mavenConfFile.exists() && mavenConfFile.isFile()) { File localRepositoryPath = SettingsXmlParser.parseXmlFile( mavenConfFile ).getLocalRepositoryPath(); if (localRepositoryPath != null) { return localRepositoryPath; } } return new File(System.getProperty("user.home"), ".m2/repository"); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/LicenseFinder.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import at.porscheinformatik.sonarqube.licensecheck.Dependency; import at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition; import java.io.File; import java.io.FileInputStream; import java.util.Collections; import java.util.List; import org.apache.maven.model.License; import org.apache.maven.model.Model; import org.apache.maven.model.Parent; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class LicenseFinder { private static final Logger LOGGER = LoggerFactory.getLogger(LicenseFinder.class); private LicenseFinder() {} public static List getLicenses( File filePath, String userSettings, String globalSettings ) { try { Model model = new MavenXpp3Reader().read(new FileInputStream(filePath)); if (!model.getLicenses().isEmpty()) { return model.getLicenses(); } else { if (model.getParent() != null) { Parent parent = model.getParent(); Dependency dependency = new Dependency( parent.getGroupId() + ":" + parent.getArtifactId(), parent.getVersion(), null, LicenseCheckRulesDefinition.LANG_JAVA ); return getLicenses( DirectoryFinder.getPomPath( dependency, DirectoryFinder.getMavenRepositoryDir(userSettings, globalSettings) ), userSettings, globalSettings ); } else { return Collections.emptyList(); } } } catch (Exception e) { LOGGER.warn("Could not parse Maven POM " + filePath, e); return Collections.emptyList(); } } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScanner.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; 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 java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Stream; import org.apache.maven.model.License; import org.apache.maven.shared.invoker.DefaultInvocationRequest; import org.apache.maven.shared.invoker.DefaultInvoker; import org.apache.maven.shared.invoker.InvocationRequest; import org.apache.maven.shared.invoker.InvocationResult; import org.apache.maven.shared.invoker.Invoker; import org.apache.maven.shared.invoker.MavenInvocationException; import org.codehaus.plexus.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; public class MavenDependencyScanner implements Scanner { private static final Logger LOGGER = LoggerFactory.getLogger(MavenDependencyScanner.class); private static final String MAVEN_REPO_LOCAL = "maven.repo.local"; private final LicenseMappingService licenseMappingService; public MavenDependencyScanner(LicenseMappingService licenseMappingService) { this.licenseMappingService = licenseMappingService; } @Override public Set scan(SensorContext context) { MavenSettings settings = getSettingsFromCommandLineArgs(); FileSystem fs = context.fileSystem(); FilePredicate pomXmlPredicate = fs.predicates().matchesPathPattern("**/pom.xml"); Set allDependencies = new HashSet<>(); for (InputFile pomXml : fs.inputFiles(pomXmlPredicate)) { context.markForPublishing(pomXml); LOGGER.info("Scanning for Maven dependencies (POM: {})", pomXml.uri()); try ( Stream dependencies = readDependencyList( new File(pomXml.uri()), settings ); ) { dependencies .map(this.loadLicenseFromPom(licenseMappingService.getLicenseMap(), settings)) .forEach(dependency -> { dependency.setInputComponent(pomXml); dependency.setTextRange(pomXml.newRange(1, 0, pomXml.lines(), 0)); allDependencies.add(dependency); }); } } return allDependencies; } private static Stream readDependencyList(File pomXml, MavenSettings settings) { Path tempFile = createTempFile(); if (tempFile == null) { return Stream.empty(); } InvocationRequest request = new DefaultInvocationRequest(); request.setRecursive(false); request.setPomFile(pomXml); request.setBaseDirectory(pomXml.getParentFile()); request.setGoals(Collections.singletonList("dependency:list")); if (settings.userSettings != null) { request.setUserSettingsFile(new File(settings.userSettings)); LOGGER.info("Using user settings {}", settings.userSettings); } if (settings.globalSettings != null) { request.setGlobalSettingsFile(new File(settings.globalSettings)); LOGGER.info("Using global settings {}", settings.globalSettings); } Properties properties = new Properties(); properties.setProperty("outputFile", tempFile.toAbsolutePath().toString()); properties.setProperty("outputAbsoluteArtifactFilename", "true"); properties.setProperty("includeScope", "runtime"); // only runtime (scope compile + runtime) if (System.getProperty(MAVEN_REPO_LOCAL) != null) { properties.setProperty(MAVEN_REPO_LOCAL, System.getProperty(MAVEN_REPO_LOCAL)); } if (System.getenv("MAVEN_OPTS") != null) { request.setMavenOpts(System.getenv("MAVEN_OPTS")); } request.setBatchMode(true); request.setProperties(properties); return invokeMaven(request, tempFile); } private static Stream invokeMaven(InvocationRequest request, Path mavenOutputFile) { try { StringBuilder mavenExecutionErrors = new StringBuilder(); Invoker invoker = new DefaultInvoker(); if (System.getProperty("maven.home") != null) { invoker.setMavenHome(new File(System.getProperty("maven.home"))); } else if (System.getenv("MAVEN_HOME") != null) { invoker.setMavenHome(new File(System.getenv("MAVEN_HOME"))); } else { String mvnPath = getMvnPath(); if (mvnPath != null) { invoker.setMavenHome(new File(mvnPath).getParentFile().getParentFile()); } else { LOGGER.warn("Could not find mvn in path"); } } request.setOutputHandler(line -> { if (line.startsWith("[ERROR] ")) { mavenExecutionErrors.append(line.substring(8)).append(System.lineSeparator()); } }); InvocationResult result = invoker.execute(request); if (result.getExitCode() != 0) { LOGGER.warn( "Could not get dependency list via maven", result.getExecutionException() ); LOGGER.warn(mavenExecutionErrors.toString()); } return Files.lines(mavenOutputFile) .filter(StringUtils::isNotBlank) .map(MavenDependencyScanner::findDependency) .filter(Objects::nonNull); } catch (MavenInvocationException e) { LOGGER.warn("Could not get dependency list via maven", e); } catch (Exception e) { LOGGER.warn("Error reading file", e); } return Stream.empty(); } private static String getMvnPath() { boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); String locateCommand = isWindows ? "where" : "which"; String mvnCommand = isWindows ? "mvn.cmd" : "mvn"; ProcessBuilder processBuilder = new ProcessBuilder(locateCommand, mvnCommand); try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()) ); String line = reader.readLine(); reader.close(); return line; } catch (IOException e) { LOGGER.warn("Could not find mvn in path: {}", e.getMessage()); return null; } } private static Path createTempFile() { try { Path tempFile = Files.createTempFile("dependencies", ".txt"); tempFile.toFile().deleteOnExit(); return tempFile; } catch (IOException e) { LOGGER.error("Could not create temp file for dependencies: {}", e.getMessage()); return null; } } static Dependency findDependency(String line) { String[] items = getItems(line); if (items == null) return null; String groupId = items[0]; String artifactId = items[1]; String version = items[3]; String path = items[5]; String classifier = null; if (items.length > 6) { classifier = items[3]; version = items[4]; path = items[6]; } if (classifier != null) { if ("data".equals(classifier)) { // skip data classifier return null; } path = path.replace("-" + classifier, ""); } int lastDotIndex = path.lastIndexOf('.'); if (lastDotIndex > 0) { path = path.substring(0, lastDotIndex) + ".pom"; } Dependency dependency = new Dependency( groupId + ":" + artifactId, version, null, LicenseCheckRulesDefinition.LANG_JAVA ); if (new File(path).exists()) { dependency.setPomPath(path); } return dependency; } private static String[] getItems(String line) { // Remove module info introduced with Maven Dependency Plugin 3.0 (and JDK > 9) line = line.replaceFirst(" -- module .*", ""); String[] items = line.trim().split(":"); if (items.length < 4) { return null; } // Windows-specific absolute path "C:\my\path" if (items[items.length - 2].length() == 1) { items[items.length - 2] += ":" + items[items.length - 1]; String[] newItems = new String[items.length - 1]; System.arraycopy(items, 0, newItems, 0, items.length - 1); items = newItems; } return items; } private Function loadLicenseFromPom( Map licenseMap, MavenSettings settings ) { return (Dependency dependency) -> { if ( StringUtils.isNotBlank(dependency.getLicense()) || dependency.getPomPath() == null ) { return dependency; } return loadLicense(licenseMap, settings, dependency); }; } private static Dependency loadLicense( Map licenseMap, MavenSettings settings, Dependency dependency ) { String pomPath = dependency.getPomPath(); if (pomPath != null) { List licenses = LicenseFinder.getLicenses( new File(pomPath), settings.userSettings, settings.globalSettings ); if (licenses.isEmpty()) { LOGGER.info("No licenses found in dependency {}", dependency.getName()); return dependency; } for (License license : licenses) { boolean found = licenseMatcher(licenseMap, dependency, license); if (found) { break; } } } return dependency; } /** * @return true if license was found in defined license list, false otherwise */ private static boolean licenseMatcher( Map licenseMap, Dependency dependency, License license ) { String licenseName = license.getName(); if (StringUtils.isBlank(licenseName)) { LOGGER.info("Dependency '{}' has an empty license.", dependency.getName()); return false; } for (Entry entry : licenseMap.entrySet()) { if (entry.getKey().matcher(licenseName).matches()) { dependency.setLicense(entry.getValue()); return true; } } dependency.setLicense(licenseName); LOGGER.info( "No licenses match found for '{}' in dependency '{}:{}'", licenseName, dependency.getName(), dependency.getVersion() ); return false; } private static MavenSettings getSettingsFromCommandLineArgs() { String globalSettings = null; String userSettings = null; String commandArgs = System.getProperty("sun.java.command"); try (java.util.Scanner scanner = new java.util.Scanner(commandArgs)) { while (scanner.hasNext()) { String part = scanner.next(); if (part.equals("-gs") || part.equals("--global-settings")) { globalSettings = scanner.next(); } else if (part.equals("-s") || part.equals("--settings")) { userSettings = scanner.next(); } } } catch (Exception e) { LOGGER.debug("Ignore unparsable command line", e); } return new MavenSettings(globalSettings, userSettings); } } class MavenSettings { final String globalSettings; final String userSettings; MavenSettings(String globalSettings, String userSetttings) { this.globalSettings = globalSettings; this.userSettings = userSetttings; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/Setting.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import java.io.File; class Setting { private File localRepositoryPath; public File getLocalRepositoryPath() { return localRepositoryPath; } public void setLocalRepositoryPath(File localRepositoryPath) { this.localRepositoryPath = localRepositoryPath; } public void setLocalRepositoryPath(String localRepositoryPath) { this.localRepositoryPath = new File(localRepositoryPath); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlHandler.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; class SettingsXmlHandler extends DefaultHandler { private boolean enableReadElementData = false; private String tagName = ""; private Setting setting; @Override public void startDocument() { setting = new Setting(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { if (qName.equals("localRepository")) { tagName = "localRepository"; } else { tagName = ""; } enableReadElementData = true; } @Override public void endElement(String uri, String localName, String qName) { enableReadElementData = false; } @Override public void characters(char[] ch, int start, int length) { if (enableReadElementData && tagName.equals("localRepository")) { setting.setLocalRepositoryPath(new String(ch, start, length)); } } public Setting getSetting() { return setting; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlParser.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import java.io.File; import javax.xml.XMLConstants; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SettingsXmlParser extends SettingsXmlHandler { private static final Logger LOGGER = LoggerFactory.getLogger(SettingsXmlParser.class); private SettingsXmlParser() {} public static Setting parseXmlFile(File filePath) { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); SettingsXmlHandler settingsXmlHandler = new SettingsXmlHandler(); SAXParser saxParser; if (filePath.exists()) { try { saxParser = saxParserFactory.newSAXParser(); saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); saxParser.parse(filePath, settingsXmlHandler); } catch (Exception e) { LOGGER.warn("Could not parse file " + filePath, e); } } else { LOGGER.info("Could not find file " + filePath); } return settingsXmlHandler.getSetting(); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.npm; 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 java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.HashSet; import java.util.Set; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; public class PackageJsonDependencyScanner implements Scanner { private static final Logger LOGGER = LoggerFactory.getLogger( PackageJsonDependencyScanner.class ); private static final String PACKAGE_LICENSE = "license"; private final LicenseMappingService licenseMappingService; private final boolean resolveTransitiveDeps; public PackageJsonDependencyScanner( LicenseMappingService licenseMappingService, boolean resolveTransitiveDeps ) { this.licenseMappingService = licenseMappingService; this.resolveTransitiveDeps = resolveTransitiveDeps; } @Override public Set scan(SensorContext context) { FileSystem fs = context.fileSystem(); FilePredicate packageJsonPredicate = fs.predicates().matchesPathPattern("**/package.json"); Set allDependencies = new HashSet<>(); LOGGER.info("Scanning for NPM dependencies (dir={})", fs.baseDir()); for (InputFile packageJsonFile : fs.inputFiles(packageJsonPredicate)) { context.markForPublishing(packageJsonFile); LOGGER.info("Scanning package.json: (path={})", packageJsonFile); allDependencies.addAll(dependencyParser(fs.baseDir(), packageJsonFile)); } return allDependencies; } private Set dependencyParser(File baseDir, InputFile packageJsonFile) { Set dependencies = new HashSet<>(); try ( InputStream fis = packageJsonFile.inputStream(); JsonReader jsonReader = Json.createReader(fis); ) { JsonObject packageJson = jsonReader.readObject(); JsonObject packageJsonDependencies = packageJson.getJsonObject("dependencies"); if (packageJsonDependencies != null) { scanDependencies( new File(packageJsonFile.uri().resolve("node_modules")), packageJsonDependencies.keySet(), dependencies ); dependencies.forEach(dependency -> { dependency.setInputComponent(packageJsonFile); dependency.setTextRange( packageJsonFile.newRange(1, 0, packageJsonFile.lines(), 0) ); }); } } catch (Exception e) { LOGGER.error("Error reading package.json", e); } return dependencies; } private void scanDependencies( File nodeModulesDir, Set packageNames, Set dependencies ) { LOGGER.info("Scanning NPM packages " + packageNames); for (String packageName : packageNames) { if (dependencies.stream().anyMatch(d -> packageName.equals(d.getName()))) { LOGGER.debug( "Package {} has already been encountered and will not be scanned again", packageName ); continue; } File packageJsonFile = new File(nodeModulesDir, packageName + "/package.json"); if (!packageJsonFile.exists()) { LOGGER.warn( "No package.json file found for package {} at {} - skipping dependency.", packageName, packageJsonFile ); continue; } try ( InputStream fis = new FileInputStream(packageJsonFile); JsonReader jsonReader = Json.createReader(fis); ) { JsonObject packageJson = jsonReader.readObject(); if (packageJson != null) { String license = licenseMappingService.mapLicense(readLicense(packageJson)); dependencies.add( new Dependency( packageName, packageJson.getString("version", null), license, LicenseCheckRulesDefinition.LANG_JS ) ); if (resolveTransitiveDeps) { JsonObject packageJsonDependencies = packageJson.getJsonObject( "dependencies" ); if (packageJsonDependencies != null) { scanDependencies( nodeModulesDir, packageJsonDependencies.keySet(), dependencies ); } } } } catch (FileNotFoundException e) { LOGGER.error("Could not load package.json", e); } catch (Exception e) { LOGGER.error("Could not check NPM package " + packageName, e); } } } private String readLicense(JsonObject packageJson) { if (packageJson.containsKey(PACKAGE_LICENSE)) { final Object licenceObj = packageJson.get(PACKAGE_LICENSE); if (licenceObj instanceof JsonObject) { return ((JsonObject) licenceObj).getString("type", ""); } else { return packageJson.getString(PACKAGE_LICENSE, ""); } } else if (packageJson.containsKey("licenses")) { final JsonArray licenses = packageJson.getJsonArray("licenses"); if (licenses.size() == 1) { return licenses.getJsonObject(0).getString("type", ""); } else if (licenses.size() > 1) { String license = "("; for (JsonValue licenseObj : licenses) { if (licenseObj instanceof JsonObject) { String licensePart = licenseObj.asJsonObject().getString("type", ""); if (!licensePart.trim().isEmpty()) { license += license.length() > 1 ? (" OR " + licensePart) : licensePart; } } } return license.length() == 1 ? "" : (license + ")"); } } return ""; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicense.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.projectlicense; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; public class ProjectLicense implements Comparable { public static final String FIELD_PROJECT_KEY = "projectKey"; public static final String FIELD_LICENSE = "license"; public static final String FIELD_ALLOWED = "allowed"; private final String projectKey; private final String license; private final Boolean allowed; public ProjectLicense(String projectKey, String license, Boolean allowed) { super(); this.projectKey = projectKey; this.license = license; this.allowed = allowed; } public ProjectLicense(String projectKey, String license, String allowed) { this(projectKey, license, Boolean.parseBoolean(allowed)); } public String getProjectKey() { return projectKey; } public String getLicense() { return license; } public Boolean getAllowed() { return allowed; } @Deprecated public static List fromString(String projectLicensesString) { List projectLicenses = new ArrayList<>(); try (JsonReader jsonReader = Json.createReader(new StringReader(projectLicensesString))) { JsonArray projectLicensesJson = jsonReader.readArray(); for (int i = 0; i < projectLicensesJson.size(); i++) { JsonObject projectLicenseJson = projectLicensesJson.getJsonObject(i); projectLicenses.add( new ProjectLicense( projectLicenseJson.getString(FIELD_PROJECT_KEY), projectLicenseJson.getString(FIELD_LICENSE), projectLicenseJson.getString("status") ) ); } } return projectLicenses; } public int compareTo(ProjectLicense o) { if (o == null) { return 1; } if (this.getProjectKey().compareTo(o.getProjectKey()) == 0) { if (this.getLicense().compareTo(o.getLicense()) == 0) { return this.getAllowed().compareTo(o.getAllowed()); } else { return this.getLicense().compareTo(o.getLicense()); } } else { return this.getProjectKey().compareTo(o.getProjectKey()); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ProjectLicense that = (ProjectLicense) o; return ( Objects.equals(projectKey, that.projectKey) && Objects.equals(license, that.license) && Objects.equals(allowed, that.allowed) ); } @Override public int hashCode() { return Objects.hash(projectKey, license, allowed); } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.projectlicense; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_SET; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_ALLOWED; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_PROJECT_KEY; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.sonar.api.config.Configuration; import org.sonar.api.scanner.ScannerSide; import org.sonar.api.server.ServerSide; @ServerSide @ScannerSide public class ProjectLicenseService { private final Configuration configuration; public ProjectLicenseService(Configuration configuration) { super(); this.configuration = configuration; } public List getProjectLicenseList() { return Arrays.stream(configuration.getStringArray(PROJECT_LICENSE_SET)) .map(idx -> { String idxProp = "." + idx + "."; String projectKey = configuration .get(PROJECT_LICENSE_SET + idxProp + FIELD_PROJECT_KEY) .orElse(null); String license = configuration .get(PROJECT_LICENSE_SET + idxProp + FIELD_LICENSE) .orElse(null); Boolean allowed = configuration .getBoolean(PROJECT_LICENSE_SET + idxProp + FIELD_ALLOWED) .orElse(Boolean.FALSE); return new ProjectLicense(projectKey, license, allowed); }) .collect(Collectors.toList()); } public Collection getProjectLicenseList(String projectKey) { Collection allProjectLicenses = getProjectLicenseList(); Collection projectSpecificLicenses = new ArrayList<>(); for (ProjectLicense projectLicense : allProjectLicenses) { if (projectLicense.getProjectKey().equals(projectKey)) { projectSpecificLicenses.add(projectLicense); } } return projectSpecificLicenses; } } ================================================ FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtils.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.utils; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; public class IOUtils { private IOUtils() {} public static String readToString(InputStream input) throws IOException { Reader in = new InputStreamReader(input, StandardCharsets.UTF_8); StringWriter out = new StringWriter(); int n; char[] buffer = new char[4096]; while ((n = in.read(buffer)) != -1) { out.write(buffer, 0, n); } return out.toString(); } } ================================================ FILE: src/main/resources/at/porscheinformatik/sonarqube/licensecheck/license/spdx_license_list.json ================================================ [ { "identifier": "Glide", "name": "3dfx Glide License", "status": "false" }, { "identifier": "Abstyles", "name": "Abstyles License", "status": "false" }, { "identifier": "AFL-1.1", "name": "Academic Free License v1.1", "status": "false" }, { "identifier": "AFL-1.2", "name": "Academic Free License v1.2", "status": "false" }, { "identifier": "AFL-2.0", "name": "Academic Free License v2.0", "status": "false" }, { "identifier": "AFL-2.1", "name": "Academic Free License v2.1", "status": "false" }, { "identifier": "AFL-3.0", "name": "Academic Free License v3.0", "status": "false" }, { "identifier": "AMPAS", "name": "Academy of Motion Picture Arts and Sciences BSD", "status": "false" }, { "identifier": "APL-1.0", "name": "Adaptive Public License 1.0", "status": "false" }, { "identifier": "Adobe-Glyph", "name": "Adobe Glyph List License", "status": "false" }, { "identifier": "APAFML", "name": "Adobe Postscript AFM License", "status": "false" }, { "identifier": "Adobe-2006", "name": "Adobe Systems Incorporated Source Code License Agreement", "status": "false" }, { "identifier": "AGPL-1.0", "name": "Affero General Public License v1.0", "status": "false" }, { "identifier": "Afmparse", "name": "Afmparse License", "status": "false" }, { "identifier": "Aladdin", "name": "Aladdin Free Public License", "status": "false" }, { "identifier": "ADSL", "name": "Amazon Digital Services License", "status": "false" }, { "identifier": "AMDPLPA", "name": "AMD's plpa_map.c License", "status": "false" }, { "identifier": "ANTLR-PD", "name": "ANTLR Software Rights Notice", "status": "false" }, { "identifier": "Apache-1.0", "name": "Apache License 1.0", "status": "false" }, { "identifier": "Apache-1.1", "name": "Apache License 1.1", "status": "false" }, { "identifier": "Apache-2.0", "name": "Apache License 2.0", "status": "false" }, { "identifier": "AML", "name": "Apple MIT License", "status": "false" }, { "identifier": "APSL-1.0", "name": "Apple Public Source License 1.0", "status": "false" }, { "identifier": "APSL-1.1", "name": "Apple Public Source License 1.1", "status": "false" }, { "identifier": "APSL-1.2", "name": "Apple Public Source License 1.2", "status": "false" }, { "identifier": "APSL-2.0", "name": "Apple Public Source License 2.0", "status": "false" }, { "identifier": "Artistic-1.0", "name": "Artistic License 1.0", "status": "false" }, { "identifier": "Artistic-1.0-Perl", "name": "Artistic License 1.0 (Perl)", "status": "false" }, { "identifier": "Artistic-1.0-cl8", "name": "Artistic License 1.0 w/clause 8", "status": "false" }, { "identifier": "Artistic-2.0", "name": "Artistic License 2.0", "status": "false" }, { "identifier": "AAL", "name": "Attribution Assurance License", "status": "false" }, { "identifier": "Bahyph", "name": "Bahyph License", "status": "false" }, { "identifier": "Barr", "name": "Barr License", "status": "false" }, { "identifier": "Beerware", "name": "Beerware License", "status": "false" }, { "identifier": "BitTorrent-1.0", "name": "BitTorrent Open Source License v1.0", "status": "false" }, { "identifier": "BitTorrent-1.1", "name": "BitTorrent Open Source License v1.1", "status": "false" }, { "identifier": "BSL-1.0", "name": "Boost Software License 1.0", "status": "false" }, { "identifier": "Borceux", "name": "Borceux license", "status": "false" }, { "identifier": "BSD-2-Clause", "name": "BSD 2-clause \"Simplified\" License", "status": "false" }, { "identifier": "BSD-2-Clause-FreeBSD", "name": "BSD 2-clause FreeBSD License", "status": "false" }, { "identifier": "BSD-2-Clause-NetBSD", "name": "BSD 2-clause NetBSD License", "status": "false" }, { "identifier": "BSD-3-Clause", "name": "BSD 3-clause \"New\" or \"Revised\" License", "status": "false" }, { "identifier": "BSD-3-Clause-Clear", "name": "BSD 3-clause Clear License", "status": "false" }, { "identifier": "BSD-3-Clause-No-Nuclear-License", "name": "BSD 3-Clause No Nuclear License", "status": "false" }, { "identifier": "BSD-3-Clause-No-Nuclear-License-2014", "name": "BSD 3-Clause No Nuclear License 2014", "status": "false" }, { "identifier": "BSD-3-Clause-No-Nuclear-Warranty", "name": "BSD 3-Clause No Nuclear Warranty", "status": "false" }, { "identifier": "BSD-4-Clause", "name": "BSD 4-clause \"Original\" or \"Old\" License", "status": "false" }, { "identifier": "BSD-Protection", "name": "BSD Protection License", "status": "false" }, { "identifier": "BSD-Source-Code", "name": "BSD Source Code Attribution", "status": "false" }, { "identifier": "BSD-3-Clause-Attribution", "name": "BSD with attribution", "status": "false" }, { "identifier": "0BSD", "name": "BSD Zero Clause License", "status": "false" }, { "identifier": "BSD-4-Clause-UC", "name": "BSD-4-Clause (University of California-Specific)", "status": "false" }, { "identifier": "bzip2-1.0.5", "name": "bzip2 and libbzip2 License v1.0.5", "status": "false" }, { "identifier": "bzip2-1.0.6", "name": "bzip2 and libbzip2 License v1.0.6", "status": "false" }, { "identifier": "Caldera", "name": "Caldera License", "status": "false" }, { "identifier": "CECILL-1.0", "name": "CeCILL Free Software License Agreement v1.0", "status": "false" }, { "identifier": "CECILL-1.1", "name": "CeCILL Free Software License Agreement v1.1", "status": "false" }, { "identifier": "CECILL-2.0", "name": "CeCILL Free Software License Agreement v2.0", "status": "false" }, { "identifier": "CECILL-2.1", "name": "CeCILL Free Software License Agreement v2.1", "status": "false" }, { "identifier": "CECILL-B", "name": "CeCILL-B Free Software License Agreement", "status": "false" }, { "identifier": "CECILL-C", "name": "CeCILL-C Free Software License Agreement", "status": "false" }, { "identifier": "ClArtistic", "name": "Clarified Artistic License", "status": "false" }, { "identifier": "MIT-CMU", "name": "CMU License", "status": "false" }, { "identifier": "CNRI-Jython", "name": "CNRI Jython License", "status": "false" }, { "identifier": "CNRI-Python", "name": "CNRI Python License", "status": "false" }, { "identifier": "CNRI-Python-GPL-Compatible", "name": "CNRI Python Open Source GPL Compatible License Agreement", "status": "false" }, { "identifier": "CPOL-1.02", "name": "Code Project Open License 1.02", "status": "false" }, { "identifier": "CDDL-1.0", "name": "Common Development and Distribution License 1.0", "status": "false" }, { "identifier": "CDDL-1.1", "name": "Common Development and Distribution License 1.1", "status": "false" }, { "identifier": "CPAL-1.0", "name": "Common Public Attribution License 1.0", "status": "false" }, { "identifier": "CPL-1.0", "name": "Common Public License 1.0", "status": "false" }, { "identifier": "CATOSL-1.1", "name": "Computer Associates Trusted Open Source License 1.1", "status": "false" }, { "identifier": "Condor-1.1", "name": "Condor Public License v1.1", "status": "false" }, { "identifier": "CC-BY-1.0", "name": "Creative Commons Attribution 1.0", "status": "false" }, { "identifier": "CC-BY-2.0", "name": "Creative Commons Attribution 2.0", "status": "false" }, { "identifier": "CC-BY-2.5", "name": "Creative Commons Attribution 2.5", "status": "false" }, { "identifier": "CC-BY-3.0", "name": "Creative Commons Attribution 3.0", "status": "false" }, { "identifier": "CC-BY-4.0", "name": "Creative Commons Attribution 4.0", "status": "false" }, { "identifier": "CC-BY-ND-1.0", "name": "Creative Commons Attribution No Derivatives 1.0", "status": "false" }, { "identifier": "CC-BY-ND-2.0", "name": "Creative Commons Attribution No Derivatives 2.0", "status": "false" }, { "identifier": "CC-BY-ND-2.5", "name": "Creative Commons Attribution No Derivatives 2.5", "status": "false" }, { "identifier": "CC-BY-ND-3.0", "name": "Creative Commons Attribution No Derivatives 3.0", "status": "false" }, { "identifier": "CC-BY-ND-4.0", "name": "Creative Commons Attribution No Derivatives 4.0", "status": "false" }, { "identifier": "CC-BY-NC-1.0", "name": "Creative Commons Attribution Non Commercial 1.0", "status": "false" }, { "identifier": "CC-BY-NC-2.0", "name": "Creative Commons Attribution Non Commercial 2.0", "status": "false" }, { "identifier": "CC-BY-NC-2.5", "name": "Creative Commons Attribution Non Commercial 2.5", "status": "false" }, { "identifier": "CC-BY-NC-3.0", "name": "Creative Commons Attribution Non Commercial 3.0", "status": "false" }, { "identifier": "CC-BY-NC-4.0", "name": "Creative Commons Attribution Non Commercial 4.0", "status": "false" }, { "identifier": "CC-BY-NC-ND-1.0", "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0", "status": "false" }, { "identifier": "CC-BY-NC-ND-2.0", "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0", "status": "false" }, { "identifier": "CC-BY-NC-ND-2.5", "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5", "status": "false" }, { "identifier": "CC-BY-NC-ND-3.0", "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0", "status": "false" }, { "identifier": "CC-BY-NC-ND-4.0", "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0", "status": "false" }, { "identifier": "CC-BY-NC-SA-1.0", "name": "Creative Commons Attribution Non Commercial Share Alike 1.0", "status": "false" }, { "identifier": "CC-BY-NC-SA-2.0", "name": "Creative Commons Attribution Non Commercial Share Alike 2.0", "status": "false" }, { "identifier": "CC-BY-NC-SA-2.5", "name": "Creative Commons Attribution Non Commercial Share Alike 2.5", "status": "false" }, { "identifier": "CC-BY-NC-SA-3.0", "name": "Creative Commons Attribution Non Commercial Share Alike 3.0", "status": "false" }, { "identifier": "CC-BY-NC-SA-4.0", "name": "Creative Commons Attribution Non Commercial Share Alike 4.0", "status": "false" }, { "identifier": "CC-BY-SA-1.0", "name": "Creative Commons Attribution Share Alike 1.0", "status": "false" }, { "identifier": "CC-BY-SA-2.0", "name": "Creative Commons Attribution Share Alike 2.0", "status": "false" }, { "identifier": "CC-BY-SA-2.5", "name": "Creative Commons Attribution Share Alike 2.5", "status": "false" }, { "identifier": "CC-BY-SA-3.0", "name": "Creative Commons Attribution Share Alike 3.0", "status": "false" }, { "identifier": "CC-BY-SA-4.0", "name": "Creative Commons Attribution Share Alike 4.0", "status": "false" }, { "identifier": "CC0-1.0", "name": "Creative Commons Zero v1.0 Universal", "status": "false" }, { "identifier": "Crossword", "name": "Crossword License", "status": "false" }, { "identifier": "CrystalStacker", "name": "CrystalStacker License", "status": "false" }, { "identifier": "CUA-OPL-1.0", "name": "CUA Office Public License v1.0", "status": "false" }, { "identifier": "Cube", "name": "Cube License", "status": "false" }, { "identifier": "curl", "name": "curl License", "status": "false" }, { "identifier": "D-FSL-1.0", "name": "Deutsche Freie Software Lizenz", "status": "false" }, { "identifier": "diffmark", "name": "diffmark license", "status": "false" }, { "identifier": "WTFPL", "name": "Do What The F*ck You Want To Public License", "status": "false" }, { "identifier": "DOC", "name": "DOC License", "status": "false" }, { "identifier": "Dotseqn", "name": "Dotseqn License", "status": "false" }, { "identifier": "DSDP", "name": "DSDP License", "status": "false" }, { "identifier": "dvipdfm", "name": "dvipdfm License", "status": "false" }, { "identifier": "EPL-1.0", "name": "Eclipse Public License 1.0", "status": "false" }, { "identifier": "ECL-1.0", "name": "Educational Community License v1.0", "status": "false" }, { "identifier": "ECL-2.0", "name": "Educational Community License v2.0", "status": "false" }, { "identifier": "eGenix", "name": "eGenix.com Public License 1.1.0", "status": "false" }, { "identifier": "EFL-1.0", "name": "Eiffel Forum License v1.0", "status": "false" }, { "identifier": "EFL-2.0", "name": "Eiffel Forum License v2.0", "status": "false" }, { "identifier": "MIT-advertising", "name": "Enlightenment License (e16)", "status": "false" }, { "identifier": "MIT-enna", "name": "enna License", "status": "false" }, { "identifier": "Entessa", "name": "Entessa Public License v1.0", "status": "false" }, { "identifier": "ErlPL-1.1", "name": "Erlang Public License v1.1", "status": "false" }, { "identifier": "EUDatagrid", "name": "EU DataGrid Software License", "status": "false" }, { "identifier": "EUPL-1.0", "name": "European Union Public License 1.0", "status": "false" }, { "identifier": "EUPL-1.1", "name": "European Union Public License 1.1", "status": "false" }, { "identifier": "Eurosym", "name": "Eurosym License", "status": "false" }, { "identifier": "Fair", "name": "Fair License", "status": "false" }, { "identifier": "MIT-feh", "name": "feh License", "status": "false" }, { "identifier": "Frameworx-1.0", "name": "Frameworx Open License 1.0", "status": "false" }, { "identifier": "FreeImage", "name": "FreeImage Public License v1.0", "status": "false" }, { "identifier": "FTL", "name": "Freetype Project License", "status": "false" }, { "identifier": "FSFAP", "name": "FSF All Permissive License", "status": "false" }, { "identifier": "FSFUL", "name": "FSF Unlimited License", "status": "false" }, { "identifier": "FSFULLR", "name": "FSF Unlimited License (with License Retention)", "status": "false" }, { "identifier": "Giftware", "name": "Giftware License", "status": "false" }, { "identifier": "GL2PS", "name": "GL2PS License", "status": "false" }, { "identifier": "Glulxe", "name": "Glulxe License", "status": "false" }, { "identifier": "AGPL-3.0", "name": "GNU Affero General Public License v3.0", "status": "false" }, { "identifier": "GFDL-1.1", "name": "GNU Free Documentation License v1.1", "status": "false" }, { "identifier": "GFDL-1.2", "name": "GNU Free Documentation License v1.2", "status": "false" }, { "identifier": "GFDL-1.3", "name": "GNU Free Documentation License v1.3", "status": "false" }, { "identifier": "GPL-1.0", "name": "GNU General Public License v1.0 only", "status": "false" }, { "identifier": "GPL-2.0", "name": "GNU General Public License v2.0 only", "status": "false" }, { "identifier": "GPL-3.0", "name": "GNU General Public License v3.0 only", "status": "false" }, { "identifier": "LGPL-2.1", "name": "GNU Lesser General Public License v2.1 only", "status": "false" }, { "identifier": "LGPL-3.0", "name": "GNU Lesser General Public License v3.0 only", "status": "false" }, { "identifier": "LGPL-2.0", "name": "GNU Library General Public License v2 only", "status": "false" }, { "identifier": "gnuplot", "name": "gnuplot License", "status": "false" }, { "identifier": "gSOAP-1.3b", "name": "gSOAP Public License v1.3b", "status": "false" }, { "identifier": "HaskellReport", "name": "Haskell Language Report License", "status": "false" }, { "identifier": "HPND", "name": "Historic Permission Notice and Disclaimer", "status": "false" }, { "identifier": "IBM-pibs", "name": "IBM PowerPC Initialization and Boot Software", "status": "false" }, { "identifier": "IPL-1.0", "name": "IBM Public License v1.0", "status": "false" }, { "identifier": "ICU", "name": "ICU License", "status": "false" }, { "identifier": "ImageMagick", "name": "ImageMagick License", "status": "false" }, { "identifier": "iMatix", "name": "iMatix Standard Function Library Agreement", "status": "false" }, { "identifier": "Imlib2", "name": "Imlib2 License", "status": "false" }, { "identifier": "IJG", "name": "Independent JPEG Group License", "status": "false" }, { "identifier": "Info-ZIP", "name": "Info-ZIP License", "status": "false" }, { "identifier": "Intel-ACPI", "name": "Intel ACPI Software License Agreement", "status": "false" }, { "identifier": "Intel", "name": "Intel Open Source License", "status": "false" }, { "identifier": "Interbase-1.0", "name": "Interbase Public License v1.0", "status": "false" }, { "identifier": "IPA", "name": "IPA Font License", "status": "false" }, { "identifier": "ISC", "name": "ISC License", "status": "false" }, { "identifier": "JasPer-2.0", "name": "JasPer License", "status": "false" }, { "identifier": "JSON", "name": "JSON License", "status": "false" }, { "identifier": "LPPL-1.0", "name": "LaTeX Project Public License v1.0", "status": "false" }, { "identifier": "LPPL-1.1", "name": "LaTeX Project Public License v1.1", "status": "false" }, { "identifier": "LPPL-1.2", "name": "LaTeX Project Public License v1.2", "status": "false" }, { "identifier": "LPPL-1.3a", "name": "LaTeX Project Public License v1.3a", "status": "false" }, { "identifier": "LPPL-1.3c", "name": "LaTeX Project Public License v1.3c", "status": "false" }, { "identifier": "Latex2e", "name": "Latex2e License", "status": "false" }, { "identifier": "BSD-3-Clause-LBNL", "name": "Lawrence Berkeley National Labs BSD variant license", "status": "false" }, { "identifier": "Leptonica", "name": "Leptonica License", "status": "false" }, { "identifier": "LGPLLR", "name": "Lesser General Public License For Linguistic Resources", "status": "false" }, { "identifier": "Libpng", "name": "libpng License", "status": "false" }, { "identifier": "libtiff", "name": "libtiff License", "status": "false" }, { "identifier": "LAL-1.2", "name": "Licence Art Libre 1.2", "status": "false" }, { "identifier": "LAL-1.3", "name": "Licence Art Libre 1.3", "status": "false" }, { "identifier": "LiLiQ-P-1.1", "name": "Licence Libre du Québec – Permissive version 1.1", "status": "false" }, { "identifier": "LiLiQ-Rplus-1.1", "name": "Licence Libre du Québec – Réciprocité forte version 1.1", "status": "false" }, { "identifier": "LiLiQ-R-1.1", "name": "Licence Libre du Québec – Réciprocité version 1.1", "status": "false" }, { "identifier": "LPL-1.02", "name": "Lucent Public License v1.02", "status": "false" }, { "identifier": "LPL-1.0", "name": "Lucent Public License Version 1.0", "status": "false" }, { "identifier": "MakeIndex", "name": "MakeIndex License", "status": "false" }, { "identifier": "MTLL", "name": "Matrix Template Library License", "status": "false" }, { "identifier": "MS-PL", "name": "Microsoft Public License", "status": "false" }, { "identifier": "MS-RL", "name": "Microsoft Reciprocal License", "status": "false" }, { "identifier": "MirOS", "name": "MirOS Licence", "status": "false" }, { "identifier": "MITNFA", "name": "MIT +no-false-attribs license", "status": "false" }, { "identifier": "MIT", "name": "MIT License", "status": "false" }, { "identifier": "Motosoto", "name": "Motosoto License", "status": "false" }, { "identifier": "MPL-1.0", "name": "Mozilla Public License 1.0", "status": "false" }, { "identifier": "MPL-1.1", "name": "Mozilla Public License 1.1", "status": "false" }, { "identifier": "MPL-2.0", "name": "Mozilla Public License 2.0", "status": "false" }, { "identifier": "MPL-2.0-no-copyleft-exception", "name": "Mozilla Public License 2.0 (no copyleft exception)", "status": "false" }, { "identifier": "mpich2", "name": "mpich2 License", "status": "false" }, { "identifier": "Multics", "name": "Multics License", "status": "false" }, { "identifier": "Mup", "name": "Mup License", "status": "false" }, { "identifier": "NASA-1.3", "name": "NASA Open Source Agreement 1.3", "status": "false" }, { "identifier": "Naumen", "name": "Naumen Public License", "status": "false" }, { "identifier": "NBPL-1.0", "name": "Net Boolean Public License v1", "status": "false" }, { "identifier": "Net-SNMP", "name": "Net-SNMP License", "status": "false" }, { "identifier": "NetCDF", "name": "NetCDF license", "status": "false" }, { "identifier": "NGPL", "name": "Nethack General Public License", "status": "false" }, { "identifier": "NOSL", "name": "Netizen Open Source License", "status": "false" }, { "identifier": "NPL-1.0", "name": "Netscape Public License v1.0", "status": "false" }, { "identifier": "NPL-1.1", "name": "Netscape Public License v1.1", "status": "false" }, { "identifier": "Newsletr", "name": "Newsletr License", "status": "false" }, { "identifier": "NLPL", "name": "No Limit Public License", "status": "false" }, { "identifier": "Nokia", "name": "Nokia Open Source License", "status": "false" }, { "identifier": "NPOSL-3.0", "name": "Non-Profit Open Software License 3.0", "status": "false" }, { "identifier": "NLOD-1.0", "name": "Norwegian Licence for Open Government Data", "status": "false" }, { "identifier": "Noweb", "name": "Noweb License", "status": "false" }, { "identifier": "NRL", "name": "NRL License", "status": "false" }, { "identifier": "NTP", "name": "NTP License", "status": "false" }, { "identifier": "Nunit", "name": "Nunit License", "status": "false" }, { "identifier": "OCLC-2.0", "name": "OCLC Research Public License 2.0", "status": "false" }, { "identifier": "ODbL-1.0", "name": "ODC Open Database License v1.0", "status": "false" }, { "identifier": "PDDL-1.0", "name": "ODC Public Domain Dedication & License 1.0", "status": "false" }, { "identifier": "OCCT-PL", "name": "Open CASCADE Technology Public License", "status": "false" }, { "identifier": "OGTSL", "name": "Open Group Test Suite License", "status": "false" }, { "identifier": "OLDAP-2.2.2", "name": "Open LDAP Public License 2.2.2", "status": "false" }, { "identifier": "OLDAP-1.1", "name": "Open LDAP Public License v1.1", "status": "false" }, { "identifier": "OLDAP-1.2", "name": "Open LDAP Public License v1.2", "status": "false" }, { "identifier": "OLDAP-1.3", "name": "Open LDAP Public License v1.3", "status": "false" }, { "identifier": "OLDAP-1.4", "name": "Open LDAP Public License v1.4", "status": "false" }, { "identifier": "OLDAP-2.0", "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", "status": "false" }, { "identifier": "OLDAP-2.0.1", "name": "Open LDAP Public License v2.0.1", "status": "false" }, { "identifier": "OLDAP-2.1", "name": "Open LDAP Public License v2.1", "status": "false" }, { "identifier": "OLDAP-2.2", "name": "Open LDAP Public License v2.2", "status": "false" }, { "identifier": "OLDAP-2.2.1", "name": "Open LDAP Public License v2.2.1", "status": "false" }, { "identifier": "OLDAP-2.3", "name": "Open LDAP Public License v2.3", "status": "false" }, { "identifier": "OLDAP-2.4", "name": "Open LDAP Public License v2.4", "status": "false" }, { "identifier": "OLDAP-2.5", "name": "Open LDAP Public License v2.5", "status": "false" }, { "identifier": "OLDAP-2.6", "name": "Open LDAP Public License v2.6", "status": "false" }, { "identifier": "OLDAP-2.7", "name": "Open LDAP Public License v2.7", "status": "false" }, { "identifier": "OLDAP-2.8", "name": "Open LDAP Public License v2.8", "status": "false" }, { "identifier": "OML", "name": "Open Market License", "status": "false" }, { "identifier": "OPL-1.0", "name": "Open Public License v1.0", "status": "false" }, { "identifier": "OSL-1.0", "name": "Open Software License 1.0", "status": "false" }, { "identifier": "OSL-1.1", "name": "Open Software License 1.1", "status": "false" }, { "identifier": "OSL-2.0", "name": "Open Software License 2.0", "status": "false" }, { "identifier": "OSL-2.1", "name": "Open Software License 2.1", "status": "false" }, { "identifier": "OSL-3.0", "name": "Open Software License 3.0", "status": "false" }, { "identifier": "OpenSSL", "name": "OpenSSL License", "status": "false" }, { "identifier": "OSET-PL-2.1", "name": "OSET Public License version 2.1", "status": "false" }, { "identifier": "PHP-3.0", "name": "PHP License v3.0", "status": "false" }, { "identifier": "PHP-3.01", "name": "PHP License v3.01", "status": "false" }, { "identifier": "Plexus", "name": "Plexus Classworlds License", "status": "false" }, { "identifier": "PostgreSQL", "name": "PostgreSQL License", "status": "false" }, { "identifier": "psfrag", "name": "psfrag License", "status": "false" }, { "identifier": "psutils", "name": "psutils License", "status": "false" }, { "identifier": "Python-2.0", "name": "Python License 2.0", "status": "false" }, { "identifier": "QPL-1.0", "name": "Q Public License 1.0", "status": "false" }, { "identifier": "Qhull", "name": "Qhull License", "status": "false" }, { "identifier": "Rdisc", "name": "Rdisc License", "status": "false" }, { "identifier": "RPSL-1.0", "name": "RealNetworks Public Source License v1.0", "status": "false" }, { "identifier": "RPL-1.1", "name": "Reciprocal Public License 1.1", "status": "false" }, { "identifier": "RPL-1.5", "name": "Reciprocal Public License 1.5", "status": "false" }, { "identifier": "RHeCos-1.1", "name": "Red Hat eCos Public License v1.1", "status": "false" }, { "identifier": "RSCPL", "name": "Ricoh Source Code Public License", "status": "false" }, { "identifier": "RSA-MD", "name": "RSA Message-Digest License ", "status": "false" }, { "identifier": "Ruby", "name": "Ruby License", "status": "false" }, { "identifier": "SAX-PD", "name": "Sax Public Domain Notice", "status": "false" }, { "identifier": "Saxpath", "name": "Saxpath License", "status": "false" }, { "identifier": "SCEA", "name": "SCEA Shared Source License", "status": "false" }, { "identifier": "SWL", "name": "Scheme Widget Library (SWL) Software License Agreement", "status": "false" }, { "identifier": "SMPPL", "name": "Secure Messaging Protocol Public License", "status": "false" }, { "identifier": "Sendmail", "name": "Sendmail License", "status": "false" }, { "identifier": "SGI-B-1.0", "name": "SGI Free Software License B v1.0", "status": "false" }, { "identifier": "SGI-B-1.1", "name": "SGI Free Software License B v1.1", "status": "false" }, { "identifier": "SGI-B-2.0", "name": "SGI Free Software License B v2.0", "status": "false" }, { "identifier": "OFL-1.0", "name": "SIL Open Font License 1.0", "status": "false" }, { "identifier": "OFL-1.1", "name": "SIL Open Font License 1.1", "status": "false" }, { "identifier": "SimPL-2.0", "name": "Simple Public License 2.0", "status": "false" }, { "identifier": "Sleepycat", "name": "Sleepycat License", "status": "false" }, { "identifier": "SNIA", "name": "SNIA Public License 1.1", "status": "false" }, { "identifier": "Spencer-86", "name": "Spencer License 86", "status": "false" }, { "identifier": "Spencer-94", "name": "Spencer License 94", "status": "false" }, { "identifier": "Spencer-99", "name": "Spencer License 99", "status": "false" }, { "identifier": "SMLNJ", "name": "Standard ML of New Jersey License", "status": "false" }, { "identifier": "SugarCRM-1.1.3", "name": "SugarCRM Public License v1.1.3", "status": "false" }, { "identifier": "SISSL", "name": "Sun Industry Standards Source License v1.1", "status": "false" }, { "identifier": "SISSL-1.2", "name": "Sun Industry Standards Source License v1.2", "status": "false" }, { "identifier": "SPL-1.0", "name": "Sun Public License v1.0", "status": "false" }, { "identifier": "Watcom-1.0", "name": "Sybase Open Watcom Public License 1.0", "status": "false" }, { "identifier": "TCL", "name": "TCL/TK License", "status": "false" }, { "identifier": "TCP-wrappers", "name": "TCP Wrappers License", "status": "false" }, { "identifier": "Unlicense", "name": "The Unlicense", "status": "false" }, { "identifier": "TMate", "name": "TMate Open Source License", "status": "false" }, { "identifier": "TORQUE-1.1", "name": "TORQUE v2.5+ Software License v1.1", "status": "false" }, { "identifier": "TOSL", "name": "Trusster Open Source License", "status": "false" }, { "identifier": "Unicode-DFS-2015", "name": "Unicode License Agreement - Data Files and Software (2015)", "status": "false" }, { "identifier": "Unicode-DFS-2016", "name": "Unicode License Agreement - Data Files and Software (2016)", "status": "false" }, { "identifier": "Unicode-TOU", "name": "Unicode Terms of Use", "status": "false" }, { "identifier": "UPL-1.0", "name": "Universal Permissive License v1.0", "status": "false" }, { "identifier": "NCSA", "name": "University of Illinois/NCSA Open Source License", "status": "false" }, { "identifier": "Vim", "name": "Vim License", "status": "false" }, { "identifier": "VOSTROM", "name": "VOSTROM Public License for Open Source", "status": "false" }, { "identifier": "VSL-1.0", "name": "Vovida Software License v1.0", "status": "false" }, { "identifier": "W3C-20150513", "name": "W3C Software Notice and Document License (2015-05-13)", "status": "false" }, { "identifier": "W3C-19980720", "name": "W3C Software Notice and License (1998-07-20)", "status": "false" }, { "identifier": "W3C", "name": "W3C Software Notice and License (2002-12-31)", "status": "false" }, { "identifier": "Wsuipa", "name": "Wsuipa License", "status": "false" }, { "identifier": "Xnet", "name": "X.Net License", "status": "false" }, { "identifier": "X11", "name": "X11 License", "status": "false" }, { "identifier": "Xerox", "name": "Xerox License", "status": "false" }, { "identifier": "XFree86-1.1", "name": "XFree86 License 1.1", "status": "false" }, { "identifier": "xinetd", "name": "xinetd License", "status": "false" }, { "identifier": "xpp", "name": "XPP License", "status": "false" }, { "identifier": "XSkat", "name": "XSkat License", "status": "false" }, { "identifier": "YPL-1.0", "name": "Yahoo! Public License v1.0", "status": "false" }, { "identifier": "YPL-1.1", "name": "Yahoo! Public License v1.1", "status": "false" }, { "identifier": "Zed", "name": "Zed License", "status": "false" }, { "identifier": "Zend-2.0", "name": "Zend License v2.0", "status": "false" }, { "identifier": "Zimbra-1.3", "name": "Zimbra Public License v1.3", "status": "false" }, { "identifier": "Zimbra-1.4", "name": "Zimbra Public License v1.4", "status": "false" }, { "identifier": "Zlib", "name": "zlib License", "status": "false" }, { "identifier": "zlib-acknowledgement", "name": "zlib/libpng License with Acknowledgement", "status": "false" }, { "identifier": "ZPL-1.1", "name": "Zope Public License 1.1", "status": "false" }, { "identifier": "ZPL-2.0", "name": "Zope Public License 2.0", "status": "false" }, { "identifier": "ZPL-2.1", "name": "Zope Public License 2.1", "status": "false" }, { "identifier": "eCos-2.0", "name": "eCos license version 2.0", "status": "false" }, { "identifier": "GPL-1.0+", "name": "GNU General Public License v1.0 or later", "status": "false" }, { "identifier": "GPL-2.0+", "name": "GNU General Public License v2.0 or later", "status": "false" }, { "identifier": "GPL-2.0-with-autoconf-exception", "name": "GNU General Public License v2.0 w/Autoconf exception", "status": "false" }, { "identifier": "GPL-2.0-with-bison-exception", "name": "GNU General Public License v2.0 w/Bison exception", "status": "false" }, { "identifier": "GPL-2.0-with-classpath-exception", "name": "GNU General Public License v2.0 w/Classpath exception", "status": "false" }, { "identifier": "GPL-2.0-with-font-exception", "name": "GNU General Public License v2.0 w/Font exception", "status": "false" }, { "identifier": "GPL-2.0-with-GCC-exception", "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", "status": "false" }, { "identifier": "GPL-3.0+", "name": "GNU General Public License v3.0 or later", "status": "false" }, { "identifier": "GPL-3.0-with-autoconf-exception", "name": "GNU General Public License v3.0 w/Autoconf exception", "status": "false" }, { "identifier": "GPL-3.0-with-GCC-exception", "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", "status": "false" }, { "identifier": "LGPL-2.1+", "name": "GNU Lesser General Public License v2.1 or later", "status": "false" }, { "identifier": "LGPL-3.0+", "name": "GNU Lesser General Public License v3.0 or later", "status": "false" }, { "identifier": "LGPL-2.0+", "name": "GNU Library General Public License v2 or later", "status": "false" }, { "identifier": "StandardML-NJ", "name": "Standard ML of New Jersey License", "status": "false" }, { "identifier": "WXwindows", "name": "wxWindows Library License", "status": "false" } ] ================================================ FILE: src/main/resources/at/porscheinformatik/sonarqube/licensecheck/licensemapping/default_license_mapping.json ================================================ [ { "regex": "Apple License", "license": "AML" }, { "regex": ".*Apache.*1\\.1.*", "license": "Apache-1.1" }, { "regex": ".*AL.*2.*", "license": "Apache-2.0" }, { "regex": ".*ASF.*2.*", "license": "Apache-2.0" }, { "regex": ".*ASL.*", "license": "Apache-2.0" }, { "regex": ".*Apache.*2.*", "license": "Apache-2.0" }, { "regex": ".*BSD.*", "license": "BSD-3-Clause" }, { "regex": "^CC0.*$", "license": "CC0-1.0" }, { "regex": ".*CDDL.*", "license": "CDDL-1.0" }, { "regex": ".*CDDL.*GPL.*", "license": "(CDDL-1.0 OR GPL-2.0)" }, { "regex": ".*CDDL.*1.1.*", "license": "CDDL-1.1" }, { "regex": ".*Common Development and Distribution License.*", "license": "CDDL-1.1" }, { "regex": ".*Common Public License.*", "license": "CPL-1.0" }, { "regex": ".*(EPL|Eclipse Public License).*", "license": "EPL-1.0" }, { "regex": ".*LGPL.*2\\.1.*", "license": "LGPL-2.1" }, { "regex": ".*Lesser.*Public License.*2\\.1.*", "license": "LGPL-2.1" }, { "regex": ".*LGPL.*", "license": "LGPL-3.0" }, { "regex": ".*Lesser Public License.*", "license": "LGPL-3.0" }, { "regex": ".*MIT.*", "license": "MIT" }, { "regex": ".*MPL.*1\\.1.*", "license": "MPL-1.1" }, { "regex": ".*Mozilla Public License.*", "license": "MPL-1.1" }, { "regex": ".*PostgreSQL License.*", "license": "PostgreSQL" }, { "regex": ".*Eclipse.Distribution.License.*.1.0.*", "license": "BSD-3-Clause" }, { "regex": ".*GPL2.*CPE.*", "license": "GPL-2.0-with-classpath-exception" }, { "regex": ".*JSON.*License.*", "license": "JSON" }, { "regex": ".*W3C.Software.*License.*", "license": "W3C" } ] ================================================ FILE: src/main/web/configuration/configuration.jsx ================================================ import { useEffect, useState } from "react"; import "../shared/styles.css"; import DependencyMappingsPage from "./dependency-mappings-page"; import LicenseMappingsPage from "./license-mappings-page"; import LicensesPage from "./licenses-page"; import ProjectLicensesPage from "./project-licenses-page"; const Configuration = () => { const [currentRoute, setCurrentRoute] = useState("licenses"); useEffect(() => { const result = window.location.search.match(/category=([^&=]*)/); if (result && result.length > 1) { setCurrentRoute(result[1]); } }, []); const activateCategory = (event, route) => { event.preventDefault(); setCurrentRoute(route); window.history.pushState({}, document.title, `?category=${route}`); }; return (

{currentRoute === "licenses" && } {currentRoute === "dependency-mappings" && } {currentRoute === "license-mappings" && } {currentRoute === "project-licenses" && }
); }; export default Configuration; ================================================ FILE: src/main/web/configuration/dependency-mappings-page.jsx ================================================ import { Button, ButtonIcon, Checkbox, IconDelete, IconEdit, IconSearch, IconTriangleDown, IconTriangleUp, Label, Modal, TextInput, } from "@sonarsource/echoes-react"; import { useEffect, useState } from "react"; import { loadDependencyMappings, loadLicenses, saveDependencyMappings } from "./sonar-api"; const DependencyMappingsPage = () => { const [items, setItems] = useState([]); const [itemToDelete, setItemToDelete] = useState(null); const [itemToEdit, setItemToEdit] = useState(null); const [editMode, setEditMode] = useState(null); const [searchText, setSearchText] = useState(""); const [licenses, setLicenses] = useState([]); const [sortBy, setSortBy] = useState("key"); const [sortDirection, setSortDirection] = useState("asc"); useEffect(() => { load(); }, []); const load = () => { return Promise.all([ loadLicenses().then((l) => setLicenses(l)), loadDependencyMappings().then((md) => setItems(md)), ]); }; const findLicenseName = (license) => { const licenseItem = licenses.find((l) => l.id === license); return licenseItem ? licenseItem.name : "-"; }; const showAddDialog = () => { setItemToEdit({}); setEditMode("add"); }; const showEditDialog = (item) => { setItemToEdit({ ...item, old_key: item.key }); setEditMode("edit"); }; const cancelEdit = () => { setItemToEdit(null); }; const saveItems = (items) => { saveDependencyMappings(items).then(() => { loadDependencyMappings().then((md) => setItems(md)); setItemToEdit(null); setItemToDelete(null); }); }; const saveItem = (item) => { if (editMode === "add") { saveItems([...items, item]); } else { const newItems = [...items]; const itemToChange = newItems.find((i) => i.key === item.old_key); itemToChange.key = item.key; itemToChange.license = item.license; itemToChange.overwrite = item.overwrite; saveItems(newItems); } }; const showDeleteDialog = (item) => { setItemToDelete(item); }; const cancelDelete = () => { setItemToDelete(null); }; const deleteItem = (item) => { saveItems(items.filter((i) => i.key !== item.key)); }; const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; const sortedItems = [...items].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const displayedItems = !searchText ? sortedItems : sortedItems.filter( (item) => item.key.toLowerCase().includes(searchText.toLowerCase()) || item.license.toLowerCase().includes(searchText.toLowerCase()), ); return (

License Check - Dependency Mappings

Maps a dependency name/key (with regex) to a license
setSearchText(e.target.value)} prefix={} />
{displayedItems.map((item) => ( ))} {!displayedItems.length && ( )}
sort("key")} scope="col"> Key Regex {sortBy === "key" && (sortDirection === "asc" ? : )} sort("license")} scope="col"> License {sortBy === "license" && (sortDirection === "asc" ? : )} sort("overwrite")} scope="col"> Overwrite License {sortBy === "overwrite" && (sortDirection === "asc" ? : )} Actions
{item.key} {item.license} / {findLicenseName(item.license)} {item.overwrite === "true" ? "Yes" : "No"} showEditDialog(item)} /> {items.length > 1 && ( showDeleteDialog(item)} /> )}
No Maven dependencies available
{ if (!isOpen) { cancelEdit(); } }} primaryButton={} secondaryButton={ } content={ itemToEdit && (
setItemToEdit({ ...itemToEdit, key: e.target.value })} id="keyEdit" name="keyEdit" maxLength="255" />
setItemToEdit({ ...itemToEdit, overwrite: checked ? "true" : "false", }) } />
) } /> { if (!isOpen) { cancelDelete(); } }} description={`Are you sure you want to delete the Maven dependency mapping "${itemToDelete?.key}" / "${itemToDelete?.license}"?`} primaryButton={} secondaryButton={ } />
); }; export default DependencyMappingsPage; ================================================ FILE: src/main/web/configuration/license-mappings-page.jsx ================================================ import { Button, ButtonIcon, IconDelete, IconEdit, IconSearch, IconTriangleDown, IconTriangleUp, Label, Modal, TextInput, } from "@sonarsource/echoes-react"; import { useEffect, useState } from "react"; import { loadLicenseMappings, loadLicenses, saveLicenseMappings } from "./sonar-api"; const LicenseMappingsPage = () => { const [items, setItems] = useState([]); const [itemToDelete, setItemToDelete] = useState(null); const [itemToEdit, setItemToEdit] = useState(null); const [editMode, setEditMode] = useState(null); const [searchText, setSearchText] = useState(""); const [licenses, setLicenses] = useState([]); const [sortBy, setSortBy] = useState("regex"); const [sortDirection, setSortDirection] = useState("asc"); useEffect(() => { load(); }, []); const load = () => { return Promise.all([ loadLicenses().then((l) => setLicenses(l)), loadLicenseMappings().then((ml) => setItems(ml)), ]); }; const findLicenseName = (license) => { const licenseItem = licenses.find((l) => l.id === license); return licenseItem ? licenseItem.name : "-"; }; const showAddDialog = () => { setItemToEdit({}); setEditMode("add"); }; const showEditDialog = (item) => { setItemToEdit({ ...item, old_regex: item.regex }); setEditMode("edit"); }; const cancelEdit = () => { setItemToEdit(null); }; const saveItems = (items) => { saveLicenseMappings(items).then(() => { loadLicenseMappings().then((ml) => setItems(ml)); setItemToEdit(null); setItemToDelete(null); }); }; const saveItem = (item) => { if (editMode === "add") { saveItems([...items, item]); } else { const newItems = [...items]; const itemToChange = newItems.find((i) => i.regex === item.old_regex); itemToChange.regex = item.regex; itemToChange.license = item.license; saveItems(newItems); } }; const showDeleteDialog = (item) => { setItemToDelete(item); }; const cancelDelete = () => { setItemToDelete(null); }; const deleteItem = (item) => { saveItems(items.filter((i) => i.regex !== item.regex)); }; const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; const sortedItems = [...items].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const displayedItems = !searchText ? sortedItems : sortedItems.filter( (item) => item.regex.toLowerCase().includes(searchText.toLowerCase()) || item.license.toLowerCase().includes(searchText.toLowerCase()), ); return (

License Check - License Mappings

Maps a license name (with regex) to a license
setSearchText(e.target.value)} prefix={} />
{displayedItems.map((item) => ( ))} {!displayedItems.length && ( )}
sort("regex")} scope="col"> License Text Regex {sortBy === "regex" && (sortDirection === "asc" ? : )} sort("license")} scope="col"> License {sortBy === "license" && (sortDirection === "asc" ? : )} Actions
{item.regex} {item.license} / {findLicenseName(item.license)} showEditDialog(item)} /> {items.length > 1 && ( showDeleteDialog(item)} /> )}
No license mappings available
{ if (!isOpen) { cancelEdit(); } }} primaryButton={} secondaryButton={ } content={ itemToEdit && (
setItemToEdit({ ...itemToEdit, regex: e.target.value })} id="regexEdit" name="regexEdit" maxLength="255" />
) } /> { if (!isOpen) { cancelDelete(); } }} description={`Are you sure you want to delete the license mapping "${itemToDelete?.regex}" / "${itemToDelete?.license}"?`} primaryButton={} secondaryButton={ } />
); }; export default LicenseMappingsPage; ================================================ FILE: src/main/web/configuration/licenses-page.jsx ================================================ import { Button, ButtonIcon, Checkbox, IconCheckCircle, IconDelete, IconEdit, IconError, IconSearch, IconTriangleDown, IconTriangleUp, Label, Modal, TextInput, } from "@sonarsource/echoes-react"; import { useEffect, useState } from "react"; import { loadLicenses, saveLicenses } from "./sonar-api"; const LicensesPage = () => { const [items, setItems] = useState([]); const [itemToDelete, setItemToDelete] = useState(null); const [itemToEdit, setItemToEdit] = useState(null); const [editMode, setEditMode] = useState(null); const [searchText, setSearchText] = useState(""); const [sortBy, setSortBy] = useState("id"); const [sortDirection, setSortDirection] = useState("asc"); useEffect(() => { load(); }, []); const load = () => { return loadLicenses().then((l) => setItems(l)); }; const showAddDialog = () => { setItemToEdit({ allowed: false }); setEditMode("add"); }; const showEditDialog = (item) => { setItemToEdit({ ...item }); setEditMode("edit"); }; const cancelEdit = () => { setItemToEdit(null); }; const importSpdx = () => { fetch("https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json") .then((r) => r.json()) .then((data) => { const licenses = data.licenses.map((l) => ({ id: l.licenseId, name: l.name, allowed: false, })); saveItems(licenses); }); }; const saveItems = (items) => { saveLicenses(items).then(() => { load(); setItemToEdit(null); setItemToDelete(null); }); }; const saveItem = (item) => { if (editMode === "add") { saveItems([...items, item]); } else { const newItems = [...items]; const itemToChange = newItems.find((i) => i.id === item.id); if (itemToChange) { itemToChange.name = item.name; itemToChange.allowed = item.allowed; saveItems(newItems); } } }; const showDeleteDialog = (item) => { setItemToDelete(item); }; const cancelDelete = () => { setItemToDelete(null); }; const deleteItem = (item) => { saveItems(items.filter((i) => i.id !== item.id)); }; const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; const sortedItems = [...items].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const displayedItems = !searchText ? sortedItems : sortedItems.filter( (item) => item.name.toLowerCase().includes(searchText.toLowerCase()) || item.id.toLowerCase().includes(searchText.toLowerCase()), ); return (

License Check - Licenses

Add and administer licenses, allow or disallow globally.
{items.length === 0 ? (

Currently, you have no licenses defined. You can add all licenses from SPDX.

) : ( <>
setSearchText(e.target.value)} prefix={} >
{displayedItems.map((item) => ( ))}
sort("id")} scope="col"> Identifier {sortBy === "id" && (sortDirection === "asc" ? : )} sort("name")} scope="col"> Name {sortBy === "name" && (sortDirection === "asc" ? : )} sort("allowed")} scope="col"> Status {sortBy === "allowed" && (sortDirection === "asc" ? : )} Actions
{item.id} {item.name} {item.allowed === "true" ? ( ) : ( )}   {item.allowed === "true" ? "Allowed" : "Forbidden"} showEditDialog(item)} /> {items.length > 1 && ( showDeleteDialog(item)} /> )}
)} { if (!isOpen) { cancelEdit(); } }} primaryButton={} secondaryButton={ } content={ itemToEdit && (
setItemToEdit({ ...itemToEdit, id: e.target.value })} id="itemIdEdit" name="itemIdEdit" type="text" size="50" maxLength="255" />
setItemToEdit({ ...itemToEdit, name: e.target.value })} id="itemNameEdit" name="itemNameEdit" type="text" size="50" maxLength="255" />
setItemToEdit({ ...itemToEdit, allowed: checked ? "true" : "false", }) } />
) } >
deleteItem(itemToDelete)}>Delete} secondaryButton={ } isOpen={!!itemToDelete} onOpenChange={(isOpen) => { if (!isOpen) cancelDelete(); }} >
); }; export default LicensesPage; ================================================ FILE: src/main/web/configuration/project-licenses-page.jsx ================================================ import { Button, ButtonIcon, Checkbox, IconCheckCircle, IconDelete, IconEdit, IconError, IconSearch, IconTriangleDown, IconTriangleUp, Label, Modal, TextInput, } from "@sonarsource/echoes-react"; import { useEffect, useState } from "react"; import { loadLicenses, loadProjectLicenses, loadProjects, saveProjectLicenses } from "./sonar-api"; const ProjectLicensesPage = () => { const [items, setItems] = useState([]); const [itemToDelete, setItemToDelete] = useState(null); const [itemToEdit, setItemToEdit] = useState(null); const [editMode, setEditMode] = useState(null); const [searchText, setSearchText] = useState(""); const [licenses, setLicenses] = useState([]); const [projects, setProjects] = useState([]); const [sortBy, setSortBy] = useState("status"); const [sortDirection, setSortDirection] = useState("asc"); useEffect(() => { load(); }, []); const load = () => { return Promise.all([ loadLicenses().then((l) => setLicenses(l)), loadProjects().then((p) => setProjects(p)), loadProjectLicenses().then((pl) => setItems(pl)), ]); }; const findProjectName = (projectKey) => { const projectItem = projects.find((p) => p.key === projectKey); return projectItem ? projectItem.name : "-"; }; const findLicenseName = (license) => { const licenseItem = licenses.find((l) => l.id === license); return licenseItem ? licenseItem.name : "-"; }; const showAddDialog = () => { setItemToEdit({}); setEditMode("add"); }; const showEditDialog = (item) => { setItemToEdit({ ...item }); setEditMode("edit"); }; const cancelEdit = () => { setItemToEdit(null); }; const saveItems = (items) => { return saveProjectLicenses(items).then(() => { loadProjectLicenses().then((pl) => setItems(pl)); setItemToEdit(null); setItemToDelete(null); }); }; const saveItem = (item) => { if (editMode === "add") { saveItems([...items, item]); } else { const newItems = [...items]; const itemToChange = newItems.find( (i) => i.projectKey === item.projectKey && i.license === item.license, ); itemToChange.allowed = item.allowed; saveItems(newItems); } }; const showDeleteDialog = (item) => { setItemToDelete(item); }; const cancelDelete = () => { setItemToDelete(null); }; const deleteItem = (item) => { saveItems(items.filter((i) => i.projectKey !== item.projectKey || i.license !== item.license)); }; const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; const sortedItems = [...items].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const displayedItems = !searchText ? sortedItems : sortedItems.filter( (item) => item.projectKey.toLowerCase().includes(searchText.toLowerCase()) || item.license.toLowerCase().includes(searchText.toLowerCase()), ); return (

License Check - Project Licenses

Allow/disallow licences for specific projects.
setSearchText(e.target.value)} prefix={} />
{displayedItems.map((item) => ( ))} {!displayedItems.length && ( )}
sort("projectName")} scope="col"> Project {sortBy === "projectName" && (sortDirection === "asc" ? : )} sort("license")} scope="col"> License {sortBy === "license" && (sortDirection === "asc" ? : )} sort("status")} scope="col"> Status {sortBy === "status" && (sortDirection === "asc" ? : )} Actions
{findProjectName(item.projectKey)} {item.license} / {findLicenseName(item.license)} {item.allowed === "true" ? ( ) : ( )}   {item.allowed === "true" ? "Allowed" : "Forbidden"} showEditDialog(item)} /> showDeleteDialog(item)} />
No project licenses available
{ if (!isOpen) { cancelEdit(); } }} primaryButton={} secondaryButton={ } content={ itemToEdit && (
setItemToEdit({ ...itemToEdit, allowed: checked ? "true" : "false", }) } />
) } /> { if (!isOpen) { cancelDelete(); } }} description={`Are you sure you want to delete the project license "${findProjectName( itemToDelete?.projectKey, )}" / "${itemToDelete?.license}"?`} primaryButton={} secondaryButton={ } />
); }; export default ProjectLicensesPage; ================================================ FILE: src/main/web/configuration/sonar-api.js ================================================ import { KEYS } from "../property_keys"; function loadProjectPage(allProjects, pageIndex) { return window.SonarRequest.getJSON( `/api/components/search?qualifiers=TRK&ps=500&p=${pageIndex}`, ).then((response) => { const projects = response.components || []; allProjects = allProjects.concat(projects); if (response.paging && response.paging.total > pageIndex * 500) { return loadProjectPage(allProjects.concat(projects), pageIndex + 1); } else { return allProjects; } }); } export function loadProjects() { return loadProjectPage([], 1); } export function loadLicenses() { return window.SonarRequest.getJSON(`/api/settings/values?keys=${KEYS.LICENSE_SET}`).then( (response) => response.settings && response.settings.length > 0 ? response.settings[0].fieldValues : [], ); } export function saveLicenses(items) { return window.SonarRequest.post(`/api/settings/set`, { key: KEYS.LICENSE_SET, fieldValues: items.map((i) => JSON.stringify(i)), }); } export function loadProjectLicenses() { return window.SonarRequest.getJSON(`/api/settings/values?keys=${KEYS.PROJECT_LICENSE_SET}`).then( (response) => response.settings && response.settings.length > 0 ? response.settings[0].fieldValues : [], ); } export function saveProjectLicenses(items) { return window.SonarRequest.post(`/api/settings/set`, { key: KEYS.PROJECT_LICENSE_SET, fieldValues: items.map((i) => JSON.stringify(i)), }); } export function loadLicenseMappings() { return window.SonarRequest.getJSON(`/api/settings/values?keys=${KEYS.LICENSE_MAPPING}`).then( (response) => response.settings[0].fieldValues, ); } export function saveLicenseMappings(items) { return window.SonarRequest.post(`/api/settings/set`, { key: KEYS.LICENSE_MAPPING, fieldValues: items.map((i) => JSON.stringify(i)), }); } export function loadDependencyMappings() { return window.SonarRequest.getJSON(`/api/settings/values?keys=${KEYS.DEPENDENCY_MAPPING}`).then( (response) => response.settings && response.settings.length > 0 ? response.settings[0].fieldValues : [], ); } export function saveDependencyMappings(items) { return window.SonarRequest.post(`/api/settings/set`, { key: KEYS.DEPENDENCY_MAPPING, fieldValues: items.map((i) => JSON.stringify(i)), }); } ================================================ FILE: src/main/web/configuration.js ================================================ import { TooltipProvider } from "@sonarsource/echoes-react"; import { createRoot } from "react-dom/client"; import { IntlProvider } from "react-intl"; import Configuration from "./configuration/configuration"; window.registerExtension("licensecheck/configuration", function (options) { const root = createRoot(options.el); root.render( , ); return function () { root.unmount(); }; }); ================================================ FILE: src/main/web/dashboard/dashboard.jsx ================================================ import { Button, IconDownload } from "@sonarsource/echoes-react"; import saveAs from "file-saverjs"; import { useEffect, useState } from "react"; import "../shared/styles.css"; import Dependencies from "./dependencies"; import buildExcel from "./excel-builder"; import Licenses from "./licenses"; const Dashboard = ({ options }) => { const [licenses, setLicenses] = useState([]); const [dependencies, setDependencies] = useState([]); const component = options.component; useEffect(() => { const params = new URLSearchParams(window.location.search); const request = { component: component.key, metricKeys: "licensecheck.license,licensecheck.dependency", }; if (params.has("branch")) { request.branch = params.get("branch"); } else if (params.has("pullRequest")) { request.pullRequest = params.get("pullRequest"); } window.SonarRequest.getJSON("/api/measures/component", request).then((response) => { const processedLicenses = []; const processedDependencies = []; response.component.measures.forEach((measure) => { if (measure.metric === "licensecheck.license") { processedLicenses.push(...JSON.parse(measure.value)); } else if (measure.metric === "licensecheck.dependency") { processedDependencies.push(...JSON.parse(measure.value)); } }); processedDependencies.forEach((dependency) => { dependency.status = "Unknown"; processedLicenses.forEach((license) => { if (dependency.license === license.identifier) { dependency.status = license.status === "true" ? "Allowed" : "Forbidden"; } }); }); setLicenses(processedLicenses); setDependencies(processedDependencies); }); }, [component.key]); const exportExcel = () => { const blob = new Blob([buildExcel(dependencies, licenses)], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); saveAs(blob, `license-check-${component.key}.xls`); }; return (

License Check

View and manage project dependencies and their licenses
); }; export default Dashboard; ================================================ FILE: src/main/web/dashboard/dependencies.jsx ================================================ import { IconCheckCircle, IconError, IconTriangleDown, IconTriangleUp, } from "@sonarsource/echoes-react"; import { useState } from "react"; const Dependencies = ({ dependencies }) => { const [sortBy, setSortBy] = useState("name"); const [sortDirection, setSortDirection] = useState("asc"); const sortedDependencies = [...dependencies].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -1 * modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; return (

Dependencies

{sortedDependencies.map((dependency) => ( ))}
Here you see all project dependencies from Maven (including transitive) and NPM.
sort("name")} scope="col"> Name {sortBy === "name" && (sortDirection === "asc" ? : )} sort("version")} scope="col"> Version {sortBy === "version" && (sortDirection === "asc" ? : )} sort("license")} scope="col"> License {sortBy === "license" && (sortDirection === "asc" ? : )} sort("status")} scope="col"> Status {sortBy === "status" && (sortDirection === "asc" ? : )}
{dependency.name} {dependency.version} {dependency.license} {dependency.status === "Allowed" ? ( ) : dependency.status === "Forbidden" ? ( ) : ( )}   {dependency.status}
); }; export default Dependencies; ================================================ FILE: src/main/web/dashboard/excel-builder.js ================================================ export default function buildExcel(dependencies, licenses) { let excelFile = ` `; excelFile += buildDependenciesWorksheet(dependencies); excelFile += buildLicensesWorksheet(licenses); excelFile += ""; return excelFile; } function buildDependenciesWorksheet(dependencies) { let result = ` Name Version License Status `; dependencies.forEach((dependency) => { result += ` ${dependency.name} ${dependency.version} ${dependency.license} ${dependency.status} `; }); return result + "
"; } function buildLicensesWorksheet(licenses) { let result = ` Identifier Name Status `; licenses.forEach((license) => { result += ` ${license.identifier} ${license.name} ${license.status === "true" ? "Allowed" : "Forbidden"} `; }); return result + "
"; } ================================================ FILE: src/main/web/dashboard/licenses.jsx ================================================ import { IconCheckCircle, IconError, IconTriangleDown, IconTriangleUp, } from "@sonarsource/echoes-react"; import { useState } from "react"; const Licenses = ({ licenses }) => { const [sortBy, setSortBy] = useState("identifier"); const [sortDirection, setSortDirection] = useState("asc"); const sortedLicenses = [...licenses].sort((a, b) => { const modifier = sortDirection === "desc" ? -1 : 1; if (a[sortBy] < b[sortBy]) return -1 * modifier; if (a[sortBy] > b[sortBy]) return modifier; return 0; }); const sort = (param) => { if (param === sortBy) { setSortDirection(sortDirection === "asc" ? "desc" : "asc"); } setSortBy(param); }; return (

Licenses

{sortedLicenses.map((license) => ( ))}
This is a list of all licenses used in any dependencies listed below.
sort("identifier")} scope="col"> Identifier {sortBy === "identifier" && (sortDirection === "asc" ? : )} sort("name")} scope="col"> Name {sortBy === "name" && (sortDirection === "asc" ? : )} sort("status")} scope="col"> Status {sortBy === "status" && (sortDirection === "asc" ? : )}
{license.identifier} {license.name} {license.status === "true" ? ( ) : ( )}   {license.status === "true" ? "Allowed" : "Forbidden"}
); }; export default Licenses; ================================================ FILE: src/main/web/dashboard.js ================================================ import { TooltipProvider } from "@sonarsource/echoes-react"; import { createRoot } from "react-dom/client"; import { IntlProvider } from "react-intl"; import Dashboard from "./dashboard/dashboard"; window.registerExtension("licensecheck/dashboard", function (options) { const root = createRoot(options.el); options.el.style.overflowY = "auto"; options.el.style.overflowX = "hidden"; options.el.style.width = "100dvw"; root.render( , ); return function () { root.unmount(); }; }); ================================================ FILE: src/main/web/property_keys.js ================================================ export const KEYS = { LICENSE_SET: "licensecheck.license-set", PROJECT_LICENSE_SET: "licensecheck.project-license-set", LICENSE_MAPPING: "licensecheck.license-mapping", DEPENDENCY_MAPPING: "licensecheck.dep-mapping", }; ================================================ FILE: src/main/web/shared/styles.css ================================================ table.sqlc-data { width: 100%; border-collapse: collapse; } table.sqlc-data th { color: rgb(29, 33, 47); font: var(--echoes-typography-text-default-semi-bold); cursor: pointer; } table.sqlc-data th, table.sqlc-data td { border-top: 3px solid rgb(235, 235, 235); padding: 1rem 0.5rem; vertical-align: middle; text-align: left; } table.sqlc-data caption { color: rgb(111, 119, 143); text-align: left; padding: 0.5rem 0; margin-bottom: 0.5rem; } table.sqlc-data tr:hover td { background-color: rgb(244, 246, 255); } .arrow { display: inline-block; width: 0; height: 0; margin-left: 4px; vertical-align: middle; } .arrow_up { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 4px solid rgb(29, 33, 47); } .arrow_down { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid rgb(29, 33, 47); } .sqlc-page { box-sizing: border-box; max-width: 1680px; min-height: 100%; padding: var(--echoes-dimension-space-300); } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/DependencyTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class DependencyTest { private static final String DEPENDENCIES_JSON = "[{\"name\":\"another\",\"version\":\"2.1.0\",\"license\":\"MIT\",\"lang\":\"java\"}," + "{\"name\":\"library\",\"version\":\"1.0.0\",\"license\":\"Apache-2.0\",\"lang\":\"java\"}]"; private static final Dependency DEP1 = new Dependency("another", "2.1.0", "MIT"); private static final Dependency DEP2 = new Dependency("library", "1.0.0", "Apache-2.0"); @Test public void createString() { String dependenciesJson = Dependency.createString(asList(DEP2, DEP1)); assertThat(dependenciesJson, equalTo(DEPENDENCIES_JSON)); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetricsTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class LicenseCheckMetricsTest { @Test public void getMetrics() { assertThat(new LicenseCheckMetrics().getMetrics(), notNullValue()); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinitionTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import org.sonar.api.web.page.Context; public class LicenseCheckPageDefinitionTest { @Test public void define() { LicenseCheckPageDefinition definition = new LicenseCheckPageDefinition(); Context context = new Context(); definition.define(context); assertThat(context.getPages().size(), is(2)); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPluginTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import org.junit.Test; import org.sonar.api.Plugin; public class LicenseCheckPluginTest { @Test public void define() { Plugin.Context context = mock(Plugin.Context.class); LicenseCheckPlugin plugin = new LicenseCheckPlugin(); plugin.define(context); verify(context).addExtensions(any()); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import org.sonar.api.server.rule.RulesDefinition; public class LicenseCheckRulesDefinitionTest { @Test public void define() { RulesDefinition.Context context = new RulesDefinition.Context(); new LicenseCheckRulesDefinition().define(context); assertThat(context.repositories().size(), is(6)); for (RulesDefinition.Repository repository : context.repositories()) { assertThat(repository.rules().size(), is(2)); } } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; 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_KOTLIN; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckRulesDefinition.RULE_REPO_KEY_TS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import at.porscheinformatik.sonarqube.licensecheck.license.License; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import java.util.Collections; import java.util.Optional; import java.util.Set; import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.Test; import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.measure.NewMeasure; import org.sonar.api.config.Configuration; import org.sonar.api.scanner.fs.InputProject; public class LicenseCheckSensorTest { public static final Set DEPENDENCIES = Collections.singleton( new Dependency("name", "1.0.0", "MIT") ); private static final Set LICENSES = Collections.singleton( new License("MIT", "MIT", true) ); @Test public void describe() { Configuration configuration = createConfiguration(); LicenseCheckSensor sensor = new LicenseCheckSensor(configuration, null, null); SensorDescriptor descriptor = mock(SensorDescriptor.class); when(descriptor.name(anyString())).thenReturn(descriptor); sensor.describe(descriptor); verify(descriptor).name("License Check"); verify(descriptor).createIssuesForRuleRepositories( RULE_REPO_KEY, RULE_REPO_KEY_JS, RULE_REPO_KEY_TS, RULE_REPO_KEY_GROOVY, RULE_REPO_KEY_KOTLIN ); } @Test public void execute() throws IllegalAccessException { Configuration configuration = createConfiguration(); ValidateLicenses validateLicenses = mock(ValidateLicenses.class); when(validateLicenses.validateLicenses(any(), any())).thenReturn(DEPENDENCIES); when(validateLicenses.getUsedLicenses(any(), any())).thenReturn(LICENSES); LicenseMappingService licenseMappingService = mock(LicenseMappingService.class); LicenseCheckSensor sensor = new LicenseCheckSensor( configuration, validateLicenses, licenseMappingService ); Scanner mockScanner = mock(Scanner.class); when(mockScanner.scan(any())).thenReturn(DEPENDENCIES); Scanner[] scanners = new Scanner[] { mockScanner }; FieldUtils.writeField(sensor, "scanners", scanners, true); SensorContext context = mock(SensorContext.class); InputProject project = mock(InputProject.class); InputModule module = mock(InputModule.class); when(module.key()).thenReturn("my-project"); when(context.module()).thenReturn(module); when(project.key()).thenReturn("my-project"); when(context.project()).thenReturn(project); NewMeasure measure = mock(NewMeasure.class); when(measure.forMetric(any())).thenReturn(measure); when(measure.withValue(any())).thenReturn(measure); when(measure.on(any())).thenReturn(measure); when(context.newMeasure()).thenReturn(measure); sensor.execute(context); verify(measure, times(7)).save(); // 5 metrics + dependencies + licenses } private Configuration createConfiguration() { Configuration configuration = mock(Configuration.class); when( configuration.getBoolean(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS) ).thenReturn(Optional.empty()); when(configuration.getBoolean(LicenseCheckPropertyKeys.ACTIVATION_KEY)).thenReturn( Optional.of(true) ); return configuration; } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.MatcherAssert.assertThat; import at.porscheinformatik.sonarqube.licensecheck.license.License; import java.util.List; import org.junit.Test; public class LicenseTest { private static final String LICENSES_JSON = "[{\"name\":\"Apache 2.0\",\"identifier\":\"Apache-2.0\",\"status\":\"true\"}," + "{\"name\":\"MIT License\",\"identifier\":\"MIT\",\"status\":\"false\"}]"; private static final String LICENSES_STRING = "Apache 2.0~Apache-2.0~true;MIT License~MIT~false;"; private static final License LIC1 = new License("Apache 2.0", "Apache-2.0", "true"); private static final License LIC2 = new License("MIT License", "MIT", "false"); @Test public void createJsonString() { String dependenciesJson = License.createJsonString(asList(LIC2, LIC1)); assertThat(dependenciesJson, equalTo(LICENSES_JSON)); } @Test public void fromStringOld() { List licenses = License.fromString(LICENSES_STRING); assertThat(licenses, hasItems(LIC1, LIC2)); } @Test public void fromStringNew() { List licenses = License.fromString(LICENSES_JSON); assertThat(licenses, hasItems(LIC1, LIC2)); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/PackageJsonDependencyScannerTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import at.porscheinformatik.sonarqube.licensecheck.npm.PackageJsonDependencyScanner; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; import java.util.stream.Collectors; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.SensorContext; public class PackageJsonDependencyScannerTest { private static final Path RESOURCE_FOLDER = Path.of("src/test/resources"); private static final Logger LOGGER = LoggerFactory.getLogger( PackageJsonDependencyScannerTest.class ); private SensorContext createContext(Path moduleBaseDir) { SensorContext context = mock(SensorContext.class); DefaultFileSystem fileSystem = new DefaultFileSystem(moduleBaseDir); try { // Provide all **source** files in the moduleBaseDir Files.walk(moduleBaseDir).forEach(path -> { try { if (Files.isDirectory(path)) { return; } if (path.toString().contains("/node_modules/")) { LOGGER.info( "Ignoring file {}, because it is in a node_modules folder", path ); return; } var lines = Files.readAllLines(path); InputFile inputFile = TestInputFileBuilder.create( "test", moduleBaseDir.toFile(), path.toFile() ) // Required to get correct metadata .setContents(lines.stream().collect(Collectors.joining("\n"))) // Otherwise .inputStream() will throw a NPE .setCharset(Charset.forName("UTF-8")) .build(); LOGGER.info("Added {} to the fs", path); fileSystem.add(inputFile); } catch (Exception e) { throw new RuntimeException(e); } }); } catch (Exception e) { throw new RuntimeException(e); } when(context.fileSystem()).thenReturn(fileSystem); return context; } @Test public void testHappyPath() { Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("example")) ); assertThat(dependencies, hasSize(2)); assertThat( dependencies, containsInAnyOrder( new Dependency("angular", "1.5.0", "MIT"), new Dependency("arangojs", "5.6.0", "Apache-2.0") ) ); } @Test public void testTransitive() { Set dependencies = createScanner(true).scan( createContext(RESOURCE_FOLDER.resolve("example")) ); assertThat(dependencies, hasSize(4)); assertThat( dependencies, containsInAnyOrder( new Dependency("angular", "1.5.0", "MIT"), new Dependency("arangojs", "5.6.0", "Apache-2.0"), new Dependency("linkedlist", "1.0.1", "LGPLv3"), new Dependency("retry", "0.10.1", "MIT") ) ); } @Test public void testPackageJsonNotInBaseDir() throws Exception { Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("example_nested")) ); assertThat( dependencies, containsInAnyOrder( new Dependency("angular", "1.5.0", "MIT"), new Dependency("arangojs", "5.6.0", "Apache-2.0") ) ); } @Test public void testNoPackageJson() { Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("no_package_json")) ); assertThat(dependencies, hasSize(0)); } @Test public void testNoNodeModules() { Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("example/node_modules/arangojs")) ); assertThat(dependencies, hasSize(0)); } @Test public void testLicenseInDeprecatedLicenseFormat() { final Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("deprecated_project")) ); assertEquals(1, dependencies.size()); final Dependency expectedDependency = new Dependency("dynamic-dedupe", "0.3.0", "MIT"); assertEquals(expectedDependency, dependencies.toArray()[0]); } @Test public void testLicenseInDeprecatedLicensesFormat() { final Set dependencies = createScanner().scan( createContext(RESOURCE_FOLDER.resolve("deprecated_multilicense_project")) ); assertEquals(1, dependencies.size()); final Dependency expectedDependency = new Dependency( "some-module", "1.7.1", "(MIT OR LGPLv3)" ); assertEquals(expectedDependency, dependencies.toArray()[0]); } private Scanner createScanner() { return createScanner(false); } private Scanner createScanner(boolean resolveTransitiveDeps) { LicenseMappingService licenseMappingService = mock(LicenseMappingService.class); when(licenseMappingService.mapLicense(anyString())).thenCallRealMethod(); return new PackageJsonDependencyScanner(licenseMappingService, resolveTransitiveDeps); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicensesTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping; import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingService; import at.porscheinformatik.sonarqube.licensecheck.license.License; import at.porscheinformatik.sonarqube.licensecheck.license.LicenseService; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.scanner.fs.InputProject; public class ValidateLicensesTest { private static final License APACHE_LICENSE = new License("Apache-2.0", "Apache-2.0", "true"); private ValidateLicenses validateLicenses; private ProjectDefinition projectDefinition; private InputProject module; private DependencyMappingService dependencyMappingService; @Before public void setup() { module = mock(InputProject.class); when(module.key()).thenReturn("at.porscheinformatik.demo:demo"); final LicenseService licenseService = mock(LicenseService.class); when(licenseService.getLicenses(module)).thenReturn( Arrays.asList( new License("MIT", "MIT", "false"), new License("LGPL is fantastic", "LGPL", "true"), APACHE_LICENSE ) ); dependencyMappingService = mock(DependencyMappingService.class); validateLicenses = new ValidateLicenses(licenseService, dependencyMappingService); } private SensorContext createContext() { SensorContext context = mock(SensorContext.class); DefaultInputModule inputModule = mock(DefaultInputModule.class); when(context.project()).thenReturn(module); when(inputModule.definition()).thenReturn(projectDefinition); when(context.module()).thenReturn(inputModule); return context; } @Test public void licenseNotAllowed() { SensorContext context = createContext(); NewIssue issue = new DefaultIssue( mock(DefaultInputProject.class), mock(SensorStorage.class) ); when(context.newIssue()).thenReturn(issue); Dependency thing = new Dependency("thing", "1.0", "MIT"); thing.setInputComponent(context.project()); validateLicenses.validateLicenses(deps(thing), context); verify(context).newIssue(); assertThat( issue.toString(), containsString(LicenseCheckRulesDefinition.RULE_NOT_ALLOWED_LICENSE_KEY) ); } @Test public void licenseAllowed() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps( new Dependency("thing", "1.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_JS), new Dependency("another", "2.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_JS) ), context ); verify(context, never()).newIssue(); } @Test public void licenseAllowed_scala() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps( new Dependency( "thing", "1.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_SCALA ), new Dependency( "another", "2.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_SCALA ) ), context ); verify(context, never()).newIssue(); } @Test public void licenseAllowed_kotlin() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps( new Dependency( "thing", "1.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_KOTLIN ), new Dependency( "another", "2.0", "Apache-2.0", LicenseCheckRulesDefinition.LANG_KOTLIN ) ), context ); verify(context, never()).newIssue(); } // (LGPL OR Apache-2.0) AND (LGPL OR Apache-2.0) @Test public void checkSpdxOrCombination() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps( new Dependency("another", "2.0", "(LGPL OR Apache-2.0)"), new Dependency("thing", "1.0", "(MIT OR Apache-2.0)") ), context ); verify(context, never()).newIssue(); } @Test public void checkSpdxSeveralOrCombination() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps(new Dependency("thing", "1.0", "(Apache-2.0 OR MIT OR Apache-2.0 OR LGPL)")), context ); verify(context, never()).newIssue(); } @Test public void checkSpdxAndCombination() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps(new Dependency("thing", "1.0", "(LGPL AND Apache-2.0)")), context ); verify(context, never()).newIssue(); } @Test public void checkSpdxAndCombinationNotAllowed() { SensorContext context = createContext(); NewIssue issue = new DefaultIssue( mock(DefaultInputProject.class), mock(SensorStorage.class) ); when(context.newIssue()).thenReturn(issue); Dependency thing = new Dependency("thing", "1.0", "(Apache-2.0 AND MIT)"); thing.setInputComponent(context.project()); validateLicenses.validateLicenses( deps( new Dependency("another", "2.0", "LGPL", LicenseCheckRulesDefinition.LANG_JAVA), thing ), context ); verify(context).newIssue(); assertThat( issue.toString(), containsString(LicenseCheckRulesDefinition.RULE_NOT_ALLOWED_LICENSE_KEY) ); } @Test public void checkSpdxAndCombinationNotFound() { SensorContext context = createContext(); NewIssue issue = new DefaultIssue( mock(DefaultInputProject.class), mock(SensorStorage.class) ); when(context.newIssue()).thenReturn(issue); Dependency thing = new Dependency( "thing", "1.0", "(Apache-2.0 AND Apache-1.1)", LicenseCheckRulesDefinition.LANG_KOTLIN ); thing.setInputComponent(context.project()); validateLicenses.validateLicenses(deps(thing), context); verify(context).newIssue(); assertThat(issue.toString(), containsString(LicenseCheckRulesDefinition.RULE_UNLISTED_KEY)); } // LGPL OR Apache-2.0 AND MIT @Test public void checkSpdxOrAndCombination() { SensorContext context = createContext(); validateLicenses.validateLicenses( deps(new Dependency("thing", "1.0", "(LGPL OR (Apache-2.0 AND MIT))")), context ); verify(context, never()).newIssue(); } @Test public void licenseNull() { SensorContext context = createContext(); NewIssue issue = new DefaultIssue( mock(DefaultInputProject.class), mock(SensorStorage.class) ); when(context.newIssue()).thenReturn(issue); Dependency thing = new Dependency( "thing", "1.0", null, LicenseCheckRulesDefinition.LANG_JS ); thing.setInputComponent(context.project()); validateLicenses.validateLicenses(deps(thing), context); verify(context).newIssue(); assertThat(issue.toString(), containsString(LicenseCheckRulesDefinition.RULE_UNLISTED_KEY)); } @Test public void licenseUnknown() { SensorContext context = createContext(); NewIssue issue = new DefaultIssue( mock(DefaultInputProject.class), mock(SensorStorage.class) ); when(context.newIssue()).thenReturn(issue); Dependency thing = new Dependency("thing", "1.0", "Mamamia"); thing.setInputComponent(context.project()); validateLicenses.validateLicenses(deps(thing), context); verify(context).newIssue(); assertThat(issue.toString(), containsString(LicenseCheckRulesDefinition.RULE_UNLISTED_KEY)); } @Test public void getUsedLicenses() { assertThat(validateLicenses.getUsedLicenses(deps(), module).size(), is(0)); Set usedLicensesApache = validateLicenses.getUsedLicenses( deps( new Dependency("thing", "1.0", "Apache-2.0"), new Dependency("another", "2.0", "Apache-2.0") ), module ); assertThat(usedLicensesApache.size(), is(1)); assertThat(usedLicensesApache, hasItem(APACHE_LICENSE)); } @Test public void dependencyMapping() { when(dependencyMappingService.getDependencyMappings()).thenReturn( Collections.singletonList( new DependencyMapping("^thing$", APACHE_LICENSE.getIdentifier(), false) ) ); Set deps = validateLicenses.validateLicenses( deps(new Dependency("thing", "1.0", null)), createContext() ); assertThat(deps.size(), is(1)); assertThat(deps.iterator().next().getLicense(), equalTo(APACHE_LICENSE.getIdentifier())); } private static Set deps(Dependency... dependencies) { final Set dependencySet = new HashSet<>(); Collections.addAll(dependencySet, dependencies); return dependencySet; } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingServiceTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.dependencymapping; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.DEPENDENCY_MAPPING; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_KEY; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_OVERWRITE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.List; import java.util.Optional; import org.junit.Test; import org.sonar.api.config.Configuration; public class DependencyMappingServiceTest { @Test public void loadConfiguration() { Configuration configuration = mock(Configuration.class); when(configuration.getStringArray(DEPENDENCY_MAPPING)).thenReturn( new String[] { "1", "2" } ); when(configuration.get(any())).thenReturn(Optional.empty()); when(configuration.getBoolean(any())).thenReturn(Optional.empty()); when(configuration.get(DEPENDENCY_MAPPING + ".1." + FIELD_KEY)).thenReturn( Optional.of("kee") ); when(configuration.get(DEPENDENCY_MAPPING + ".1." + FIELD_LICENSE)).thenReturn( Optional.of("MIT") ); when(configuration.getBoolean(DEPENDENCY_MAPPING + ".1." + FIELD_OVERWRITE)).thenReturn( Optional.of(true) ); DependencyMappingService dependencyMappingService = new DependencyMappingService( configuration ); List dependencyMappings = dependencyMappingService.getDependencyMappings(); assertThat(dependencyMappings.size(), is(2)); assertThat(dependencyMappings.get(0), is(new DependencyMapping("kee", "MIT", true))); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.gradle; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import at.porscheinformatik.sonarqube.licensecheck.Dependency; import at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.config.Configuration; public class GradleDependencyScannerTest { private SensorContext createContext(File folder) { SensorContext context = mock(SensorContext.class); FileSystem fileSystem = mock(FileSystem.class); Configuration config = mock(Configuration.class); when(fileSystem.baseDir()).thenReturn(folder); when(fileSystem.workDir()).thenReturn(folder); when(config.get(anyString())).thenReturn(Optional.empty()); when(context.fileSystem()).thenReturn(fileSystem); when(context.config()).thenReturn(config); return context; } @Test public void testScannerWithMissingJsonFile() { GradleDependencyScanner scanner = new GradleDependencyScanner(mockLicenseService()); Set dependencies = scanner.scan(createContext(new File("/abc"))); assertEquals(0, dependencies.size()); } @Test public void testScanner() { GradleDependencyScanner scanner = new GradleDependencyScanner(mockLicenseService()); Path resourceDirectory = Paths.get("src", "test", "resources"); String absolutePath = resourceDirectory.toFile().getAbsolutePath(); Set dependencies = scanner.scan(createContext(new File(absolutePath))); assertEquals(43, dependencies.size()); } @Test public void testScannerWithConfiguredDirectory() { GradleDependencyScanner scanner = new GradleDependencyScanner(mockLicenseService()); Path resourceDirectory = Paths.get("src", "test", "resources"); String absolutePath = resourceDirectory.toFile().getAbsolutePath(); SensorContext context = createContext(new File(absolutePath)); when(context.config().get(LicenseCheckPropertyKeys.GRADLE_JSON_REPORT_PATH)).thenReturn( Optional.of("build/my-reports/license-details.json") ); Set dependencies = scanner.scan(context); assertEquals(43, dependencies.size()); } private LicenseMappingService mockLicenseService() { Map licenseMap = new HashMap<>(); licenseMap.put(Pattern.compile(".*Apache.*2.*"), "Apache-2.0"); LicenseMappingService licenseService = Mockito.mock(LicenseMappingService.class); when(licenseService.getLicenseMap()).thenReturn(licenseMap); return licenseService; } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingServiceTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.licensemapping; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_MAPPING; import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_REGEX; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; import org.junit.Test; import org.sonar.api.config.Configuration; public class LicenseMappingServiceTest { @Test public void getLicenseMap() { LicenseMappingService service = createService(); Map licenseMap = service.getLicenseMap(); assertThat(licenseMap.size(), is(2)); } @Test public void mapLicense() { LicenseMappingService service = createService(); String license = service.mapLicense("Apache Software License, 2.0"); assertThat(license, is("ASL2")); } private LicenseMappingService createService() { Configuration configuration = mock(Configuration.class); when(configuration.getStringArray(LICENSE_MAPPING)).thenReturn(new String[] { "1", "2" }); when(configuration.get(LICENSE_MAPPING + ".1." + FIELD_REGEX)).thenReturn( Optional.of("MIT") ); when(configuration.get(LICENSE_MAPPING + ".1." + FIELD_LICENSE)).thenReturn( Optional.of("MIT") ); when(configuration.get(LICENSE_MAPPING + ".2." + FIELD_REGEX)).thenReturn( Optional.of("^Apache.*2.*$") ); when(configuration.get(LICENSE_MAPPING + ".2." + FIELD_LICENSE)).thenReturn( Optional.of("ASL2") ); return new LicenseMappingService(configuration); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.licensemapping; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; import at.porscheinformatik.sonarqube.licensecheck.utils.IOUtils; import java.io.IOException; import java.util.HashSet; import java.util.List; import org.junit.Test; public class LicenseMappingTest { @Test public void loadFromJson() throws IOException { String licenseMappingListString = IOUtils.readToString( LicenseMapping.class.getResourceAsStream("default_license_mapping.json") ); List licenseMappings = LicenseMapping.fromString(licenseMappingListString); assertThat(licenseMappings, hasItem(new LicenseMapping("Apple License", "AML"))); } @Test public void compare() { LicenseMapping lm = new LicenseMapping("b", "A"); LicenseMapping lm2 = new LicenseMapping("a", "B"); assertThat(lm.compareTo(lm2), lessThan(0)); } @Test public void hash() { HashSet set = new HashSet<>(); set.add(new LicenseMapping("x", "X")); set.add(new LicenseMapping("x", "X")); assertThat(set.size(), is(1)); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DependencyMappingScannerTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import at.porscheinformatik.sonarqube.licensecheck.Dependency; import at.porscheinformatik.sonarqube.licensecheck.Scanner; import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.sensor.SensorContext; public class DependencyMappingScannerTest { @Test public void testLicensesAreFound() { File moduleDir = new File("."); Scanner scanner = new MavenDependencyScanner(mockLicenseService()); // - Set dependencies = scanner.scan(createContext(moduleDir)); assertThat(dependencies.size(), Matchers.greaterThan(0)); // - for (Dependency dep : dependencies) { if ("org.apache.commons:commons-lang3".equals(dep.getName())) { assertThat(dep.getLicense(), is("Apache-2.0")); } else if ("org.codehaus.plexus:plexus-utils".equals(dep.getName())) { assertThat(dep.getLicense(), is("Apache-2.0")); } } } @Test public void testNullMavenProjectDependencies() throws IOException { LicenseMappingService licenseService = Mockito.mock(LicenseMappingService.class); Scanner scanner = new MavenDependencyScanner(licenseService); File moduleDir = Files.createTempDirectory("lala").toFile(); moduleDir.deleteOnExit(); Set dependencies = scanner.scan(createContext(moduleDir)); assertThat(dependencies.size(), is(0)); } @Test public void testFindDependency() { String jarFilePath = new File("src/test/resources/test.jar").getAbsolutePath(); Dependency dependency = MavenDependencyScanner.findDependency( "at.porscheinformatik.test:test:jar:1.2.3:compile:" + jarFilePath + " -- module test (auto)" ); assertThat(dependency.getName(), is("at.porscheinformatik.test:test")); assertThat(dependency.getVersion(), is("1.2.3")); assertThat(dependency.getPomPath(), endsWith("test.pom")); } @Test public void testFindDependencyWithClassifier() { String jarFilePath = new File("src/test/resources/test-sources.jar").getAbsolutePath(); Dependency dependency = MavenDependencyScanner.findDependency( "at.porscheinformatik.test:test:jar:sources:2.2:compile:" + jarFilePath + " -- module test (auto)" ); assertThat(dependency.getName(), is("at.porscheinformatik.test:test")); assertThat(dependency.getVersion(), is("2.2")); assertThat(dependency.getPomPath(), endsWith("test.pom")); } @Test public void testSettingsFromMaven() { String oldVal = System.getProperty("sun.java.command"); try { System.setProperty( "sun.java.command", "thisisatest -X -s src/test/resources/settings-does-not-exist.xml -gs src/test/resources/settings-does-not-exist.xml -B" ); Scanner scanner = new MavenDependencyScanner(mockLicenseService()); Set dependencies = scanner.scan(createContext(new File("."))); assertThat(dependencies.size(), is(0)); } finally { System.setProperty("sun.java.command", oldVal); } } private SensorContext createContext(File moduleDir) { SensorContext context = mock(SensorContext.class); InputFile pomXml = mock(InputFile.class); when(pomXml.language()).thenReturn("xml"); when(pomXml.filename()).thenReturn("pom.xml"); when(pomXml.uri()).thenReturn(new File(moduleDir, "pom.xml").toURI()); when(pomXml.relativePath()).thenReturn("/pom.xml"); when(pomXml.type()).thenReturn(InputFile.Type.MAIN); try { when(pomXml.inputStream()).thenAnswer(i -> new FileInputStream(new File(moduleDir, "pom.xml")) ); } catch (IOException e) { throw new RuntimeException(e); } FileSystem fileSystem = new DefaultFileSystem(moduleDir.toPath()).add(pomXml); when(context.fileSystem()).thenReturn(fileSystem); return context; } private LicenseMappingService mockLicenseService() { Map licenseMap = new HashMap<>(); licenseMap.put(Pattern.compile(".*Apache.*2.*"), "Apache-2.0"); LicenseMappingService licenseService = Mockito.mock(LicenseMappingService.class); when(licenseService.getLicenseMap()).thenReturn(licenseMap); return licenseService; } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinderTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.maven; import static org.junit.Assert.assertEquals; import java.io.File; import org.junit.Test; public class DirectoryFinderTest { @Test public void checkMavenRepoLocal() { String mavenRepoLocalOld = System.getProperty("maven.repo.local"); System.setProperty("maven.repo.local", "/tmp/maven-repo"); File repoDir = DirectoryFinder.getMavenRepositoryDir(null, null); if (mavenRepoLocalOld != null) { System.setProperty("maven.repo.local", mavenRepoLocalOld); } else { System.clearProperty("maven.repo.local"); } assertEquals(repoDir.getAbsolutePath(), "/tmp/maven-repo"); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.projectlicense; import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_SET; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_ALLOWED; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_LICENSE; import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_PROJECT_KEY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Collection; import java.util.List; import java.util.Optional; import org.junit.Test; import org.sonar.api.config.Configuration; public class ProjectLicenseTest { @Test public void getAllProjectLicenses() { List projectLicenseList = createProjectLicenseService().getProjectLicenseList(); assertThat(projectLicenseList.size(), is(2)); } @Test public void getSpecificProjectLicenses() { Collection projectLicenseList = createProjectLicenseService().getProjectLicenseList("proj1"); assertThat(projectLicenseList.size(), is(1)); assertThat(projectLicenseList.iterator().next().getLicense(), is("MIT")); } private ProjectLicenseService createProjectLicenseService() { Configuration configuration = mock(Configuration.class); when(configuration.getStringArray(PROJECT_LICENSE_SET)).thenReturn( new String[] { "1", "2" } ); when(configuration.get(PROJECT_LICENSE_SET + ".1." + FIELD_PROJECT_KEY)).thenReturn( Optional.of("proj1") ); when(configuration.get(PROJECT_LICENSE_SET + ".2." + FIELD_PROJECT_KEY)).thenReturn( Optional.of("proj2") ); when(configuration.get(PROJECT_LICENSE_SET + ".1." + FIELD_LICENSE)).thenReturn( Optional.of("MIT") ); when(configuration.get(PROJECT_LICENSE_SET + ".2." + FIELD_LICENSE)).thenReturn( Optional.of("BSD") ); when(configuration.getBoolean(PROJECT_LICENSE_SET + ".1." + FIELD_ALLOWED)).thenReturn( Optional.of(false) ); when(configuration.getBoolean(PROJECT_LICENSE_SET + ".2." + FIELD_ALLOWED)).thenReturn( Optional.of(true) ); return new ProjectLicenseService(configuration); } } ================================================ FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtilsTest.java ================================================ package at.porscheinformatik.sonarqube.licensecheck.utils; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; public class IOUtilsTest { @Test public void testLoadFile() throws IOException { String packageJson = IOUtils.readToString( IOUtilsTest.class.getResourceAsStream("/example/package.json") ); assertThat(packageJson, containsString("\"name\": \"test\"")); } } ================================================ FILE: src/test/resources/build/my-reports/license-details.json ================================================ { "dependencies": [ { "moduleName": "ch.qos.logback:logback-classic", "moduleVersion": "1.2.3", "moduleUrls": ["http://www.qos.ch"] }, { "moduleName": "ch.qos.logback:logback-core", "moduleVersion": "1.2.3", "moduleUrls": ["http://www.qos.ch"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html, http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }, { "moduleLicense": "Eclipse Public License - v 1.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html" }, { "moduleLicense": "GNU Lesser General Public License", "moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-annotations", "moduleVersion": "2.10.4", "moduleUrls": ["http://github.com/FasterXML/jackson"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-core", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-core"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-databind", "moduleVersion": "2.10.4", "moduleUrls": ["http://github.com/FasterXML/jackson"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names", "moduleVersion": "2.10.4", "moduleUrls": [ "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names" ], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml:classmate", "moduleVersion": "1.5.1", "moduleUrls": ["https://github.com/FasterXML/java-classmate"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "jakarta.annotation:jakarta.annotation-api", "moduleVersion": "1.3.5", "moduleUrls": ["https://projects.eclipse.org/projects/ee4j.ca", "https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "jakarta.validation:jakarta.validation-api", "moduleVersion": "2.0.2", "moduleUrls": ["https://beanvalidation.org", "https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": "Apache License 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "jakarta.ws.rs:jakarta.ws.rs-api", "moduleVersion": "2.1.6", "moduleUrls": [ "https://github.com/eclipse-ee4j/jaxrs-api", "https://www.eclipse.org/org/foundation/" ], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.apache.logging.log4j:log4j-api", "moduleVersion": "2.12.1", "moduleUrls": ["https://www.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.logging.log4j:log4j-to-slf4j", "moduleVersion": "2.12.1", "moduleUrls": ["https://www.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-core", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0", "moduleLicenseUrl": "https://opensource.org/licenses/CDDL-1.0" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-el", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-websocket", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.glassfish.hk2.external:jakarta.inject", "moduleVersion": "2.6.1", "moduleUrls": ["http://www.oracle.com"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.glassfish.hk2:osgi-resource-locator", "moduleVersion": "1.0.3", "moduleUrls": ["https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.glassfish.jersey.core:jersey-client", "moduleVersion": "2.29.1", "moduleUrls": ["https://www.eclipse.org/org/foundation/"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.eclipse.org/org/documents/edl-v10.php, https://opensource.org/licenses/BSD-2-Clause, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/, http://asm.objectweb.org/license.html, jquery.org/license, http://www.opensource.org/licenses/mit-license.php, https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "Apache License, 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" }, { "moduleLicense": "BSD 2-Clause", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleLicense": "EDL 1.0", "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "MIT license", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleLicense": "Modified BSD", "moduleLicenseUrl": "http://asm.objectweb.org/license.html" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "W3C license", "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "jQuery license", "moduleLicenseUrl": "jquery.org/license" } ] }, { "moduleName": "org.glassfish.jersey.core:jersey-common", "moduleVersion": "2.29.1", "moduleUrls": ["https://www.eclipse.org/org/foundation/"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "Apache License, 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" }, { "moduleLicense": "BSD 2-Clause", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleLicense": "EDL 1.0", "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "MIT license", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleLicense": "Modified BSD", "moduleLicenseUrl": "http://asm.objectweb.org/license.html" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "The GNU General Public License (GPL), Version 2, With Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "W3C license", "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "jQuery license", "moduleLicenseUrl": "jquery.org/license" } ] }, { "moduleName": "org.hibernate.validator:hibernate-validator", "moduleVersion": "6.0.20.Final", "moduleLicenses": [ { "moduleLicense": "Apache License 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.jboss.logging:jboss-logging", "moduleVersion": "3.4.1.Final", "moduleUrls": ["http://www.jboss.org"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleLicense": "Apache License, version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt" } ] }, { "moduleName": "org.slf4j:jul-to-slf4j", "moduleVersion": "1.7.30", "moduleUrls": ["http://www.slf4j.org"], "moduleLicenses": [ { "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" } ] }, { "moduleName": "org.slf4j:slf4j-api", "moduleVersion": "1.7.30", "moduleUrls": ["http://www.slf4j.org"], "moduleLicenses": [ { "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" } ] }, { "moduleName": "org.springframework.boot:spring-boot", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": ["https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-autoconfigure", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-autoconfigure" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-devtools", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-devtools" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-json", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-logging", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-logging" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-tomcat", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-tomcat" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-validation", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-validation" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-web", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-aop", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-beans", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-context", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-core", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-expression", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-jcl", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-web", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-webmvc", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.yaml:snakeyaml", "moduleVersion": "1.25", "moduleUrls": ["http://www.snakeyaml.org"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] } ] } ================================================ FILE: src/test/resources/build/reports/dependency-license/license-details.json ================================================ { "dependencies": [ { "moduleName": "ch.qos.logback:logback-classic", "moduleVersion": "1.2.3", "moduleUrls": ["http://www.qos.ch"] }, { "moduleName": "ch.qos.logback:logback-core", "moduleVersion": "1.2.3", "moduleUrls": ["http://www.qos.ch"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html, http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }, { "moduleLicense": "Eclipse Public License - v 1.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html" }, { "moduleLicense": "GNU Lesser General Public License", "moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-annotations", "moduleVersion": "2.10.4", "moduleUrls": ["http://github.com/FasterXML/jackson"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-core", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-core"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.core:jackson-databind", "moduleVersion": "2.10.4", "moduleUrls": ["http://github.com/FasterXML/jackson"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", "moduleVersion": "2.10.4", "moduleUrls": ["https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310"], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names", "moduleVersion": "2.10.4", "moduleUrls": [ "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names" ], "moduleLicenses": [ { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "com.fasterxml:classmate", "moduleVersion": "1.5.1", "moduleUrls": ["https://github.com/FasterXML/java-classmate"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "jakarta.annotation:jakarta.annotation-api", "moduleVersion": "1.3.5", "moduleUrls": ["https://projects.eclipse.org/projects/ee4j.ca", "https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "jakarta.validation:jakarta.validation-api", "moduleVersion": "2.0.2", "moduleUrls": ["https://beanvalidation.org", "https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": "Apache License 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "jakarta.ws.rs:jakarta.ws.rs-api", "moduleVersion": "2.1.6", "moduleUrls": [ "https://github.com/eclipse-ee4j/jaxrs-api", "https://www.eclipse.org/org/foundation/" ], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.apache.logging.log4j:log4j-api", "moduleVersion": "2.12.1", "moduleUrls": ["https://www.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.logging.log4j:log4j-to-slf4j", "moduleVersion": "2.12.1", "moduleUrls": ["https://www.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-core", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0", "moduleLicenseUrl": "https://opensource.org/licenses/CDDL-1.0" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-el", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-websocket", "moduleVersion": "9.0.36", "moduleUrls": ["https://tomcat.apache.org/"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.glassfish.hk2.external:jakarta.inject", "moduleVersion": "2.6.1", "moduleUrls": ["http://www.oracle.com"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.glassfish.hk2:osgi-resource-locator", "moduleVersion": "1.0.3", "moduleUrls": ["https://www.eclipse.org"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" } ] }, { "moduleName": "org.glassfish.jersey.core:jersey-client", "moduleVersion": "2.29.1", "moduleUrls": ["https://www.eclipse.org/org/foundation/"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.eclipse.org/org/documents/edl-v10.php, https://opensource.org/licenses/BSD-2-Clause, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/, http://asm.objectweb.org/license.html, jquery.org/license, http://www.opensource.org/licenses/mit-license.php, https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "Apache License, 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" }, { "moduleLicense": "BSD 2-Clause", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleLicense": "EDL 1.0", "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "MIT license", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleLicense": "Modified BSD", "moduleLicenseUrl": "http://asm.objectweb.org/license.html" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "W3C license", "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "jQuery license", "moduleLicenseUrl": "jquery.org/license" } ] }, { "moduleName": "org.glassfish.jersey.core:jersey-common", "moduleVersion": "2.29.1", "moduleUrls": ["https://www.eclipse.org/org/foundation/"], "moduleLicenses": [ { "moduleLicense": null, "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "Apache License, 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" }, { "moduleLicense": "BSD 2-Clause", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleLicense": "EDL 1.0", "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php" }, { "moduleLicense": "EPL 2.0", "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0" }, { "moduleLicense": "Eclipse Public License v. 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "GPL2 w/ CPE", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "MIT license", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleLicense": "Modified BSD", "moduleLicenseUrl": "http://asm.objectweb.org/license.html" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/" }, { "moduleLicense": "The GNU General Public License (GPL), Version 2, With Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleLicense": "W3C license", "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405" }, { "moduleLicense": "jQuery license", "moduleLicenseUrl": "jquery.org/license" } ] }, { "moduleName": "org.hibernate.validator:hibernate-validator", "moduleVersion": "6.0.20.Final", "moduleLicenses": [ { "moduleLicense": "Apache License 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] }, { "moduleName": "org.jboss.logging:jboss-logging", "moduleVersion": "3.4.1.Final", "moduleUrls": ["http://www.jboss.org"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleLicense": "Apache License, version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleLicense": "Public Domain", "moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt" } ] }, { "moduleName": "org.slf4j:jul-to-slf4j", "moduleVersion": "1.7.30", "moduleUrls": ["http://www.slf4j.org"], "moduleLicenses": [ { "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" } ] }, { "moduleName": "org.slf4j:slf4j-api", "moduleVersion": "1.7.30", "moduleUrls": ["http://www.slf4j.org"], "moduleLicenses": [ { "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" } ] }, { "moduleName": "org.springframework.boot:spring-boot", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": ["https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-autoconfigure", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-autoconfigure" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-devtools", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-devtools" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-json", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-logging", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-logging" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-tomcat", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-tomcat" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-validation", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-validation" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework.boot:spring-boot-starter-web", "moduleVersion": "2.2.8.RELEASE", "moduleUrls": [ "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web" ], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-aop", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-beans", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-context", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-core", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-expression", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-jcl", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-web", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.springframework:spring-webmvc", "moduleVersion": "5.2.7.RELEASE", "moduleUrls": ["https://github.com/spring-projects/spring-framework"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" } ] }, { "moduleName": "org.yaml:snakeyaml", "moduleVersion": "1.25", "moduleUrls": ["http://www.snakeyaml.org"], "moduleLicenses": [ { "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" } ] } ] } ================================================ FILE: src/test/resources/deprecated_multilicense_project/package.json ================================================ { "name": "test", "version": "1.0.0", "description": "An example module to illustrate the usage of a package.json", "author": "Your Name ", "dependencies": { "some-module": "^1.7.1" }, "license": "MIT" } ================================================ FILE: src/test/resources/deprecated_project/package.json ================================================ { "name": "test", "version": "1.0.0", "description": "An example module to illustrate the usage of a package.json", "author": "Your Name ", "dependencies": { "dynamic-dedupe": "^0.3.0" }, "license": "MIT" } ================================================ FILE: src/test/resources/example/package.json ================================================ { "name": "test", "version": "1.0.0", "description": "An example module to illustrate the usage of a package.json", "author": "Your Name ", "dependencies": { "angular": "^1.4.3", "angular-ui-router": "~0.2.18", "angular-ui-bootstrap": "1.1.2", "arangojs": "5.6.0" }, "devDependencies": { "gulp": "^3.9.1" }, "license": "MIT" } ================================================ FILE: src/test/resources/example_nested/example/package.json ================================================ { "name": "test", "version": "1.0.0", "description": "An example module to illustrate the usage of a package.json", "author": "Your Name ", "dependencies": { "angular": "^1.4.3", "angular-ui-router": "~0.2.18", "angular-ui-bootstrap": "1.1.2", "arangojs": "5.6.0" }, "devDependencies": { "gulp": "^3.9.1" }, "license": "MIT" } ================================================ FILE: src/test/resources/no_package_json/.gitkeep ================================================ ================================================ FILE: src/test/resources/spdc_license_list.json ================================================ { "Glide": { "name": "3dfx Glide License", "url": "http://www.users.on.net/~triforce/glidexp/COPYING.txt", "osiApproved": false }, "Abstyles": { "name": "Abstyles License", "url": "https://fedoraproject.org/wiki/Licensing/Abstyles", "osiApproved": false }, "AFL-1.1": { "name": "Academic Free License v1.1", "url": "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", "osiApproved": true }, "AFL-1.2": { "name": "Academic Free License v1.2", "url": "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", "osiApproved": true }, "ZPL-2.1": { "name": "Zope Public License 2.1", "url": "http://old.zope.org/Resources/ZPL/", "osiApproved": false } } ================================================ FILE: src/test/resources/test.pom ================================================ EMPTY ================================================ FILE: test-server.js ================================================ const http = require("http"), httpProxy = require("http-proxy"), fs = require("fs"); const proxy = httpProxy.createProxyServer({}); // Setup proxy to remove content-security-policy header from responses proxy.on("proxyRes", function (proxyRes, req, res) { delete proxyRes.headers["content-security-policy"]; }); const server = http.createServer(function (req, res) { let match = req.url.match(/\/static\/licensecheck\/(\w+).js/); if (match) { res.writeHead(200, { "Content-Type": "application/javascript" }); fs.readFile(`./target/classes/static/${match[1]}.js`, "utf8", function (err, data) { if (err) { return console.log(err); } res.write(data); res.end(); }); } else { proxy.web(req, res, { target: "http://127.0.0.1:9000" }); } }); console.log("listening on port 5050"); server.listen(5050); ================================================ FILE: webpack.config.js ================================================ const path = require("path"); module.exports = { entry: { configuration: "./src/main/web/configuration.js", dashboard: "./src/main/web/dashboard.js", }, output: { path: path.resolve(__dirname, "target/classes/static/"), filename: "[name].js", }, resolve: { extensions: [".js", ".jsx", ".json"], }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env", "@babel/preset-react"], }, }, }, { test: /\.css$/, use: ["style-loader", "css-loader"], }, ], }, };