Add LicenseExpressionChecker with feature flag and denylist AND operator

What does this MR do and why?

Introduces Security::MergeRequestApprovalPolicies::LicenseExpressionChecker behind the license_expression_checker wip feature flag (disabled by default). The checker handles the denylist path for SPDX compound license expressions, starting with AND expressions and plain single-identifier licenses.

Gitlab::SPDX::ExpressionParser (copied from !235888 (merged)) produces a Node tree from an SPDX expression string. The checker recursively walks the tree so that multi-term AND expressions (e.g. MIT AND Apache-2.0 AND GPL-3.0-only) are fully evaluated — denying any single component is a violation.

SPDX IDs in expression nodes are resolved to display names via Gitlab::SPDX::Catalogue before comparison, matching the format that policies store (e.g. MIT → MIT License). The catalogue is already cached for 7 days so there is no performance concern.

UpdateLicenseApprovalsService branches between DeniedLicensesChecker and LicenseExpressionChecker based on the flag. DeniedLicensesChecker continues to handle all traffic until the flag is enabled.

How to set up and validate locally

  1. Enable the license_expression_checker feature flag on the rails console
Feature.enable(:license_expression_checker)
  1. Create a new project
  2. Go to Secure > Policies
  3. Click in New policy
  4. Select Merge request approval policy
  5. Create a policy to block MIT License:
approval_policy:
  - name: block MIT
    description: ''
    enabled: true
    enforcement_type: warn
    rules:
      - type: license_finding
        match_on_inclusion_license: true
        licenses:
          denied:
            - name: MIT License
        license_states:
          - newly_detected
        branch_type: protected
    actions:
      - type: require_approval
        approvals_required: 1
        role_approvers:
          - developer
      - type: send_bot_message
        enabled: true
  1. Add an empty Gemfile.lock file
  2. Add a file called gl-sbom-gem-bundler.cdx.json with the content to report the license expression MIT AND Apache-2.0
{
    "bomFormat": "CycloneDX",
    "specVersion": "1.4",
    "serialNumber": "urn:uuid:a15e529c-2113-4a11-a694-6bc3ea4e2b53",
    "version": 1,
    "metadata": {
        "timestamp": "2022-02-23T08:02:39Z",
        "tools": [
            {
                "vendor": "GitLab",
                "name": "Gemnasium",
                "version": "2.34.0"
            }
        ],
        "authors": [
            {
                "name": "GitLab",
                "email": "support@gitlab.com"
            }
        ],
        "properties": [
            {
                "name": "gitlab:dependency_scanning:input_file:path",
                "value": "Gemfile.lock"
            },
            {
                "name": "gitlab:dependency_scanning:package_manager:name",
                "value": "bundler"
            },
            {
                "name": "gitlab:meta:schema_version",
                "value": "1"
            }
        ]
    },
    "components": [
        {
            "name": "sidekiq",
            "version": "4.2.10",
            "purl": "pkg:gem/sidekiq@4.2.10",
            "type": "library",
            "bom-ref": "pkg:gem/sidekiq@4.2.10",
            "licenses": [
                {
                    "license": {
                        "name": "MIT AND Apache-2.0"
                    }
                }
            ]
        }
    ]
}
  1. Create a new MR adding a .gitlab-ci.yml with the content
include:
  - template: Jobs/Dependency-Scanning.gitlab-ci.yml

gemnasium-dependency_scanning:
  stage: test
  script: 'pwd'
  artifacts:
    reports:
      cyclonedx: gl-sbom-gem-bundler.cdx.json
  1. Run a pipeline
  2. Verify the MR is blocked and required approval for the policy

References

Edited by Marcos Rocha

Merge request reports

Loading
Loading