GitLab CI can scan your code for open-source license compliance, but it’s not a magic bullet; it’s a tool that requires careful configuration and understanding of its limitations.

Here’s what GitLab’s Dependency Scanning feature, when configured for license compliance, looks like in action. Let’s say you have a simple Gemfile in a Ruby project:

source "https://rubygems.org"

gem "rails", "7.0.0"
gem "pg"

When you add the Dependency Scanning template to your .gitlab-ci.yml, it will automatically pick up this Gemfile. Here’s a snippet of what a successful scan might report in your merge request:

{
  "version": 1,
  "scan": {
    "analyzer": {
      "id": "gemnasium",
      "version": "3.2.0"
    },
    "type": "dependency_scanning"
  },
  "data": [
    {
      "category": "dependency_scanning",
      "id": "gemnasium:rails:7.0.0",
      "name": "rails",
      "version": "7.0.0",
      "licenses": [
        {
          "id": "MIT",
          "name": "MIT License",
          "url": "https://opensource.org/licenses/MIT"
        }
      ],
      "location": {
        "file": "/path/to/your/project/Gemfile.lock",
        "dependency": {
          "package": {
            "name": "rails"
          },
          "version": "7.0.0"
        }
      },
      "message": "The `rails` dependency (version 7.0.0) is licensed under the MIT license. Please ensure this license is compatible with your project's licensing requirements.",
      "severity": "info",
      "type": "dependency_scanning"
    },
    {
      "category": "dependency_scanning",
      "id": "gemnasium:pg:1.4.4",
      "name": "pg",
      "version": "1.4.4",
      "licenses": [
        {
          "id": "MIT",
          "name": "MIT License",
          "url": "https://opensource.org/licenses/MIT"
        }
      ],
      "location": {
        "file": "/path/to/your/project/Gemfile.lock",
        "dependency": {
          "package": {
            "name": "pg"
          },
          "version": "1.4.4"
        }
      },
      "message": "The `pg` dependency (version 1.4.4) is licensed under the MIT license. Please ensure this license is compatible with your project's licensing requirements.",
      "severity": "info",
      "type": "dependency_scanning"
    }
  ]
}

This output, when integrated into GitLab, appears as a "Security" tab in your merge request, highlighting each dependency, its version, and its identified licenses. The severity: "info" is important here; by default, license findings are informational. You’ll need to configure rules to treat certain licenses as violations.

The core problem this feature solves is the manual, error-prone process of tracking third-party library licenses. Without it, you’d be digging through each project’s LICENSE file, cross-referencing with your company’s legal policies, and hoping you didn’t miss anything. GitLab Dependency Scanning automates this by:

  1. Identifying Dependencies: It uses language-specific tools (like bundler for Ruby, npm for Node.js, pip for Python, etc.) to generate a lock file or manifest of all direct and transitive dependencies.
  2. Querying Vulnerability Databases: While primarily for security vulnerabilities, the underlying scanners (like Gemnasium, developed by GitLab) also maintain a database of open-source licenses associated with known package versions.
  3. Reporting Findings: It outputs a standardized JSON format (Security Report) that GitLab’s UI can parse and display.

To get this working, you’d typically add a template to your .gitlab-ci.yml:

include:
  - template: Security/Dependency-Scanning.gitlab-ci.yml

This single line pulls in a pre-configured job that runs the appropriate scanner for your project’s language. The scanner analyzes your dependency manifest files (e.g., package-lock.json, Gemfile.lock, Pipfile.lock, pom.xml, build.gradle).

The real power comes from configuring the dependency_scanning job and setting up rules. You can define which licenses are acceptable and which are not. For example, to fail a pipeline if a GPL-3.0 license is detected, you’d add a rules section to your CI configuration:

dependency_scanning:
  # ... other configurations if needed ...
  rules:
    - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "master"'
      when: on_success
    - when: manual # Or 'never' if you don't want it to run on other branches
  variables:
    LICENSE_EXPD_SEVERITY_LEVEL: "critical" # Default, but can be adjusted
    LICENSE_FINDING_SEVERITY_LEVEL: "high" # Default, but can be adjusted
    # Define specific license policies
    LICENSE_POLICY_FILE: ".gitlab/license_policy.yml"

And your .gitlab/license_policy.yml might look like this:

policies:
  - name: "Restrict GPL-3.0"
    license_codes:
      - "GPL-3.0-only"
      - "GPL-3.0-or-later"
    allow: false
    severity: "critical"

When GitLab processes the scan results, it compares the detected licenses against these policies. If a dependency’s license matches a policy with allow: false, the finding’s severity is elevated, and depending on your CI job’s rules and allow_failure settings, the pipeline can be configured to fail.

The most surprising thing about GitLab’s license compliance scanning is that it doesn’t directly enforce license compatibility; it reports detected licenses and allows you to define policies to trigger pipeline failures based on those reports. The actual legal interpretation and decision-making still rest with your team and legal counsel. The tool is an aid to process, not a replacement for due diligence. It’s also crucial to remember that the scanner relies on its internal database of licenses. If a new or obscure license is used by a dependency, or if the database is out of date, it might not be detected or correctly identified. Furthermore, transitive dependencies can become complex; the scanner aims to uncover these, but very deep dependency trees can sometimes present challenges.

The next step after setting up basic license scanning is to explore integrating with dedicated Software Composition Analysis (SCA) tools for more in-depth analysis and broader policy management.

Want structured learning?

Take the full Gitlab-ci course →