JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 126
  • Score
    100M100P100Q85579F
  • License MIT

Tool to scan for licenses in our dependencies, and report on any that are not allow-listed.

Package Exports

  • @worktango/license-scanner
  • @worktango/license-scanner/build/src/scanner.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@worktango/license-scanner) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@worktango/license-scanner

A tool to scan for licenses in your dependencies and report on any that are not allow-listed.

⚠️ By default, this library uses a naive glob-based search of package.json files to find dependencies. This is not a comprehensive solution and may miss some dependencies, but is suitable for WorkTango's use case. Read the documentation below for information on how to customize the scanner to your needs.

The node/NPM ecosystem is the primary focus of this package, but custom license scanners can be passed in to allow this to work for any dependency ecosystem.

Features

  • Scans all production dependencies for their licenses
  • Injectable scanners for different dependency ecosystems
  • Configurable allowed license list
  • CLI tool with JSON output option
  • Configurable logging levels
  • Exit code support for CI/CD pipelines

Installation (optional)

# Yarn
yarn add @worktango/license-scanner@latest

# NPM
npm install @worktango/license-scanner@latest

# PNPM
pnpm add @worktango/license-scanner@latest

Usage

Fastest way to get started:

$ npx @worktango/license-scanner

CLI

# Run with default settings
npx -y @worktango/license-scanner

# Output as JSON
npx @worktango/license-scanner --json

# Set logging level
npx @worktango/license-scanner --logLevel debug

# Skip failure when disallowed licenses are found
npx @worktango/license-scanner --pass-on-disallowed

# Output as CSV
npx @worktango/license-scanner --csv-output licenses.csv

API

import { scanLicenses } from "@worktango/license-scanner";

async function checkLicenses() {
  const results = await scanLicenses({});

  if (results.hasDisallowedLicenses) {
    console.error(
      "Found disallowed licenses:",
      results.dependenciesWithDisallowedLicenses
    );
    process.exit(1);
  }
}

CLI Options

  • --pass-on-disallowed, -p: Skip failure when disallowed licenses are found (default: false)
  • --logLevel, -l: Set logging level [error|warn|info|debug] (default: "info")
  • --json, -j: Output results as JSON (default: false)
  • --csv-output: Output results as CSV to specified file (e.g., --csv-output licenses.csv)
  • --config, -c: Path to configuration file (e.g., licenses.json)
  • --help, -h: Show help

Configuration File

You can provide a JSON configuration file to customize the allowed licenses and ignored dependencies. The file should have the following structure:

{
  "allowedLicenses": ["MIT", "ISC", "/^Apache-2\\.0$/", "/^BSD-[23]-Clause$/"],
  "ignoredDependencies": ["@internal/package", "/^@company\\/.+/"]
}

Configuration Options

  • allowedLicenses: Array of strings or regex patterns for allowed licenses
  • ignoredDependencies: Array of strings or regex patterns for dependencies to ignore

Regex patterns should be specified as strings with the format /pattern/flags. For example:

  • /^MIT$/ - Exact match for "MIT"
  • /Apache.*/i - Case-insensitive match for licenses starting with "Apache"

Custom License Policies

You can customize the allowed licenses by implementing your own getAllowedLicense function:

import {
  scanLicenses,
  AllowedLicensePolicy,
} from "@worktango//license-scanner";

async function customGetAllowedLicenses(): Promise<AllowedLicensePolicy[]> {
  return [
    "MIT",
    /^Apache-2\.0$/,
    async (dependency) => {
      // Custom async validation logic
      return dependency.name.startsWith("@your-org/");
    },
  ];
}

const results = await scanLicenses({
  getAllowedLicense: customGetAllowedLicenses,
});

Custom Scanners

The scanner uses a default scanner for NPM dependencies, but custom scanners can be passed in to allow this to work for any dependency ecosystem.

For example, to use the license-checker tool, you can pass in the following custom scanner:

import { exec } from "child_process";

const results = await scanLicenses({
  getLicenses: async () => {
    const licenses = await exec("npx -y license-checker --json --production");
    return JSON.parse(licenses);
  },
});

getLicenses should return an array of objects with the following shape:

export type Dependency = {
  /**
   * Name of the dependency
   */
  name: string;
  /**
   * Version of the dependency
   */
  version: string;
  /**
   * Path to the dependency's artifact, e.g., a package.json file
   */
  artifactPath: string;
  /**
   * The licenses for the dependency
   */
  licenses?: string | string[] | { type: string }[] | { type: string };
  /**
   * The repository for the dependency
   */
  repository?: string;
};

⚠️ CI Job Failing Due to License Check?

If you're here because your CI job failed due to a license check:

  1. First, check which dependency is causing the issue in your CI logs
  2. Look up that dependency's license on their repository or npm page
  3. If the license is in our allow list below, it might be that:
    • The license is incorrectly specified in their package.json
    • The package has multiple licenses
    • The license format doesn't match our expected format
  4. If the license is NOT in our allow list:
    • Consider finding an alternative package with an allowed license
    • If the package is essential, feel free to create a PR adding the license to our allow list (it's easy!) to start the discussion

Common Issues and Solutions

  1. Multiple Licenses: If a package has multiple licenses (e.g., "MIT OR Apache-2.0"), it will pass if any of the licenses are allowed.

  2. License Format Mismatch: Sometimes packages specify licenses in non-standard ways:

  • e.g., "MIT License" instead of "MIT"
  • In these cases, you can:
    • Open a PR to fix the format in the package's repository
    • Contact the package maintainer
    • As a last resort, discuss adding a regex pattern to our allow list
  1. Missing License: If a package doesn't specify its license properly:
    • Check the package's repository for a LICENSE file
    • Contact the package maintainer
    • Consider finding an alternative package

Publishing a new version

To publish a new version, you need to work at WorkTango. Update the version in the package.json file in this package's directory of the monorepo, commit the change, and merge it to master. The version will be published to npm automatically by CircleCI.

⚠️ We follow Semantic Versioning, so make sure to update the version number appropriately and document any breaking changes in the CHANGELOG.md file.

Output Formats

CSV Output

When using the --csv-output option, the tool will generate a CSV file with the following columns:

  • name: Name of the dependency
  • version: Version of the dependency
  • licenses: License(s) of the dependency (multiple licenses are semicolon-separated)
  • repository: Repository URL (if available)
  • artifactPath: Path to the dependency's artifact
  • isDisallowed: Whether the license is disallowed (true/false)

Example:

name,version,licenses,repository,artifactPath,isDisallowed
lodash,4.17.21,MIT,https://github.com/lodash/lodash,node_modules/lodash/package.json,false
express,4.17.1,MIT,https://github.com/expressjs/express,node_modules/express/package.json,false

Architecture

This scanner is built as a wrapper around other open-source tools, but shipped with a set of defaults suitable for WorkTango.

Out of the box, the scanner uses a naive glob-assisted search of package.json files to find NPM dependencies and licenses; and Ruby's license_finder gem to find licenses for the Ruby ecosystem.

We want to keep the number of dependencies to a minimum to avoid licensing issues and allow us to ship this as a zero-dependency package that can very quickly be downloaded, run via npx, and used across repositories.