Metadata-Version: 2.4
Name: licsniff
Version: 0.1.0
Summary: Audit your dependencies' licenses offline and flag copyleft (GPL/LGPL/AGPL/MPL) risk. Scans site-packages, no account, no network. Zero dependencies.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://github.com/jjdoor/licsniff-py
Project-URL: Repository, https://github.com/jjdoor/licsniff-py
Project-URL: Issues, https://github.com/jjdoor/licsniff-py/issues
Keywords: license,licenses,license-checker,spdx,copyleft,gpl,compliance,audit,dependencies,cli,devtools,offline
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Legal Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# licsniff

**Audit your dependencies' licenses, offline.** Point it at a virtualenv and get
a clean table of every installed package, its license, and a **risk tier** —
`permissive`, `weak-copyleft`, `strong-copyleft`, `proprietary`, or `unknown` —
so a stray GPL transitive dep can't sneak into your closed-source product.

No account. No network. No config. Zero dependencies (pure standard library).

```bash
pipx run licsniff
```

```
PACKAGE          VERSION  LICENSE                     RISK
some-gpl-lib     2.1.0    GPL-3.0                     strong-copyleft
mystery-pkg      0.0.3    (none)                      unknown
copyleft-utils   1.4.0    LGPL-2.1                    weak-copyleft
requests         2.32.3   Apache-2.0                  permissive
packaging        24.1     Apache-2.0 OR BSD-2-Clause  permissive
```

Riskiest first, so the line you need to worry about is at the top.

## Why another license tool?

The hosted options — Snyk, FOSSA, Black Duck — all want a signup, a token, and a
network round-trip before they'll tell you something your `site-packages`
already knows. The Node ecosystem's go-to,
[`license-checker`](https://www.npmjs.com/package/license-checker), has been
unmaintained for years and doesn't read Python anyway.

`licsniff` is the gap: it reads the `*.dist-info/METADATA` files already on your
disk, classifies each license locally, and exits. Nothing leaves the machine.

It also does the part those tools are vague about — **tiering by actual risk**.
Knowing a dep is "GPL-3.0" only helps if you know GPL is *strong copyleft* and
MPL is *weak*. licsniff bakes that in, understands SPDX expressions
(`(MIT OR Apache-2.0)` → least-restrictive, `GPL-3.0 AND MIT` →
most-restrictive), and normalizes the messy variants (`GPLv3`, `GPL-3.0+`,
`GPL-3.0-only`, `Apache License 2.0`).

## Install

```bash
pipx run licsniff           # no install, run on demand
pip install licsniff        # or install the `licsniff` command
```

There's an identical Node build too: `npx licsniff` / `npm i -g licsniff`
(see [licsniff](https://github.com/jjdoor/licsniff)) — it audits a Node project's
`node_modules` instead of `site-packages`. Both ports share the exact same
classifier, tested against the same vectors, so they tier licenses byte-for-byte
the same.

## Usage

```bash
licsniff [options]               # scans the active venv's site-packages
licsniff --path <dir> [options]  # scan a specific site-packages folder
```

By default licsniff resolves your `$VIRTUAL_ENV` site-packages, falling back to
`./.venv/lib/python*/site-packages`. Pass `--path` to point it anywhere.

| Option | Description |
| --- | --- |
| `--path <dir>` | `site-packages` dir to scan. |
| `--summary` | Print counts per risk tier instead of the full table. |
| `--json` | Machine-readable JSON (`{path, total, counts, packages[]}`). |
| `--fail-on <tier>` | Exit `1` if any package is **at or above** `<tier>`. CI gate. |
| `--no-color` | Disable ANSI color. |
| `-h, --help` | Show help. |
| `-v, --version` | Print version. |

### Risk tiers

| Tier | Examples | What it means |
| --- | --- | --- |
| `permissive` | MIT, ISC, BSD-2/3-Clause, Apache-2.0, 0BSD, Unlicense, CC0 | Use freely, just keep the notice. |
| `weak-copyleft` | LGPL-\*, MPL-2.0, EPL-\*, CDDL-\* | File-level / linking obligations. |
| `strong-copyleft` | GPL-\*, AGPL-\* | Can force you to open-source your code. |
| `proprietary` | UNLICENSED, "SEE LICENSE IN …" | Not open source — read the terms. |
| `unknown` | missing / unrecognized | No idea — investigate manually. |

### Examples

```bash
# counts at a glance
licsniff --summary

# CI gate: fail the build if anything copyleft-or-worse slipped in
licsniff --fail-on strong-copyleft

# pipe to jq
licsniff --json | jq '.packages[] | select(.tier=="unknown") | .name'

# audit a specific environment
licsniff --path /path/to/venv/lib/python3.11/site-packages
```

## Design notes

- **One pure function at the core.** `classify_license(id_or_name)` →
  `{tier, spdx}` has no I/O, no clock, no globals. The CLI is a thin
  `site-packages` reader wrapped around it. That's what makes the Node and
  Python ports verifiably identical — they share one test table.
- **It reads the real METADATA fields.** The modern PEP 639
  `License-Expression:` (SPDX), the legacy `License:` field, and the
  `License :: …` trove classifiers — preferring the classifier when the
  `License:` field is empty, `UNKNOWN`, or a pasted-in wall of license text.
- **SPDX expressions are evaluated, not guessed.** `OR` picks the *least*
  restrictive option (you get to choose), `AND` picks the *most* restrictive
  (you must satisfy all). `WITH` exception clauses fall back to the base license.
- **Fully offline, read-only.** It never writes anything and never opens a
  socket. Safe to run anywhere, including air-gapped CI.

## License

MIT
