Documentation
Install LeakLens, run a first audit, understand every detector, and wire the exit code into CI. Everything here applies to the open-source CLI — no hosted service is required.
Install
The PyPI package is leaklens-ml; the installed command is leaklens. Python 3.10 or newer is required, and the base install needs no account, key, or network access.
python -m pip install --upgrade leaklens-ml
leaklens --version
leaklens doctor
leaklens doctor checks the interpreter, install location, entry point, core dependencies, optional extras and TOML validity without a network call. A missing optional extra is reported but does not fail the check.
py -3 -m pip install --upgrade leaklens-ml if python is not on PATH. Linux/macOS: if pip reports externally-managed-environment, create a virtual environment first.Optional extras
| Extra | Adds | Install |
|---|---|---|
parquet | Parquet input via pyarrow | pip install "leaklens-ml[parquet]" |
feather | Feather input via pyarrow | pip install "leaklens-ml[feather]" |
excel | Excel input via openpyxl | pip install "leaklens-ml[excel]" |
images | Near-duplicate image check via Pillow | pip install "leaklens-ml[images]" |
arena | Three-model arena with MLflow tracking | pip install "leaklens-ml[arena]" |
app | FastAPI service and Streamlit UI | pip install "leaklens-ml[app]" |
taintwatch | Optional guard for --llm | pip install "leaklens-ml[taintwatch]" |
dev | pytest, ruff, bandit, pip-audit and release tools | pip install "leaklens-ml[dev]" |
Quickstart
Run a dataset audit with a label, compare a held-out split, and scan a codebase. The same audit can write JSON, HTML, SARIF, or a shields.io badge.
# Single-table audit
leaklens train.csv --target label
# Compare train and test, including temporal ordering
leaklens train.csv --test test.csv --target label --time date
# Statically scan Python files and notebooks
leaklens --scan src/ --explain
# Gate CI on the trust score
leaklens train.csv --target label --fail-under 80
The CLI exits 1 when any high-severity finding is present, or when --fail-under is set and the trust score is lower. That single rule works in GitHub Actions, pre-commit, Makefiles, and any shell.
From Python
import pandas as pd
from leaklens.leakage import scan, score
df = pd.read_csv("train.csv")
findings = scan(df, "label")
print(f"trust score: {score(findings)}/100")
for finding in findings:
print(finding.severity, finding.kind, finding.column, finding.detail)
Scanning source uses leaklens.scanner.scan_source(source, name="snippet.py"). It parses the code; it never imports or executes it.
New to the problem? The walkthrough notebook plants three leaks, shows the inflated score, then fixes them. The interactive demo replays real scanner output for the same kinds of case.
Detections
Severity is about confidence in the observed pattern. High means the code or data satisfies a confirmed leakage rule; medium means the pattern is suspicious and context-dependent; low means a reproducibility or hygiene issue that rarely changes a score on its own.
Dataset checks
| Kind | What it observes | Severity |
|---|---|---|
target_leakage | One feature alone predicts the target above the threshold (cross-validated ROC AUC, R² or balanced accuracy). | high / medium |
categorical_target_leakage | A high-cardinality category is near-pure with respect to the label (conditional entropy). | high / medium |
id_like | At least 98% unique values in a column; likely an identifier. | medium |
split_overlap | Exact row hashes occur in both train and test. | high |
feature_overlap | Identical feature vectors across the split; conflicting labels raise it to high. | high / medium |
near_duplicate_overlap | Rows match after case/whitespace normalisation and rounding to four decimals. | medium |
temporal_leakage | Training timestamps extend beyond the start of the test period. | high |
group_overlap | The same entity appears in both splits when a --group column is supplied. | high / medium |
group_leakage_risk | Group sizes are small or uneven enough to make entity leakage risky. | medium |
Code checks: pipeline order
| Kind | What it observes | Severity |
|---|---|---|
preprocess_before_split | A scaler, imputer or transformer is fitted before the split. | high |
transformer_before_split | An unrecognised transformer is fitted before the split. | medium |
encoder_before_split | A benign encoder is fitted before the split. | low |
fillna_before_split | Imputation statistics are learned from the full frame. | medium |
get_dummies_before_split | Category levels are fixed from the full frame. | low |
manual_scale_before_split | Manual mean/std or min/max scaling is computed before the split. | high |
resample_before_split | SMOTE or another sampler runs before train_test_split. | high |
target_encode_before_split | Target means or aggregates become a feature before the split. | high |
feature_selection_before_split | SelectKBest, RFE, SelectFromModel or similar is fitted before the split. | high |
frequency_encode_before_split | value_counts is mapped into a column before the split; no label involved. | low |
future_target_feature_before_split | A future-window target statistic is used as a feature. | high |
group_stat_before_split | A per-entity statistic is computed across the full frame before the split. | medium |
preprocess_outside_cv | Preprocessing happens before cross_val_score or GridSearchCV. | high / low |
resample_outside_cv | Resampling happens before cross-validation. | high |
fit_on_full_data | A transformer is fitted on the full frame after the split exists. | medium |
fit_on_eval_data | A transform is fitted on the test or validation partition. | high / medium |
Code checks: tuning, time and reproducibility
| Kind | What it observes | Severity |
|---|---|---|
tuning_on_test_data | A search or manual loop fits on the test partition. | high |
tuning_before_split | A search or CV runs on the full dataset before the split. | medium |
tuning_on_full_data | A search is fitted on the original frame after the split exists. | medium |
cv_on_test_data | Cross-validation runs over the held-out partition. | medium |
test_set_reuse | The test partition is scored three or more times, i.e. used for selection. | low |
group_cv_missing_groups | A group splitter is used without groups=. | medium |
temporal_split_shuffle | A time-sorted frame is shuffled by train_test_split. | high / medium |
no_random_state | A split or sampler omits random_state. | low |
notebook_out_of_order_execution | Notebook execution counts are non-monotonic. | low |
parse_error | A file could not be parsed; it is reported instead of silently skipped. | low |
Every kind has a remediation string in leaklens/fixes.py, printed by --explain and shown in the demo. The scanner never executes the code it reads.
CLI reference
Dataset audit
leaklens train.csv --target label \
--test test.csv --group patient_id --time date \
--fail-under 80 --html report.html
| Flag | Meaning |
|---|---|
train | Path to the training dataset (positional). CSV and JSON work in the base install. |
--target | Label column in the training data. |
--test | Held-out split to compare against; enables the duplicate, temporal and group checks. |
--time | Timestamp column for temporal leakage. |
--group | Entity column (patient, customer, device) for group leakage. |
--threshold | Single-feature score that counts as leakage (default 0.95). |
--fail-under | Exit 1 when the trust score is below N. |
--json | Emit the report as JSON. |
--explain | Print the suggested fix under each finding. |
--intelligence | Deterministic confidence, root causes and remediation ranking. |
--html | Write a single-file HTML report. |
--sarif | Write SARIF 2.1.0 for GitHub code scanning. |
--badge | Write a shields.io endpoint JSON with the score. |
--arena | Compare three model families with MLflow tracking (needs arena). |
--llm | Optional LLM explanations; off by default, local Ollama by default. |
Code scan
leaklens --scan src/ tests/ --explain
leaklens --scan . --sarif leaklens.sarif
--scan accepts any number of .py, .ipynb files or directories, ignores common environment folders, and reports parse failures instead of guessing. It works on Windows, Linux and macOS.
Other commands
| Command | Purpose |
|---|---|
leaklens doctor [--json] | Check the install and optional dependencies offline. |
leaklens init [dir] | Write a commented starter .leaklens.toml and print the pre-commit snippet. |
leaklens images TRAIN TEST | Near-duplicate images across split folders (needs images). |
leaklens --version | Print the installed version and exit. |
Configuration
leaklens init writes a documented .leaklens.toml. The file is a reference today: the CLI does not read it yet, and explicit flags always win. Treat the generated file as documentation for the options you may want to pass.
[leaklens]
# threshold = 0.95 # --threshold
# explain = false # --explain
# fail_under = 80 # --fail-under
# sarif = "leaklens.sarif" # --sarif
# html = "report.html" # --html
# badge = "badge.json" # --badge
# scan = ["notebooks"] # --scan
This is an honest limitation, not a hidden feature. If you need configuration in CI today, put the flags in the workflow or a shell script.
CI and pre-commit
LeakLens is designed to fail a build on a confirmed leak. Add the scan to any workflow; SARIF uploads to GitHub code scanning when you also pass --sarif.
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: pip install leaklens-ml
- run: leaklens --scan src/ --explain
- run: leaklens train.csv --target label --fail-under 80
repos:
- repo: https://github.com/eyadstein/leaklens
rev: v0.13.0
hooks:
- id: leaklens
The hook scans staged Python and Jupyter files. It is fast because it only parses source; no dataset is read and no model is trained.
Reports and exports
| Output | Flag | Notes |
|---|---|---|
| Terminal | default | Trust score, findings, counts; --explain adds a fix under each. |
| JSON | --json | Stable shape: trust_score, findings, audit stats, optional intelligence. |
| HTML | --html FILE | Single self-contained file, no external assets. Example report. |
| SARIF | --sarif FILE | SARIF 2.1.0 for GitHub code scanning. |
| Badge | --badge FILE | shields.io endpoint JSON with the trust score. |
| Intelligence | --intelligence | Deterministic confidence, root causes, impact analysis and prioritised fixes. |
| LLM notes | --llm | Opt-in. Sends findings, never dataset rows or full source. Local Ollama by default. |
| AI hypotheses | --ai | Opt-in, offline and experimental. Adds an ai object and a separate report section; never changes findings or the score. |
The trust score is max(0, 100 − 35·high − 15·medium − 5·low). It is computed centrally by leaklens.leakage.score so the CLI, API, badge, and HTML report cannot disagree.
Experimental AI intelligence
An optional, offline layer adds evidence-bound hypotheses and a root-cause remediation plan on top of the deterministic detectors. It is off by default, needs no GPU, network, paid service or credentials, and is available in the base install.
leaklens --scan notebooks/ --ai
leaklens train.csv --target label --ai
leaklens --scan src --ai-llm
It combines deterministic semantic rules (pipeline order, future information, target-proxy names, identifier names, shared keys) with a small in-memory CPU model trained on grouped, labelled code patterns. Every hypothesis carries inspectable evidence and an explicit limitation. Code is never executed.
info notice and never followed.POST /audit and POST /scan accept include_ai=true; the response adds an ai object and leaves the deterministic score and findings unchanged. The live page has an explicit opt-in checkbox and renders AI output in a separate panel.
Measured precision, recall, F1, false-positive rate, false positives, misses and limitations are in AI_BENCHMARK.md, with the design and safety model in AI_INTELLIGENCE.md. The independent real-world check is AI_REALWORLD.md: 48 reviewed units from 12 public repositories, measured at accuracy 0.833, precision 0.767, recall 0.958 and FPR 0.292, with every mistake listed. It is a real measurement, not a production-accuracy claim, and is lower than the authored locked test. The layer is not claimed as an improvement over the deterministic scanner.
API and UI
Install the app extra, then run the FastAPI service or the Streamlit UI. Both are optional; the CLI has no server dependency.
pip install "leaklens-ml[app]"
uvicorn leaklens.api:app --port 8000
streamlit run leaklens/ui.py
| Endpoint | Input | Returns |
|---|---|---|
GET /health | — | {"status": "ok", "version": "..."} |
POST /audit | Multipart CSV/JSON file + target + optional threshold | Trust score, audit stats and findings. |
POST /scan | JSON {"source": "...", "name": "snippet.py"} | Findings and the aggregate trust score from the static scanner. |
POST /predict | JSON {"rows": [...], "model_path": "models/best.joblib"} | Predictions from a locally trained arena model. |
/predict only loads models under ./models/; joblib artifacts are still pickle files, so only place models you trust there.Public live-scan API
The static website can connect to a separate, hardened API for real scans in the browser. It intentionally exposes no /predict endpoint and no model deserialization:
pip install "leaklens-ml[api]"
uvicorn leaklens.public_api:app --host 127.0.0.1 --port 8000
GET /health and GET /capabilities describe the deployment. POST /inspect lists columns, POST /audit runs the dataset detectors (optionally with a held-out file plus group and time columns), and POST /scan runs the static Python scanner. Uploads default to 4 MiB, 5,000 rows and 200 columns, are parsed in memory, and are never retained.
Dockerfile.api and render.yaml are documented in the repository; the free instance sleeps after idle time, has 512 MB RAM, and offers no persistent storage. See LIVE_SCANNING.md for verified limits and the security model.Docker users can run docker compose up --build for the API on port 8000 and the UI on 8501.
Benchmarks
The scanner benchmark is a regression guard built from labeled fixtures. It is not a claim of accuracy on your codebase.
| Suite | Cases | Result | Command |
|---|---|---|---|
| Scanner (combined) | 109 | 58 leaky / 51 clean · precision 1.00 · recall 1.00 | python -m leaklens.bench |
| Advanced tuning and encoding | 32 | All expected classifications | python -m leaklens.bench_advanced |
| Dataset detectors | 12 | 9 leaky / 3 clean · precision 1.00 · recall 1.00 | python -m leaklens.bench_data |
| Experimental AI layer | 72 | Grouped CV + external transfer; reported honestly, not an improvement claim | python -m leaklens.bench_ai |
Generated results are committed to docs/BENCHMARK.md and docs/DATA_BENCHMARK.md; CI runs all three suites.
Limitations
- Static code analysis. Source is parsed, never executed. Runtime data flow, dynamic dispatch and information from external state can be missed.
- Thresholds. Target leakage is a statistical test. A strong legitimate feature can be flagged; a weak leak can fall below the threshold.
- Bounded duplicate checks. Feature overlap samples up to 50,000 rows and near-duplicates up to 20,000. Matching is exact after normalisation, not fuzzy.
- No auto-fix. Every finding has a remediation hint, but changes must be reviewed and re-validated.
- Not a privacy scanner. LeakLens does not detect or redact personal data.
- LLM explanations are opt-in. Only finding text is sent; dataset rows and full source are not. The default provider is a local Ollama endpoint.
- AI hypotheses are experimental. They are evidence-bound and offline, never change deterministic findings or the trust score, and can be wrong. A clean AI pass does not prove the pipeline is risk-free.
- The website demo is prerecorded. GitHub Pages is static, so the demo replays committed fixture output and never simulates a backend.
Troubleshooting
A format raises ImportError
The message names the extra that provides the reader. Install it, for example pip install "leaklens-ml[parquet]" or pip install "leaklens-ml[excel]".
A file with no extension is read as CSV
Unknown suffixes fall back to CSV by design. Pass a filename with the correct suffix or convert the file first.
The command exits 1 but no high finding is printed
Check whether --fail-under is set: the gate also fails when the trust score is below the floor. Remove the flag or raise the score.
My .leaklens.toml is ignored
This is expected in 0.13.0. The file is documentation-only; pass flags explicitly. The limitation is recorded in the changelog.
Doctor reports a missing optional extra
Optional extras are listed but do not fail the check. Only a missing core dependency, Python below 3.10, or invalid TOML returns exit 1.
I need help or found a bug
Open an issue on GitHub. Include the LeakLens version, Python version, the command, and the finding output with any sensitive data removed.