master 998fe713a3a4 cached
87 files
315.3 KB
80.3k tokens
269 symbols
1 requests
Download .txt
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

[![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=at.porscheinformatik.sonarqube.licensecheck:sonarqube-licensecheck-plugin&metric=alert_status)](https://sonarcloud.io/dashboard?id=at.porscheinformatik.sonarqube.licensecheck:sonarqube-licensecheck-plugin)

This [SonarQube](http://www.sonarqube.org/) plugin ensures that projects use dependencies with compliant licenses. All dependencies and licenses can be viewed per projects and exported to Excel 2003 XML Format. This enables a simple governance of dependencies and licenses for the whole organization.

## License

This software is licensed under the [Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)

## Table of Contents

<!-- 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`.

    ![Transitive](docs/Administration_General_Settings_License_Check_2.png)

### Project Dashboard

The plugin contains a project dashboard showing a list of dependencies with version and a list of all used licences. Each table shows the status of the license
(allowed, not allowed, not found). You can also export the data to Excel.

![Project Dashboard](docs/License_Check_dashboard.png)

## Compatibility

This plugin is compatible:

- 7.x is compatible with SonarQube 25.x / 2025 LTS and 26.x / 2026 LTS version
- 6.x is compatible with SonarQube 9 LTS (>= 9.5) and 10.x
- 5.x is compatible with SonarQube 8.9 LTS and < 10 (9.x is compatible)
- 4.x is compatible with SonarQube 8.x

For all changes see [CHANGELOG.md](CHANGELOG.md)

## Installation

Put the pre-built jar-file (from release downloads) in the directory `$SONARQUBE_HOME/extensions/plugins` and
restart the server to install the plugin. Activate the rules of this plugin ("License is not allowed", "Dependency has unknown license") in your SonarQube quality profiles - otherwise the plugin is not executed.

## Configuration

After booting the SonarQube Server with the License-Check Plugin be found in the tab <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".

    ![License Configuration1](docs/Administration_General_Settings_License_Check_1.png)

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

    ![License Configuration2](docs/Administration_General_Settings_License_Check_3.png)

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

    ![License Configuration2](docs/Administration_General_Settings_License_Check_2.png)

### Configuration via License Menu

Administration -> Configuration(dropdown) -> License Check

![alternative License Configuration1](docs/1-nice-General%20Settings%20-%20Administration.png)

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

  ![alternative License Configuration2](docs/2-nice-License%20Check%20-%20Administration.png)

  ![alternative License Configuration3](docs/3-nice-License%20Check%20-%20Administration.png)

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

  ![alternative License Configuration4](docs/4-nice-License%20Check%20-%20Administration.png)

  ![alternative License Configuration5](docs/5-nice-License%20Check%20-%20Administration.png)

- Under "Dependency Mapping" you can map a dependency name/key (with regex) to a license, e.g. `^asm:asm$` to "BSD-3-Clause"

  ![alternative License Configuration6](docs/6-nice-License%20Check%20-%20Administration.png)

  ![alternative License Configuration7](docs/7-nice-License%20Check%20-%20Administration.png)

- Under "License Mappings" you can map a license name (with regex) to a license, e.g. `.*Apache.*2.*` to "Apache-2.0".

  ![alternative License Configuration8](docs/8-nice-License%20Check%20-%20Administration.png)

  ![alternative License Configuration9](docs/9-nice-License%20Check%20-%20Administration.png)

### Activation rules in Quality Profile

You have to activate the new rules in a (new) quality profile, for each supported language (Groovy, Kotlin, Java, JavaScript, TypeScript) And you have to use this profile for your project.

1. Step 1

   ![activate 1](docs/profile/activate_profile1.png)

2. Step 2

   ![activate 2](docs/profile/activate_profile2.png)

3. Step 3

   ![activate 3](docs/profile/activate_profile3.png)

4. Step 4

   ![activate 4](docs/profile/activate_profile4.png)

5. Step 5

   ![activate 5](docs/profile/activate_profile5.png)

6. Step 6

   ![activate 6](docs/profile/activate_profile6.png)

7. Step 7

   ![activate 7](docs/profile/activate_profile7.png)

## Execution

When a project is analyzed using the `mvn sonar:sonar` in command line the extension is started automatically.

Please make sure to have all dependencies installed before launching the SonarQube analysis. So your complete build
should look something like this:

    mvn -B org.jacoco:jacoco-maven-plugin:prepare-agent install org.jacoco:jacoco-maven-plugin:report
    mvn -B sonar:sonar

## Supported Languages

Groovy, Kotlin, Java, JavaScript, TypeScript

## Supported Project Types

### Maven + NPM

When using Maven and a Javascript Package Manager, define the `sonar.sources` property to point to the files which contain dependency information.

```xml
...
<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
Download .txt
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
Download .txt
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[![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=at.porscheinfo"
  },
  {
    "path": "docker-compose.yml",
    "chars": 816,
    "preview": "version: \"3\"\n\nservices:\n  sonarqube:\n    image: sonarqube:25.3.0.104237-community\n    depends_on:\n      - db\n    environ"
  },
  {
    "path": "jsconfig.json",
    "chars": 41,
    "preview": "{\n  \"include\": [\"./src/main/web/**/*\"]\n}\n"
  },
  {
    "path": "mise.toml",
    "chars": 40,
    "preview": "[tools]\njava = \"temurin-21\"\nmaven = \"3\"\n"
  },
  {
    "path": "package.json",
    "chars": 1347,
    "preview": "{\n  \"name\": \"sonarqube-licensecheck\",\n  \"version\": \"7.0.0\",\n  \"description\": \"License Check Plugin for SonarQube\",\n  \"pr"
  },
  {
    "path": "pom.xml",
    "chars": 8661,
    "preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaL"
  },
  {
    "path": "renovate.json",
    "chars": 742,
    "preview": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\"config:base\", \"group:allNonMajor\"],\n "
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/Dependency.java",
    "chars": 4163,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static at.porscheinformatik.sonarqube.licensecheck.LicenseC"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckMetrics.java",
    "chars": 3183,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport org.sonar.a"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPageDefinition.java",
    "chars": 766,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport org.sonar.api.web.page.Context;\nimport org.sonar.api.web.pa"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java",
    "chars": 7748,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport at.porscheinformatik.sonarqube.licensecheck.dependencymappi"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java",
    "chars": 1410,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\npublic class LicenseCheckPropertyKeys {\n\n    /**\n     * Category f"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckRulesDefinition.java",
    "chars": 2456,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport org.sonar.api.rule.Severity;\nimport org.sonar.api.server.ru"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java",
    "chars": 6929,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport static at.porscheinformatik.sonarqube.licensecheck.LicenseC"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/Scanner.java",
    "chars": 204,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport java.util.Set;\nimport org.sonar.api.batch.sensor.SensorCont"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/ValidateLicenses.java",
    "chars": 9815,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck;\n\nimport at.porscheinformatik.sonarqube.licensecheck.dependencymappi"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMapping.java",
    "chars": 1924,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;\n\nimport java.util.Objects;\n\npublic class Dependen"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java",
    "chars": 1784,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;\n\nimport static at.porscheinformatik.sonarqube.lic"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java",
    "chars": 5753,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.gradle;\n\nimport static at.porscheinformatik.sonarqube.licensecheck.L"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/License.java",
    "chars": 6336,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.license;\n\nimport java.io.StringReader;\nimport java.io.StringWriter;\n"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java",
    "chars": 2748,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.license;\n\nimport static at.porscheinformatik.sonarqube.licensecheck."
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMapping.java",
    "chars": 3422,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.licensemapping;\n\nimport java.io.StringReader;\nimport java.util.Array"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java",
    "chars": 2487,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.licensemapping;\n\nimport static at.porscheinformatik.sonarqube.licens"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/DirectoryFinder.java",
    "chars": 2970,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport at.porscheinformatik.sonarqube.licensecheck.Dependenc"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/LicenseFinder.java",
    "chars": 2056,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport at.porscheinformatik.sonarqube.licensecheck.Dependenc"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScanner.java",
    "chars": 13029,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport at.porscheinformatik.sonarqube.licensecheck.Dependenc"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/Setting.java",
    "chars": 497,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport java.io.File;\n\nclass Setting {\n\n    private File loca"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlHandler.java",
    "chars": 1129,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport org.xml.sax.Attributes;\nimport org.xml.sax.helpers.De"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/maven/SettingsXmlParser.java",
    "chars": 1268,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.maven;\n\nimport java.io.File;\nimport javax.xml.XMLConstants;\nimport j"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java",
    "chars": 7171,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.npm;\n\nimport at.porscheinformatik.sonarqube.licensecheck.Dependency;"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicense.java",
    "chars": 3151,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.projectlicense;\n\nimport java.io.StringReader;\nimport java.util.Array"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java",
    "chars": 2348,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.projectlicense;\n\nimport static at.porscheinformatik.sonarqube.licens"
  },
  {
    "path": "src/main/java/at/porscheinformatik/sonarqube/licensecheck/utils/IOUtils.java",
    "chars": 681,
    "preview": "package at.porscheinformatik.sonarqube.licensecheck.utils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimpo"
  },
  {
    "path": "src/main/resources/at/porscheinformatik/sonarqube/licensecheck/license/spdx_license_list.json",
    "chars": 37012,
    "preview": "[\n  {\n    \"identifier\": \"Glide\",\n    \"name\": \"3dfx Glide License\",\n    \"status\": \"false\"\n  },\n  {\n    \"identifier\": \"Abs"
  },
  {
    "path": "src/main/resources/at/porscheinformatik/sonarqube/licensecheck/licensemapping/default_license_mapping.json",
    "chars": 1602,
    "preview": "[\n  { \"regex\": \"Apple License\", \"license\": \"AML\" },\n  { \"regex\": \".*Apache.*1\\\\.1.*\", \"license\": \"Apache-1.1\" },\n  { \"re"
  },
  {
    "path": "src/main/web/configuration/configuration.jsx",
    "chars": 2427,
    "preview": "import { useEffect, useState } from \"react\";\nimport \"../shared/styles.css\";\nimport DependencyMappingsPage from \"./depend"
  },
  {
    "path": "src/main/web/configuration/dependency-mappings-page.jsx",
    "chars": 8754,
    "preview": "import {\n  Button,\n  ButtonIcon,\n  Checkbox,\n  IconDelete,\n  IconEdit,\n  IconSearch,\n  IconTriangleDown,\n  IconTriangleU"
  },
  {
    "path": "src/main/web/configuration/license-mappings-page.jsx",
    "chars": 7805,
    "preview": "import {\n  Button,\n  ButtonIcon,\n  IconDelete,\n  IconEdit,\n  IconSearch,\n  IconTriangleDown,\n  IconTriangleUp,\n  Label,\n"
  },
  {
    "path": "src/main/web/configuration/licenses-page.jsx",
    "chars": 9267,
    "preview": "import {\n  Button,\n  ButtonIcon,\n  Checkbox,\n  IconCheckCircle,\n  IconDelete,\n  IconEdit,\n  IconError,\n  IconSearch,\n  I"
  },
  {
    "path": "src/main/web/configuration/project-licenses-page.jsx",
    "chars": 9795,
    "preview": "import {\n  Button,\n  ButtonIcon,\n  Checkbox,\n  IconCheckCircle,\n  IconDelete,\n  IconEdit,\n  IconError,\n  IconSearch,\n  I"
  },
  {
    "path": "src/main/web/configuration/sonar-api.js",
    "chars": 2341,
    "preview": "import { KEYS } from \"../property_keys\";\n\nfunction loadProjectPage(allProjects, pageIndex) {\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.

Copied to clipboard!