Showing preview only (345K chars total). Download the full file or copy to clipboard to get everything.
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
[](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
<!-- TOC -->
- [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)
<!-- TOC -->
## 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`.

### 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.

## 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 <b>Administration</b> or also in the <b>Configuration -> LicenseCheck</b> drop down menu.
### Configuration via Administration Tab
- Within the <b>General Settings</b> and <b>License Check</b> 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".

- Under "Licenses" you can allow or disallow licenses globally and add/edit the list of known licenses.

- Under "Project Licenses" you can allow and disallow licenses for a specific project.

### Configuration via License Menu
Administration -> Configuration(dropdown) -> License Check

- Under "Licenses" you can allow or disallow licenses globally and add/edit the list of known licenses.


- Under "Project Licenses" you can allow and disallow licenses for a specific project.


- 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 Mappings" you can map a license name (with regex) to a license, e.g. `.*Apache.*2.*` to "Apache-2.0".


### 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

2. Step 2

3. Step 3

4. Step 4

5. Step 5

6. Step 6

7. Step 7

## 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
...
<properties>
<sonar.sources>pom.xml,package.json</sonar.sources>
<properties>
...
```
### 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
================================================
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>at.porscheinformatik.sonarqube.licensecheck</groupId>
<artifactId>sonarqube-licensecheck-plugin</artifactId>
<version>7.0.0-SNAPSHOT</version>
<name>SonarQube License Check Plugin</name>
<packaging>sonar-plugin</packaging>
<description>Checks Maven and NPM dependencies against a defined set of allowed licenses and reports any unknown or
not allowed licenses as an issue.
</description>
<url>https://github.com/porscheinformatik/sonarqube-licensecheck</url>
<organization>
<name>Porsche Informatik</name>
<url>https://www.porscheinformatik.at</url>
</organization>
<developers>
<developer>
<id>derkoe</id>
<name>Christian Köberl</name>
</developer>
<developer>
<name>Stephanie Kaschnitz</name>
</developer>
</developers>
<issueManagement>
<system>github</system>
<url>https://github.com/porscheinformatik/sonarqube-licensecheck/issues</url>
</issueManagement>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.sources>src/main,pom.xml,package.json</sonar.sources>
<sonar.pluginTermsConditionsUrl>
https://github.com/porscheinformatik/sonarqube-licensecheck/blob/master/LICENSE</sonar.pluginTermsConditionsUrl>
<sonar.apiVersion>11.0.0.2664</sonar.apiVersion>
<sonar.apiImplVersion>25.1.0.102122</sonar.apiImplVersion>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.17.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.api.plugin</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${sonar.apiVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api-impl</artifactId>
<version>${sonar.apiImplVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
<version>1.1.4c</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>3.9.9</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-invoker</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<scm>
<connection>scm:git:https://github.com/porscheinformatik/sonarqube-licensecheck.git</connection>
<developerConnection>https://github.com/porscheinformatik/sonarqube-licensecheck.git</developerConnection>
<url>https://github.com/porscheinformatik/sonarqube-licensecheck</url>
<tag>HEAD</tag>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>3.7</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<version>1.23.0.740</version>
<extensions>true</extensions>
<configuration>
<pluginKey>licensecheck</pluginKey>
<pluginClass>at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPlugin</pluginClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<systemPropertyVariables>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.15.1</version>
<executions>
<execution>
<id>install-node-and-corepack</id>
<goals>
<goal>install-node-and-corepack</goal>
</goals>
<phase>generate-resources</phase>
</execution>
<execution>
<id>pnpm install</id>
<goals>
<goal>corepack</goal>
</goals>
<configuration>
<arguments>pnpm install</arguments>
</configuration>
</execution>
<execution>
<id>pnpm run prettier-check</id>
<goals>
<goal>corepack</goal>
</goals>
<configuration>
<arguments>pnpm run prettier-check</arguments>
</configuration>
<phase>test</phase>
</execution>
<execution>
<id>pnpm run build</id>
<goals>
<goal>corepack</goal>
</goals>
<configuration>
<arguments>pnpm run build</arguments>
</configuration>
</execution>
</executions>
<configuration>
<nodeVersion>v24.13.1</nodeVersion>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>install</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/docker/plugins</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}</directory>
<includes>
<include>${project.artifactId}-${project.version}.jar</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
================================================
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<Dependency> {
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<Dependency> dependencies) {
TreeSet<Dependency> 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<String> 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<String> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Metric> 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<License> AGGREGATED_LICENSES = ConcurrentHashMap.newKeySet();
private static final Set<Dependency> 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<Dependency> dependencies
) {
LOGGER.debug(
"Saving dependencies for module {}: {}",
sensorContext.project(),
dependencies
);
if (!dependencies.isEmpty()) {
sensorContext
.<String>newMeasure()
.forMetric(LicenseCheckMetrics.DEPENDENCY)
.withValue(Dependency.createString(dependencies))
.on(sensorContext.project())
.save();
}
}
private static void saveLicenses(SensorContext sensorContext, Set<License> licenses) {
LOGGER.debug("Saving licenses for project {}: {}", sensorContext.project(), licenses);
if (!licenses.isEmpty()) {
sensorContext
.<String>newMeasure()
.forMetric(LicenseCheckMetrics.LICENSE)
.withValue(License.createJsonString(licenses))
.on(sensorContext.project())
.save();
}
}
private static void saveMeasures(
SensorContext sensorContext,
Set<License> licenses,
Set<Dependency> dependencies
) {
sensorContext
.<Integer>newMeasure()
.forMetric(LicenseCheckMetrics.NO_LICENSES)
.withValue(licenses.size())
.on(sensorContext.project())
.save();
sensorContext
.<Integer>newMeasure()
.forMetric(LicenseCheckMetrics.NO_LICENSES_FORBIDDEN)
.withValue((int) licenses.stream().filter(not(License::getAllowed)).count())
.on(sensorContext.project())
.save();
sensorContext
.<Integer>newMeasure()
.forMetric(LicenseCheckMetrics.NO_DEPENDENCIES)
.withValue(dependencies.size())
.on(sensorContext.project())
.save();
sensorContext
.<Integer>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
.<Integer>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<Dependency> dependencies = new TreeSet<>();
for (Scanner scanner : scanners) {
dependencies.addAll(scanner.scan(context));
}
InputProject project = context.project();
Set<Dependency> validatedDependencies = validateLicenses.validateLicenses(
dependencies,
context
);
Set<License> 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<Dependency> 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<Dependency> validateLicenses(Set<Dependency> dependencies, SensorContext context) {
List<License> licenses = licenseService.getLicenses(context.project());
List<DependencyMapping> 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<License> getUsedLicenses(Set<Dependency> dependencies, InputProject project) {
List<License> licenses = licenseService.getLicenses(project);
Set<License> 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<License> licenses, Dependency dependency) {
for (License license : licenses) {
if (license.getIdentifier().equals(dependency.getLicense())) {
return license;
}
}
return null;
}
private static void mapDependencyToLicense(
List<DependencyMapping> 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<DependencyMapping> 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<License> licenses,
Dependency dependency
) {
if (StringUtils.isBlank(dependency.getLicense())) {
licenseNotFoundIssue(context, dependency);
return false;
}
if (checkSpdxLicense(dependency.getLicense(), licenses)) {
return true;
}
List<License> 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<License> 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<License> 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<License> licenses
) {
String[] andLicenses = spdxLicenseString.replace("(", "").replace(")", "").split(" AND ");
long count = andLicenses.length;
List<License> 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<DependencyMapping> {
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<DependencyMapping> 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<Dependency> scan(SensorContext context) {
Map<Pattern, String> 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<Dependency> readLicenseDetailsJson(File licenseDetailsJsonFile) {
Set<Dependency> 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<Dependency> 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<Pattern, String> licenseMap, Dependency dependency) {
if (StringUtils.isBlank(dependency.getLicense())) {
LOGGER.info("Dependency '{}' has no license set.", dependency.getName());
return dependency;
}
for (Map.Entry<Pattern, String> 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<License> {
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<License> fromString(String serializedLicensesString) {
if (serializedLicensesString == null) {
return new ArrayList<>();
}
if (serializedLicensesString.startsWith("[")) {
List<License> 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<License> readLegacySeparated(String serializedLicensesString) {
List<License> 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<License> readLegacyJson(String serializedLicensesString) {
List<License> licenses = new ArrayList<>();
try (
JsonReader jsonReader = Json.createReader(new StringReader(serializedLicensesString));
) {
JsonObject licensesJson = jsonReader.readObject();
for (Map.Entry<String, JsonValue> 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<License> licenses) {
TreeSet<License> 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<License> getLicenses(InputProject module) {
List<License> globalLicenses = getLicenses();
if (module == null) {
return globalLicenses;
}
Collection<ProjectLicense> 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<License> 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<LicenseMapping> {
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<LicenseMapping> fromString(String licenseMappingString) {
List<LicenseMapping> 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<Pattern, String> LICENSE_MAP;
public LicenseMappingService(Configuration configuration) {
super();
this.configuration = configuration;
}
public List<LicenseMapping> 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<Pattern, String> 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<Pattern, String> licenseMap = getLicenseMap();
for (Map.Entry<Pattern, String> 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<License> 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<Dependency> scan(SensorContext context) {
MavenSettings settings = getSettingsFromCommandLineArgs();
FileSystem fs = context.fileSystem();
FilePredicate pomXmlPredicate = fs.predicates().matchesPathPattern("**/pom.xml");
Set<Dependency> allDependencies = new HashSet<>();
for (InputFile pomXml : fs.inputFiles(pomXmlPredicate)) {
context.markForPublishing(pomXml);
LOGGER.info("Scanning for Maven dependencies (POM: {})", pomXml.uri());
try (
Stream<Dependency> 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<Dependency> 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<Dependency> 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<Dependency, Dependency> loadLicenseFromPom(
Map<Pattern, String> 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<Pattern, String> licenseMap,
MavenSettings settings,
Dependency dependency
) {
String pomPath = dependency.getPomPath();
if (pomPath != null) {
List<License> 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<Pattern, String> 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<Pattern, String> 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<Dependency> scan(SensorContext context) {
FileSystem fs = context.fileSystem();
FilePredicate packageJsonPredicate = fs.predicates().matchesPathPattern("**/package.json");
Set<Dependency> 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<Dependency> dependencyParser(File baseDir, InputFile packageJsonFile) {
Set<Dependency> 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<String> packageNames,
Set<Dependency> 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<ProjectLicense> {
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<ProjectLicense> fromString(String projectLicensesString) {
List<ProjectLicense> 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<ProjectLicense> 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<ProjectLicense> getProjectLicenseList(String projectKey) {
Collection<ProjectLicense> allProjectLicenses = getProjectLicenseList();
Collection<ProjectLicense> 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 (
<div className="sqlc-page">
<ul className="sw-flex sw-items-end sw-gap-8 sw-mt-4">
<li>
<a
href="?category=licenses"
className="sw-flex sw-items-center"
style={currentRoute === "licenses" ? { borderBottomColor: "rgb(123, 135, 217)" } : {}}
onClick={(e) => activateCategory(e, "licenses")}
>
Licenses
</a>
</li>
<li>
<a
href="?category=license-mappings"
className={currentRoute === "license-mappings" ? "selected" : ""}
onClick={(e) => activateCategory(e, "license-mappings")}
>
License Mappings
</a>
</li>
<li>
<a
href="?category=dependency-mappings"
className={currentRoute === "dependency-mappings" ? "selected" : ""}
onClick={(e) => activateCategory(e, "dependency-mappings")}
>
Dependency Mappings
</a>
</li>
<li>
<a
href="?category=project-licenses"
className={currentRoute === "project-licenses" ? "selected" : ""}
onClick={(e) => activateCategory(e, "project-licenses")}
>
Project Licenses
</a>
</li>
</ul>
<br />
{currentRoute === "licenses" && <LicensesPage />}
{currentRoute === "dependency-mappings" && <DependencyMappingsPage />}
{currentRoute === "license-mappings" && <LicenseMappingsPage />}
{currentRoute === "project-licenses" && <ProjectLicensesPage />}
</div>
);
};
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 (
<div>
<header className="sw-mb-5">
<h1 className="sw-mb-4">License Check - Dependency Mappings</h1>
<div className="sw-mb-4">Maps a dependency name/key (with regex) to a license</div>
<div className="page-actions">
<Button onClick={showAddDialog}>Add Dependency Mapping</Button>
</div>
</header>
<div className="sw-mb-4">
<TextInput
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
prefix={<IconSearch key="IconSearch" />}
/>
</div>
<div>
<table className="sqlc-data">
<thead>
<tr>
<th onClick={() => sort("key")} scope="col">
Key Regex
{sortBy === "key" &&
(sortDirection === "asc" ? <IconTriangleUp /> : <IconTriangleDown />)}
</th>
<th onClick={() => sort("license")} scope="col">
License
{sortBy === "license" &&
(sortDirection === "asc" ? <IconTriangleUp /> : <IconTriangleDown />)}
</th>
<th onClick={() => sort("overwrite")} scope="col">
Overwrite License
{sortBy === "overwrite" &&
(sortDirection === "asc" ? <IconTriangleUp /> : <IconTriangleDown />)}
</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{displayedItems.map((item) => (
<tr key={item.key}>
<td className="sw-truncate">{item.key}</td>
<td className="sw-truncate">
{item.license} / {findLicenseName(item.license)}
</td>
<td>{item.overwrite === "true" ? "Yes" : "No"}</td>
<td className="sw-whitespace-nowrap">
<ButtonIcon
variety="default-ghost"
ariaLabel="Edit"
Icon={IconEdit}
onClick={() => showEditDialog(item)}
/>
{items.length > 1 && (
<ButtonIcon
variety="default-ghost"
ariaLabel="Delete"
Icon={IconDelete}
onClick={() => showDeleteDialog(item)}
/>
)}
</td>
</tr>
))}
{!displayedItems.length && (
<tr>
<td colSpan="4">No Maven dependencies available</td>
</tr>
)}
</tbody>
</table>
</div>
<Modal
title={editMode === "add" ? "Add Maven Dependency" : "Edit Maven Dependency"}
isOpen={!!itemToEdit}
onOpenChange={(isOpen) => {
if (!isOpen) {
cancelEdit();
}
}}
primaryButton={<Button onClick={() => saveItem(itemToEdit)}>Save</Button>}
secondaryButton={
<Button variety="default-ghost" onClick={cancelEdit}>
Cancel
</Button>
}
content={
itemToEdit && (
<div>
<div className="modal-field">
<Label htmlFor="keyEdit">
Key Regex<em className="mandatory">*</em>
</Label>
<TextInput
required
autoFocus={true}
value={itemToEdit.key || ""}
onChange={(e) => setItemToEdit({ ...itemToEdit, key: e.target.value })}
id="keyEdit"
name="keyEdit"
maxLength="255"
/>
</div>
<div className="modal-field">
<Label htmlFor="licenseSelect">
License<em className="mandatory">*</em>
</Label>
<select
required
value={itemToEdit.license || ""}
onChange={(e) => setItemToEdit({ ...itemToEdit, license: e.target.value })}
id="licenseSelect"
name="licenseSelect"
className="sw-w-full sw-px-3 sw-py-2 sw-border sw-border-neutral-200 sw-rounded"
>
<option value="">Select a license</option>
{licenses.map((license) => (
<option key={license.id} value={license.id}>
{license.id} / {license.name}
</option>
))}
</select>
</div>
<div className="modal-field">
<Label>Overwrite License</Label>
<div>
<Checkbox
label="Overwrite"
name="overwrite"
checked={itemToEdit.overwrite === "true"}
onCheck={(checked) =>
setItemToEdit({
...itemToEdit,
overwrite: checked ? "true" : "false",
})
}
/>
</div>
</div>
</div>
)
}
/>
<Modal
title="Delete Maven Dependency"
isOpen={!!itemToDelete}
onOpenChange={(isOpen) => {
if (!isOpen) {
cancelDelete();
}
}}
description={`Are you sure you want to delete the Maven dependency mapping "${itemToDelete?.key}" / "${itemToDelete?.license}"?`}
primaryButton={<Button onClick={() => deleteItem(itemToDelete)}>Delete</Button>}
secondaryButton={
<Button variety="default-ghost" onClick={cancelDelete}>
Cancel
</Button>
}
/>
</div>
);
};
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 (
<div>
<header className="sw-mb-5">
<h1 className="sw-mb-4">License Check - License Mappings</h1>
<div className="sw-mb-4">Maps a license name (with regex) to a license</div>
<div className="page-actions">
<Button onClick={showAddDialog}>Add License Mapping</Button>
</div>
</header>
<div className="sw-mb-4">
<TextInput
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
prefix={<IconSearch key="IconSearch" />}
/>
</div>
<div>
<table className="sqlc-data">
<thead>
<tr>
<th onClick={() => sort("regex")} scope="col">
License Text Regex
{sortBy === "regex" &&
(sortDirection === "asc" ? <IconTriangleUp /> : <IconTriangleDown />)}
</th>
<th onClick={() => sort("license")} scope="col">
License
{sortBy === "license" &&
(sortDirection === "asc" ? <IconTriangleUp /> : <IconTriangleDown />)}
</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{displayedItems.map((item) => (
<tr key={item.regex}>
<td className="sw-truncate">{item.regex}</td>
<td className="sw-truncate">
{item.license} / {findLicenseName(item.license)}
</td>
<td className="sw-whitespace-nowrap">
<ButtonIcon
variety="default-ghost"
ariaLabel="Edit"
Icon={IconEdit}
onClick={() => showEditDialog(item)}
/>
{items.length > 1 && (
<ButtonIcon
variety="default-ghost"
ariaLabel="Delete"
Icon={IconDelete}
onClick={() => showDeleteDialog(item)}
/>
)}
</td>
</tr>
))}
{!displayedItems.length && (
<tr>
<td colSpan="3">No license mappings available</td>
</tr>
)}
</tbody>
</table>
</div>
<Modal
title={editMode === "add" ? "Add License Mapping" : "Edit License Mapping"}
isOpen={!!itemToEdit}
onOpenChange={(isOpen) => {
if (!isOpen) {
cancelEdit();
}
}}
primaryButton={<Button onClick={() => saveItem(itemToEdit)}>Save</Button>}
secondaryButton={
<Button variety="default-ghost" onClick={cancelEdit}>
Cancel
</Button>
}
content={
itemToEdit && (
<div>
<div className="modal-field">
<Label htmlFor="regexEdit">
License Text Regex<em className="mandatory">*</em>
</Label>
<TextInput
required
autoFocus
value={itemToEdit.regex || ""}
onChange={(e) => setItemToEdit({ ...itemToEdit, regex: e.target.value })}
id="regexEdit"
name="regexEdit"
maxLength="255"
/>
</div>
<div className="modal-field">
<Label htmlFor="licenseSelect">
License<em className="mandatory">*</em>
</Label>
<select
required
value={itemToEdit.license || ""}
onChange={(e) => setItemToEdit({ ...itemToEdit, license: e.target.value })}
id="licenseSelect"
name="licenseSelect"
className="sw-w-full sw-px-3 sw-py-2 sw-border sw-border-neutral-200 sw-rounded"
>
<option value="">Select a license</option>
{lic
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
SYMBOL INDEX (269 symbols across 46 files)
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/Dependency.java
class Dependency (line 14) | public class Dependency implements Comparable<Dependency> {
method Dependency (line 25) | public Dependency(String name, String version, String license, String ...
method Dependency (line 33) | public Dependency(String name, String version, String license) {
method getName (line 37) | public String getName() {
method setName (line 41) | public void setName(String name) {
method getVersion (line 45) | public String getVersion() {
method setVersion (line 49) | public void setVersion(String version) {
method getLicense (line 53) | public String getLicense() {
method setLicense (line 57) | public void setLicense(String license) {
method getLang (line 61) | public String getLang() {
method setLang (line 65) | public void setLang(String lang) {
method setStatus (line 69) | public void setStatus(final Status status) {
method getStatus (line 73) | public Status getStatus() {
method getPomPath (line 77) | public String getPomPath() {
method setPomPath (line 81) | public void setPomPath(String pomPath) {
method getInputComponent (line 85) | public InputComponent getInputComponent() {
method setInputComponent (line 89) | public void setInputComponent(InputComponent inputComponent) {
method getTextRange (line 93) | public TextRange getTextRange() {
method setTextRange (line 97) | public void setTextRange(TextRange textRange) {
method equals (line 101) | @Override
method hashCode (line 117) | @Override
method toString (line 122) | @Override
method compareTo (line 127) | @Override
method createString (line 138) | public static String createString(Collection<Dependency> dependencies) {
type Status (line 158) | public enum Status {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetrics.java
class LicenseCheckMetrics (line 9) | public class LicenseCheckMetrics implements Metrics {
method getMetrics (line 80) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinition.java
class LicenseCheckPageDefinition (line 7) | public class LicenseCheckPageDefinition implements PageDefinition {
method define (line 9) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java
class LicenseCheckPlugin (line 18) | public class LicenseCheckPlugin implements Plugin {
method define (line 24) | @Override
method getExtensions (line 29) | private List<?> getExtensions() {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java
class LicenseCheckPropertyKeys (line 3) | public class LicenseCheckPropertyKeys {
method LicenseCheckPropertyKeys (line 45) | private LicenseCheckPropertyKeys() {}
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java
class LicenseCheckRulesDefinition (line 9) | public final class LicenseCheckRulesDefinition implements RulesDefinition {
method define (line 28) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java
class LicenseCheckSensor (line 26) | public class LicenseCheckSensor implements Sensor {
method LicenseCheckSensor (line 35) | public LicenseCheckSensor(
method saveDependencies (line 54) | private static void saveDependencies(
method saveLicenses (line 74) | private static void saveLicenses(SensorContext sensorContext, Set<Lice...
method saveMeasures (line 87) | private static void saveMeasures(
method describe (line 138) | @Override
method execute (line 151) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/Scanner.java
type Scanner (line 6) | public interface Scanner {
method scan (line 7) | Set<Dependency> scan(SensorContext context);
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java
class ValidateLicenses (line 21) | @ScannerSide
method ValidateLicenses (line 29) | public ValidateLicenses(
method validateLicenses (line 38) | public Set<Dependency> validateLicenses(Set<Dependency> dependencies, ...
method getUsedLicenses (line 60) | public Set<License> getUsedLicenses(Set<Dependency> dependencies, Inpu...
method findLicense (line 74) | private License findLicense(List<License> licenses, Dependency depende...
method mapDependencyToLicense (line 83) | private static void mapDependencyToLicense(
method overrideLicenseForDependency (line 97) | private static void overrideLicenseForDependency(
method isLicensesValid (line 112) | private static boolean isLicensesValid(
method checkSpdxLicense (line 153) | private static boolean checkSpdxLicense(String spdxLicenseString, List...
method checkSpdxLicenseWithOr (line 166) | private static boolean checkSpdxLicenseWithOr(
method checkSpdxLicenseWithAnd (line 177) | private static boolean checkSpdxLicenseWithAnd(
method licenseNotAllowedIssue (line 191) | private static void licenseNotAllowedIssue(
method licenseNotFoundIssue (line 216) | private static void licenseNotFoundIssue(SensorContext context, Depend...
method createIssue (line 228) | private static void createIssue(
method getRepoKey (line 244) | private static String getRepoKey(Dependency dependency) {
method contains (line 262) | private static boolean contains(String[] items, String valueToFind) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMapping.java
class DependencyMapping (line 5) | public class DependencyMapping implements Comparable<DependencyMapping> {
method DependencyMapping (line 15) | public DependencyMapping(String key, String license, Boolean overwrite) {
method getKey (line 22) | public String getKey() {
method setKey (line 26) | public void setKey(String key) {
method getLicense (line 30) | public String getLicense() {
method setLicense (line 34) | public void setLicense(String license) {
method getOverwrite (line 38) | public Boolean getOverwrite() {
method setOverwrite (line 42) | public void setOverwrite(Boolean overwrite) {
method toString (line 46) | @Override
method compareTo (line 51) | @Override
method equals (line 64) | @Override
method hashCode (line 76) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java
class DependencyMappingService (line 15) | @ServerSide
method DependencyMappingService (line 21) | public DependencyMappingService(Configuration configuration) {
method getDependencyMappings (line 26) | public List<DependencyMapping> getDependencyMappings() {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java
class GradleDependencyScanner (line 28) | public class GradleDependencyScanner implements Scanner {
method GradleDependencyScanner (line 43) | public GradleDependencyScanner(LicenseMappingService licenseMappingSer...
method scan (line 47) | @Override
method readLicenseDetailsJson (line 78) | private Set<Dependency> readLicenseDetailsJson(File licenseDetailsJson...
method prepareDependencySet (line 97) | private void prepareDependencySet(Set<Dependency> dependencySet, JsonA...
method getModuleLicenseFromJsonObject (line 117) | private String getModuleLicenseFromJsonObject(JsonObject jsonDepObj) {
method getModuleLicense (line 126) | private String getModuleLicense(JsonArray arrModuleLicenses) {
method matchLicense (line 138) | private Dependency matchLicense(Map<Pattern, String> licenseMap, Depen...
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/License.java
class License (line 10) | public class License implements Comparable<License> {
method License (line 21) | public License(String name, String identifier, Boolean allowed) {
method License (line 28) | public License(String name, String identifier, String allowed) {
method getName (line 32) | public String getName() {
method setName (line 36) | public void setName(String name) {
method getIdentifier (line 40) | public String getIdentifier() {
method setIdentifier (line 44) | public void setIdentifier(String identifier) {
method getAllowed (line 48) | public Boolean getAllowed() {
method setAllowed (line 52) | public void setAllowed(Boolean allowed) {
method toString (line 56) | @Override
method fromString (line 61) | public static List<License> fromString(String serializedLicensesString) {
method readLegacySeparated (line 98) | @Deprecated
method readLegacyJson (line 122) | @Deprecated
method createJsonString (line 145) | public static String createJsonString(Collection<License> licenses) {
method compareTo (line 164) | @Override
method equals (line 181) | @Override
method hashCode (line 197) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java
class LicenseService (line 19) | @ServerSide
method LicenseService (line 26) | public LicenseService(
method getLicenses (line 35) | public List<License> getLicenses(InputProject module) {
method getLicenses (line 57) | public List<License> getLicenses() {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMapping.java
class LicenseMapping (line 14) | public class LicenseMapping implements Comparable<LicenseMapping> {
method LicenseMapping (line 22) | public LicenseMapping(String regex, String license) {
method getRegex (line 28) | public Pattern getRegex() {
method getLicense (line 32) | public String getLicense() {
method compareTo (line 36) | @Override
method fromString (line 47) | public static List<LicenseMapping> fromString(String licenseMappingStr...
method equals (line 85) | @Override
method hashCode (line 100) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java
class LicenseMappingService (line 18) | @ServerSide
method LicenseMappingService (line 27) | public LicenseMappingService(Configuration configuration) {
method getLicenseMappingList (line 32) | public List<LicenseMapping> getLicenseMappingList() {
method getLicenseMap (line 47) | public Map<Pattern, String> getLicenseMap() {
method mapLicense (line 59) | public String mapLicense(String licenseName) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinder.java
class DirectoryFinder (line 9) | class DirectoryFinder {
method DirectoryFinder (line 13) | private DirectoryFinder() {}
method getPomPath (line 15) | public static File getPomPath(Dependency findPathOfDependency, File ma...
method getMavenRepositoryDir (line 35) | public static File getMavenRepositoryDir(String userSettings, String g...
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/LicenseFinder.java
class LicenseFinder (line 16) | class LicenseFinder {
method LicenseFinder (line 20) | private LicenseFinder() {}
method getLicenses (line 22) | public static List<License> getLicenses(
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScanner.java
class MavenDependencyScanner (line 39) | public class MavenDependencyScanner implements Scanner {
method MavenDependencyScanner (line 46) | public MavenDependencyScanner(LicenseMappingService licenseMappingServ...
method scan (line 50) | @Override
method readDependencyList (line 82) | private static Stream<Dependency> readDependencyList(File pomXml, Mave...
method invokeMaven (line 117) | private static Stream<Dependency> invokeMaven(InvocationRequest reques...
method getMvnPath (line 158) | private static String getMvnPath() {
method createTempFile (line 178) | private static Path createTempFile() {
method findDependency (line 189) | static Dependency findDependency(String line) {
method getItems (line 229) | private static String[] getItems(String line) {
method loadLicenseFromPom (line 249) | private Function<Dependency, Dependency> loadLicenseFromPom(
method loadLicense (line 264) | private static Dependency loadLicense(
method licenseMatcher (line 294) | private static boolean licenseMatcher(
method getSettingsFromCommandLineArgs (line 324) | private static MavenSettings getSettingsFromCommandLineArgs() {
class MavenSettings (line 344) | class MavenSettings {
method MavenSettings (line 349) | MavenSettings(String globalSettings, String userSetttings) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/Setting.java
class Setting (line 5) | class Setting {
method getLocalRepositoryPath (line 9) | public File getLocalRepositoryPath() {
method setLocalRepositoryPath (line 13) | public void setLocalRepositoryPath(File localRepositoryPath) {
method setLocalRepositoryPath (line 17) | public void setLocalRepositoryPath(String localRepositoryPath) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlHandler.java
class SettingsXmlHandler (line 6) | class SettingsXmlHandler extends DefaultHandler {
method startDocument (line 12) | @Override
method startElement (line 17) | @Override
method endElement (line 28) | @Override
method characters (line 33) | @Override
method getSetting (line 40) | public Setting getSetting() {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlParser.java
class SettingsXmlParser (line 10) | public class SettingsXmlParser extends SettingsXmlHandler {
method SettingsXmlParser (line 14) | private SettingsXmlParser() {}
method parseXmlFile (line 16) | public static Setting parseXmlFile(File filePath) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java
class PackageJsonDependencyScanner (line 25) | public class PackageJsonDependencyScanner implements Scanner {
method PackageJsonDependencyScanner (line 35) | public PackageJsonDependencyScanner(
method scan (line 43) | @Override
method dependencyParser (line 61) | private Set<Dependency> dependencyParser(File baseDir, InputFile packa...
method scanDependencies (line 91) | private void scanDependencies(
method readLicense (line 155) | private String readLicense(JsonObject packageJson) {
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicense.java
class ProjectLicense (line 12) | public class ProjectLicense implements Comparable<ProjectLicense> {
method ProjectLicense (line 22) | public ProjectLicense(String projectKey, String license, Boolean allow...
method ProjectLicense (line 29) | public ProjectLicense(String projectKey, String license, String allowe...
method getProjectKey (line 33) | public String getProjectKey() {
method getLicense (line 37) | public String getLicense() {
method getAllowed (line 41) | public Boolean getAllowed() {
method fromString (line 45) | @Deprecated
method compareTo (line 66) | public int compareTo(ProjectLicense o) {
method equals (line 82) | @Override
method hashCode (line 98) | @Override
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java
class ProjectLicenseService (line 17) | @ServerSide
method ProjectLicenseService (line 23) | public ProjectLicenseService(Configuration configuration) {
method getProjectLicenseList (line 28) | public List<ProjectLicense> getProjectLicenseList() {
method getProjectLicenseList (line 46) | public Collection<ProjectLicense> getProjectLicenseList(String project...
FILE: src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtils.java
class IOUtils (line 10) | public class IOUtils {
method IOUtils (line 12) | private IOUtils() {}
method readToString (line 14) | public static String readToString(InputStream input) throws IOException {
FILE: src/main/web/configuration/sonar-api.js
function loadProjectPage (line 3) | function loadProjectPage(allProjects, pageIndex) {
function loadProjects (line 17) | function loadProjects() {
function loadLicenses (line 21) | function loadLicenses() {
function saveLicenses (line 28) | function saveLicenses(items) {
function loadProjectLicenses (line 35) | function loadProjectLicenses() {
function saveProjectLicenses (line 42) | function saveProjectLicenses(items) {
function loadLicenseMappings (line 49) | function loadLicenseMappings() {
function saveLicenseMappings (line 55) | function saveLicenseMappings(items) {
function loadDependencyMappings (line 62) | function loadDependencyMappings() {
function saveDependencyMappings (line 69) | function saveDependencyMappings(items) {
FILE: src/main/web/dashboard/excel-builder.js
function buildExcel (line 1) | function buildExcel(dependencies, licenses) {
function buildDependenciesWorksheet (line 13) | function buildDependenciesWorksheet(dependencies) {
function buildLicensesWorksheet (line 41) | function buildLicensesWorksheet(licenses) {
FILE: src/main/web/property_keys.js
constant KEYS (line 1) | const KEYS = {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/DependencyTest.java
class DependencyTest (line 9) | public class DependencyTest {
method createString (line 17) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetricsTest.java
class LicenseCheckMetricsTest (line 8) | public class LicenseCheckMetricsTest {
method getMetrics (line 10) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinitionTest.java
class LicenseCheckPageDefinitionTest (line 9) | public class LicenseCheckPageDefinitionTest {
method define (line 11) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPluginTest.java
class LicenseCheckPluginTest (line 10) | public class LicenseCheckPluginTest {
method define (line 12) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java
class LicenseCheckRulesDefinitionTest (line 9) | public class LicenseCheckRulesDefinitionTest {
method define (line 11) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java
class LicenseCheckSensorTest (line 29) | public class LicenseCheckSensorTest {
method describe (line 38) | @Test
method execute (line 57) | @Test
method createConfiguration (line 91) | private Configuration createConfiguration() {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseTest.java
class LicenseTest (line 12) | public class LicenseTest {
method createJsonString (line 23) | @Test
method fromStringOld (line 30) | @Test
method fromStringNew (line 37) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/PackageJsonDependencyScannerTest.java
class PackageJsonDependencyScannerTest (line 26) | public class PackageJsonDependencyScannerTest {
method createContext (line 34) | private SensorContext createContext(Path moduleBaseDir) {
method testHappyPath (line 75) | @Test
method testTransitive (line 91) | @Test
method testPackageJsonNotInBaseDir (line 109) | @Test
method testNoPackageJson (line 123) | @Test
method testNoNodeModules (line 132) | @Test
method testLicenseInDeprecatedLicenseFormat (line 141) | @Test
method testLicenseInDeprecatedLicensesFormat (line 153) | @Test
method createScanner (line 169) | private Scanner createScanner() {
method createScanner (line 173) | private Scanner createScanner(boolean resolveTransitiveDeps) {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicensesTest.java
class ValidateLicensesTest (line 26) | public class ValidateLicensesTest {
method setup (line 34) | @Before
method createContext (line 50) | private SensorContext createContext() {
method licenseNotAllowed (line 59) | @Test
method licenseAllowed (line 79) | @Test
method licenseAllowed_scala (line 94) | @Test
method licenseAllowed_kotlin (line 119) | @Test
method checkSpdxOrCombination (line 145) | @Test
method checkSpdxSeveralOrCombination (line 160) | @Test
method checkSpdxAndCombination (line 172) | @Test
method checkSpdxAndCombinationNotAllowed (line 184) | @Test
method checkSpdxAndCombinationNotFound (line 210) | @Test
method checkSpdxOrAndCombination (line 233) | @Test
method licenseNull (line 245) | @Test
method licenseUnknown (line 267) | @Test
method getUsedLicenses (line 284) | @Test
method dependencyMapping (line 300) | @Test
method deps (line 317) | private static Set<Dependency> deps(Dependency... dependencies) {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingServiceTest.java
class DependencyMappingServiceTest (line 18) | public class DependencyMappingServiceTest {
method loadConfiguration (line 20) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java
class GradleDependencyScannerTest (line 25) | public class GradleDependencyScannerTest {
method createContext (line 27) | private SensorContext createContext(File folder) {
method testScannerWithMissingJsonFile (line 39) | @Test
method testScanner (line 46) | @Test
method testScannerWithConfiguredDirectory (line 56) | @Test
method mockLicenseService (line 70) | private LicenseMappingService mockLicenseService() {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingServiceTest.java
class LicenseMappingServiceTest (line 17) | public class LicenseMappingServiceTest {
method getLicenseMap (line 19) | @Test
method mapLicense (line 28) | @Test
method createService (line 37) | private LicenseMappingService createService() {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingTest.java
class LicenseMappingTest (line 14) | public class LicenseMappingTest {
method loadFromJson (line 16) | @Test
method compare (line 26) | @Test
method hash (line 33) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DependencyMappingScannerTest.java
class DependencyMappingScannerTest (line 28) | public class DependencyMappingScannerTest {
method testLicensesAreFound (line 30) | @Test
method testNullMavenProjectDependencies (line 51) | @Test
method testFindDependency (line 63) | @Test
method testFindDependencyWithClassifier (line 77) | @Test
method testSettingsFromMaven (line 91) | @Test
method createContext (line 108) | private SensorContext createContext(File moduleDir) {
method mockLicenseService (line 129) | private LicenseMappingService mockLicenseService() {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinderTest.java
class DirectoryFinderTest (line 8) | public class DirectoryFinderTest {
method checkMavenRepoLocal (line 10) | @Test
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseTest.java
class ProjectLicenseTest (line 18) | public class ProjectLicenseTest {
method getAllProjectLicenses (line 20) | @Test
method getSpecificProjectLicenses (line 28) | @Test
method createProjectLicenseService (line 37) | private ProjectLicenseService createProjectLicenseService() {
FILE: src/test/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtilsTest.java
class IOUtilsTest (line 9) | public class IOUtilsTest {
method testLoadFile (line 11) | @Test
Condensed preview — 87 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (350K chars).
[
{
"path": ".babelrc",
"chars": 62,
"preview": "{\n \"presets\": [\"@babel/preset-env\", \"@babel/preset-react\"]\n}\n"
},
{
"path": ".editorconfig",
"chars": 235,
"preview": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\ncharset = utf-8\ninsert"
},
{
"path": ".github/workflows/build.yml",
"chars": 661,
"preview": "name: Build\n\non:\n push:\n branches: \"**\"\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/"
},
{
"path": ".github/workflows/release.yml",
"chars": 704,
"preview": "name: Release\n\non:\n push:\n tags: [\"v*\"]\n release:\n types: [created]\n\njobs:\n build:\n runs-on: ubuntu-latest\n "
},
{
"path": ".github/workflows/verify.yml",
"chars": 439,
"preview": "name: Verify\n\non:\n pull_request:\n branches: [master]\n\njobs:\n build:\n runs-on: ubuntu-latest\n strategy:\n "
},
{
"path": ".gitignore",
"chars": 127,
"preview": ".classpath\n.project\n.factorypath\n.settings/\ntarget/\nnode_modules/\nnode/\n.idea/\n*.iml\n.vscode/\n.yarn/\ndocker/plugins/*.ja"
},
{
"path": ".prettierignore",
"chars": 14,
"preview": "pnpm-lock.yaml"
},
{
"path": ".prettierrc",
"chars": 63,
"preview": "{\n \"plugins\": [\"prettier-plugin-java\"],\n \"printWidth\": 100\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 4921,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "LICENSE",
"chars": 10769,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 10269,
"preview": "# SonarQube License-Check\n\n[ {\n return window.SonarReques"
},
{
"path": "src/main/web/configuration.js",
"chars": 544,
"preview": "import { TooltipProvider } from \"@sonarsource/echoes-react\";\nimport { createRoot } from \"react-dom/client\";\nimport { Int"
},
{
"path": "src/main/web/dashboard/dashboard.jsx",
"chars": 2853,
"preview": "import { Button, IconDownload } from \"@sonarsource/echoes-react\";\nimport saveAs from \"file-saverjs\";\nimport { useEffect,"
},
{
"path": "src/main/web/dashboard/dependencies.jsx",
"chars": 2927,
"preview": "import {\n IconCheckCircle,\n IconError,\n IconTriangleDown,\n IconTriangleUp,\n} from \"@sonarsource/echoes-react\";\nimpor"
},
{
"path": "src/main/web/dashboard/excel-builder.js",
"chars": 2096,
"preview": "export default function buildExcel(dependencies, licenses) {\n let excelFile = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><?"
},
{
"path": "src/main/web/dashboard/licenses.jsx",
"chars": 2448,
"preview": "import {\n IconCheckCircle,\n IconError,\n IconTriangleDown,\n IconTriangleUp,\n} from \"@sonarsource/echoes-react\";\nimpor"
},
{
"path": "src/main/web/dashboard.js",
"chars": 641,
"preview": "import { TooltipProvider } from \"@sonarsource/echoes-react\";\nimport { createRoot } from \"react-dom/client\";\nimport { Int"
},
{
"path": "src/main/web/property_keys.js",
"chars": 228,
"preview": "export const KEYS = {\n LICENSE_SET: \"licensecheck.license-set\",\n PROJECT_LICENSE_SET: \"licensecheck.project-license-se"
},
{
"path": "src/main/web/shared/styles.css",
"chars": 1053,
"preview": "table.sqlc-data {\n width: 100%;\n border-collapse: collapse;\n}\n\ntable.sqlc-data th {\n color: rgb(29, 33, 47);\n font: "
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/DependencyTest.java",
"chars": 879,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static java.util.Arrays.asList;\nimport static org.hamcrest."
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetricsTest.java",
"chars": 351,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport stati"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinitionTest.java",
"chars": 521,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamc"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPluginTest.java",
"chars": 524,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org."
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinitionTest.java",
"chars": 658,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamc"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java",
"chars": 4462,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static at.porscheinformatik.sonarqube.licensecheck.LicenseC"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseTest.java",
"chars": 1440,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static java.util.Arrays.asList;\nimport static org.hamcrest."
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/PackageJsonDependencyScannerTest.java",
"chars": 6375,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicensesTest.java",
"chars": 10632,
"preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static org.hamcrest.CoreMatchers.*;\nimport static org.hamcr"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingServiceTest.java",
"chars": 2064,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;\n\nimport static at.porscheinformatik.sonarqube.lic"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java",
"chars": 3228,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.gradle;\n\nimport static org.junit.Assert.assertEquals;\nimport static "
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingServiceTest.java",
"chars": 1985,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.licensemapping;\n\nimport static at.porscheinformatik.sonarqube.licens"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingTest.java",
"chars": 1330,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.licensemapping;\n\nimport static org.hamcrest.CoreMatchers.hasItem;\nim"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DependencyMappingScannerTest.java",
"chars": 5322,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport static org.hamcrest.CoreMatchers.endsWith;\nimport sta"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinderTest.java",
"chars": 722,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.io"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseTest.java",
"chars": 2539,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.projectlicense;\n\nimport static at.porscheinformatik.sonarqube.licens"
},
{
"path": "src/test/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtilsTest.java",
"chars": 528,
"preview": "package at.porscheinformatik.sonarqube.licensecheck.utils;\n\nimport static org.hamcrest.CoreMatchers.containsString;\nimpo"
},
{
"path": "src/test/resources/build/my-reports/license-details.json",
"chars": 24313,
"preview": "{\n \"dependencies\": [\n {\n \"moduleName\": \"ch.qos.logback:logback-classic\",\n \"moduleVersion\": \"1.2.3\",\n "
},
{
"path": "src/test/resources/build/reports/dependency-license/license-details.json",
"chars": 24313,
"preview": "{\n \"dependencies\": [\n {\n \"moduleName\": \"ch.qos.logback:logback-classic\",\n \"moduleVersion\": \"1.2.3\",\n "
},
{
"path": "src/test/resources/deprecated_multilicense_project/package.json",
"chars": 244,
"preview": "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"An example module to illustrate the usage of a package.json\""
},
{
"path": "src/test/resources/deprecated_project/package.json",
"chars": 247,
"preview": "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"An example module to illustrate the usage of a package.json\""
},
{
"path": "src/test/resources/example/package.json",
"chars": 387,
"preview": "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"An example module to illustrate the usage of a package.json\""
},
{
"path": "src/test/resources/example_nested/example/package.json",
"chars": 387,
"preview": "{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"description\": \"An example module to illustrate the usage of a package.json\""
},
{
"path": "src/test/resources/no_package_json/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "src/test/resources/spdc_license_list.json",
"chars": 735,
"preview": "{\n \"Glide\": {\n \"name\": \"3dfx Glide License\",\n \"url\": \"http://www.users.on.net/~triforce/glidexp/COPYING.txt\",\n "
},
{
"path": "src/test/resources/test.pom",
"chars": 6,
"preview": "EMPTY\n"
},
{
"path": "test-server.js",
"chars": 861,
"preview": "const http = require(\"http\"),\n httpProxy = require(\"http-proxy\"),\n fs = require(\"fs\");\nconst proxy = httpProxy.createP"
},
{
"path": "webpack.config.js",
"chars": 697,
"preview": "const path = require(\"path\");\n\nmodule.exports = {\n entry: {\n configuration: \"./src/main/web/configuration.js\",\n d"
}
]
About this extraction
This page contains the full source code of the porscheinformatik/sonarqube-licensecheck GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 87 files (315.3 KB), approximately 80.3k tokens, and a symbol index with 269 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.