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.

shell
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.

Windows: use 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

ExtraAddsInstall
parquetParquet input via pyarrowpip install "leaklens-ml[parquet]"
featherFeather input via pyarrowpip install "leaklens-ml[feather]"
excelExcel input via openpyxlpip install "leaklens-ml[excel]"
imagesNear-duplicate image check via Pillowpip install "leaklens-ml[images]"
arenaThree-model arena with MLflow trackingpip install "leaklens-ml[arena]"
appFastAPI service and Streamlit UIpip install "leaklens-ml[app]"
taintwatchOptional guard for --llmpip install "leaklens-ml[taintwatch]"
devpytest, ruff, bandit, pip-audit and release toolspip 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.

shell
# 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

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

KindWhat it observesSeverity
target_leakageOne feature alone predicts the target above the threshold (cross-validated ROC AUC, R² or balanced accuracy).high / medium
categorical_target_leakageA high-cardinality category is near-pure with respect to the label (conditional entropy).high / medium
id_likeAt least 98% unique values in a column; likely an identifier.medium
split_overlapExact row hashes occur in both train and test.high
feature_overlapIdentical feature vectors across the split; conflicting labels raise it to high.high / medium
near_duplicate_overlapRows match after case/whitespace normalisation and rounding to four decimals.medium
temporal_leakageTraining timestamps extend beyond the start of the test period.high
group_overlapThe same entity appears in both splits when a --group column is supplied.high / medium
group_leakage_riskGroup sizes are small or uneven enough to make entity leakage risky.medium

Code checks: pipeline order

KindWhat it observesSeverity
preprocess_before_splitA scaler, imputer or transformer is fitted before the split.high
transformer_before_splitAn unrecognised transformer is fitted before the split.medium
encoder_before_splitA benign encoder is fitted before the split.low
fillna_before_splitImputation statistics are learned from the full frame.medium
get_dummies_before_splitCategory levels are fixed from the full frame.low
manual_scale_before_splitManual mean/std or min/max scaling is computed before the split.high
resample_before_splitSMOTE or another sampler runs before train_test_split.high
target_encode_before_splitTarget means or aggregates become a feature before the split.high
feature_selection_before_splitSelectKBest, RFE, SelectFromModel or similar is fitted before the split.high
frequency_encode_before_splitvalue_counts is mapped into a column before the split; no label involved.low
future_target_feature_before_splitA future-window target statistic is used as a feature.high
group_stat_before_splitA per-entity statistic is computed across the full frame before the split.medium
preprocess_outside_cvPreprocessing happens before cross_val_score or GridSearchCV.high / low
resample_outside_cvResampling happens before cross-validation.high
fit_on_full_dataA transformer is fitted on the full frame after the split exists.medium
fit_on_eval_dataA transform is fitted on the test or validation partition.high / medium

Code checks: tuning, time and reproducibility

KindWhat it observesSeverity
tuning_on_test_dataA search or manual loop fits on the test partition.high
tuning_before_splitA search or CV runs on the full dataset before the split.medium
tuning_on_full_dataA search is fitted on the original frame after the split exists.medium
cv_on_test_dataCross-validation runs over the held-out partition.medium
test_set_reuseThe test partition is scored three or more times, i.e. used for selection.low
group_cv_missing_groupsA group splitter is used without groups=.medium
temporal_split_shuffleA time-sorted frame is shuffled by train_test_split.high / medium
no_random_stateA split or sampler omits random_state.low
notebook_out_of_order_executionNotebook execution counts are non-monotonic.low
parse_errorA 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

shell
leaklens train.csv --target label \
  --test test.csv --group patient_id --time date \
  --fail-under 80 --html report.html
FlagMeaning
trainPath to the training dataset (positional). CSV and JSON work in the base install.
--targetLabel column in the training data.
--testHeld-out split to compare against; enables the duplicate, temporal and group checks.
--timeTimestamp column for temporal leakage.
--groupEntity column (patient, customer, device) for group leakage.
--thresholdSingle-feature score that counts as leakage (default 0.95).
--fail-underExit 1 when the trust score is below N.
--jsonEmit the report as JSON.
--explainPrint the suggested fix under each finding.
--intelligenceDeterministic confidence, root causes and remediation ranking.
--htmlWrite a single-file HTML report.
--sarifWrite SARIF 2.1.0 for GitHub code scanning.
--badgeWrite a shields.io endpoint JSON with the score.
--arenaCompare three model families with MLflow tracking (needs arena).
--llmOptional LLM explanations; off by default, local Ollama by default.

Code scan

shell
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

CommandPurpose
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 TESTNear-duplicate images across split folders (needs images).
leaklens --versionPrint 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.toml
[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.

GitHub Actions
- 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
.pre-commit-config.yaml
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

OutputFlagNotes
TerminaldefaultTrust score, findings, counts; --explain adds a fix under each.
JSON--jsonStable shape: trust_score, findings, audit stats, optional intelligence.
HTML--html FILESingle self-contained file, no external assets. Example report.
SARIF--sarif FILESARIF 2.1.0 for GitHub code scanning.
Badge--badge FILEshields.io endpoint JSON with the trust score.
Intelligence--intelligenceDeterministic confidence, root causes, impact analysis and prioritised fixes.
LLM notes--llmOpt-in. Sends findings, never dataset rows or full source. Local Ollama by default.
AI hypotheses--aiOpt-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.

shell
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.

Deterministic findings remain authoritative. AI proposals are marked experimental, capped at medium severity, and never change a finding, its severity, the trust score or the exit code. Dataset analysis receives column metadata only, never rows or values. Instruction-like text in code or column names is reported as an 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.

shell
pip install "leaklens-ml[app]"
uvicorn leaklens.api:app --port 8000
streamlit run leaklens/ui.py
EndpointInputReturns
GET /health—{"status": "ok", "version": "..."}
POST /auditMultipart CSV/JSON file + target + optional thresholdTrust score, audit stats and findings.
POST /scanJSON {"source": "...", "name": "snippet.py"}Findings and the aggregate trust score from the static scanner.
POST /predictJSON {"rows": [...], "model_path": "models/best.joblib"}Predictions from a locally trained arena model.
Uploads are capped at 8 MiB and 50,000 rows, with archive decompression limits and ZIP path-traversal rejection. /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:

shell
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.

Deployment status: the GitHub Pages site is static and ships without a connected backend, so Live scan shows an explicit “not connected” state until you supply a URL. A free Render web service plus 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.

SuiteCasesResultCommand
Scanner (combined)10958 leaky / 51 clean · precision 1.00 · recall 1.00python -m leaklens.bench
Advanced tuning and encoding32All expected classificationspython -m leaklens.bench_advanced
Dataset detectors129 leaky / 3 clean · precision 1.00 · recall 1.00python -m leaklens.bench_data
Experimental AI layer72Grouped CV + external transfer; reported honestly, not an improvement claimpython -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.