Skip to main content

leakgate

A pre-publish gate for what generic secret scanners miss: Korean personal data, local usernames and internal hosts, and your organisation's unpublished terms — alongside ordinary API keys. Built for researchers who publish code, notebooks and AI-agent transcripts (Claude Code / Codex JSONL).

pip install leakgate                 # stdlib only, Python 3.11+
leakgate scan .                      # report (exit 1 if anything found)
leakgate redact logs/ --apply        # mask in place, JSONL-safe

Why another scanner

Excellent scanners already exist, and leakgate does not try to replace them: gitleaks for vendor secrets, maskingtape and ko-pii for Korean PII, neuralyzer for agent transcripts. Each covers one axis. A repository you are about to make public leaks on all of them at once — a traceback with C:\Users\<you>, a meeting note with a colleague's phone number, an unpublished compound name, an API key a helper script echoed into a log. leakgate checks all four axes in one pass and can call the specialised tools as extra engines.

Axis What it catches
secret ~30 vendor formats (incl. Docker Hub, Airtable; Kakao and 공공데이터포털 keys by context), KEY=/password:/비밀번호: assignments, URLs with passwords, JWTs, private-key blocks — with placeholder discrimination (<YOUR_KEY>, sk-xxxx, os.environ[...] pass)
pii 주민/외국인등록번호 (checksum or post-2020 form), mobile/landline, email, card (Luhn + issuer), 사업자번호 (checksum), bank account/passport/licence (with context), road-name and lot-number addresses, names with titles, labels and lists, birth dates, vehicle plates, student/employee IDs, and anyone on your names list
infra usernames in Windows / JSON-escaped / git-bash / POSIX / macOS home paths, OneDrive and SharePoint tenants, RFC 1918 and Tailscale/CGNAT addresses, internal hostnames
custom your own dictionary: codenames, private repo names, regexes, and metric words whose nearby number is sensitive (yield, titer, MPSP)
known values point it at your real secrets file (--known-secrets secrets.json / .env): every value is matched literally, so your own key is caught whatever its format. Values are never printed.

Benchmark

bench/run.py scores every tool line by line on four synthetic corpora (credentials generated at runtime, fictional people and organisations):

  • dev — the corpus the rules were written against (optimistic by construction),
  • heldout — a blind set written by an agent that never saw the rules; its misses were then used to fix general gaps, so it is no longer blind,
  • heldout2 — a second blind set, first run after the 0.1.0 rules were frozen; its known gaps drove the 0.2.0 rules, so it is no longer blind either,
  • heldout3 — a third blind set (270 lines), written for 0.2.0 by an agent that read none of the sources, tests, README or other sets, first run after the 0.2.0 rules were frozen. This is the honest number for 0.2.0.

heldout3 (60 secrets, 70 Korean PII, 30 infra, 110 hard negatives):

leakgate Secrets Korean PII Infra False positives
0.1.0 49 44 23 0
0.2.0, rules frozen (the honest number) 49 46 23 0
0.2.0 after fixing four general gaps it exposed 55 48 23 0

The four fixes (PyPI tokens, Discord webhooks, session cookies, hyphenated employee IDs) came from reading heldout3's misses, so the last row is not blind. What it still misses is mostly names of people nobody listed: 17 of the 22 remaining Korean-PII misses are bare names in messenger logs, author lines and romanized form — list the people you know in names (see Configuration). The 0.2.0 changes that matter most — reading inside documents, git history, the names list — are not line-level and do not show up in these tables at all.

heldout2 (51 secrets, 36 Korean PII, 22 infra, 75 hard negatives), 0.1.0:

Tool Secrets Korean PII Infra False positives
leakgate (rules frozen) 46 27 18 2
leakgate (current, after real-world FP hardening) 43 27 17 1
gitleaks 8.30.1 32 0 0 1
neuralyzer 35 0 0 5
detect-secrets 1.5.0 23 0 0 4
Presidio 2.2.364 (+KR recognizers) 28 30 18 35
ko-pii 1.16.0 10 32 8 13
maskingtape 0.3.0 (rules only) 8 27 0 3

Read it plainly: leakgate leads on secrets and infra with the fewest false positives, but ko-pii is better at Korean PII. Add it as an engine (--engine ko-pii) when PII recall matters more than noise. The FP hardening came from scanning 90 000 real site-packages files (floats read as card numbers, result_key: "Name" read as credentials) and cost three heldout2 detections — a trade we chose deliberately. (0.1.0's known gaps — plates, student/employee IDs, Kakao/공공데이터포털/Airtable/Docker Hub keys — are rules in 0.2.0.)

Windows note: detect-secrets reads files with the locale codec, so on a Korean Windows it silently reports nothing for any UTF-8 file containing Hangul. Run it with PYTHONUTF8=1.

Usage

leakgate scan PATH...                    # text report, exit 0 clean / 1 findings / 2 error
leakgate scan --history .                # every line the git history ever added
leakgate scan --history --rev origin/main..HEAD .   # only what you are about to push
leakgate hook install                    # pre-push hook: block a push that adds a leak
leakgate scan . --format sarif > r.sarif # for GitHub code scanning
leakgate scan - < transcript.jsonl       # stdin
leakgate scan . --known-secrets ~/.secrets/secrets.json
leakgate scan . --engine gitleaks --engine ko-pii   # fails closed if an engine is missing
leakgate redact ~/.claude/projects --apply --backup  # mask agent transcripts
leakgate init                            # write a commented .leakgate.toml

Documents: Word, Excel, PowerPoint, HWPX, OpenDocument, PDF, images

A manuscript leaks where nobody looks, so container files are opened, not skipped. From .docx/.xlsx/.pptx/.hwpx/.odt leakgate reads the body, tracked deletions (text you deleted is still in the file), comments, headers and footers, footnotes, speaker notes, chart data, embedded workbooks, and the author fields (document properties, tracked-change and comment authors) — every named author is reported as document-author. Findings say where: paper.docx [word/comments.xml]:1:9.

PDFs need pip install "leakgate[pdf]" (pages, annotations, metadata). Images are read only with --ocr (needs the tesseract binary, kor+eng).

A file that can hold text but cannot be read — legacy .doc/.hwp/.xls, an archive, a scanned PDF with no text layer, a PDF whose Korean encoding pypdf cannot decode, an Office lock file ~$… (it stores the editor's user name) — is listed as UNSCANNED and the run exits 2. Pass --allow-unscanned to accept that knowingly. Redaction never rewrites a container: fix it in the application that made it.

Redaction keeps files usable: C:\Users\<you>\proj becomes C:\Users\<USER>\proj, a key becomes [REDACTED:github-token]. It never opens binaries, writes atomically, refuses to write a JSON/JSONL file that would stop parsing, and backs up raw bytes (not decoded text) when asked. Suppress a reviewed line with leakgate:allow in a comment, or add regexes to allow.

pre-commit

- repo: https://github.com/jahyunlee00299/leakgate
  rev: main
  hooks:
    - id: leakgate

Configuration (.leakgate.toml)

categories = ["secret", "pii", "infra", "custom"]
engines = []                   # "gitleaks", "maskingtape", "ko-pii"
known_secrets = []             # files whose values are your real secrets
exclude = ["**/fixtures/**"]
allow = []                     # regexes; matching values are dropped

[custom]
terms = ["project-falcon"]     # codenames, private repo names
patterns = ['\bZq[A-Z]{2}DH\b']
metrics = ["yield", "titer"]   # flag a number within 40 chars of these
names = ["김민준", "Jane Roe"]  # people you know: matched anywhere, even in bare prose
names_files = ["people.txt"]   # one name per line, e.g. exported from a contact list

Rules need a cue to find a name (교수님, 참석자:); the people who actually leak from a lab's files are a known set, so names matches them anywhere: 김민준이 어제 보냈다, 김 민준, 담당은 김민준이다, and for Latin names the reorderings Kim Minjun, Kim, Minjun, M. Kim, Kim, M.. A Korean name followed by anything but a particle, the copula or a title is left alone, so 김민준호 (someone else) and 정민수 do not match 김민준 / 정민.

Scope and limits

A secret removed in a later commit is still in every clone. scan --history reads what each commit added (so a value is reported once, at the commit that introduced it, with its line number there), including documents committed and later deleted. hook install adds a pre-push hook that scans only the outgoing commits; pass hook options with --scan-args="--known-secrets ~/s.json", and --remote public to scan only pushes to a public mirror. Finding a leak in history means rewriting it (git-filter-repo) and revoking the credential — redaction is never a substitute for rotation. Name detection is rule-based (titles, labels, lists): a bare name in running prose is not caught unless it is in your names list.

한국어 요약

공개 전에 레포·노트북·AI 에이전트 로그에서 API 키, 한국 개인정보(주민번호·전화·계좌·주소·이름), 로컬 사용자명·내부 IP, 조직의 미공개 용어를 한 번에 찾고 가립니다. 외부 의존성 없이 동작하며, gitleaks·maskingtape·ko-pii를 추가 엔진으로 붙일 수 있습니다.

  • 문서 내부까지: docx·xlsx·pptx·hwpx·odt의 본문뿐 아니라 변경 내용 추적으로 지운 글자, 메모, 머리글·바닥글, 문서 속성의 작성자까지 봅니다. PDF는 leakgate[pdf], 이미지는 --ocr. 읽지 못한 파일(구형 .hwp/.doc, 텍스트 없는 스캔 PDF 등)은 조용히 넘기지 않고 exit 2로 알립니다.
  • 아는 사람 명단: names에 등록한 이름은 문장 어디에 있든 잡습니다(김민준이 보냈다).
  • git 히스토리: scan --history로 과거 커밋에서 지운 키까지 찾고, hook install로 push 전에 막습니다.
  • 정직한 수치: 규칙을 본 적 없는 세트(heldout3)에서 시크릿 49/60, 한국 PII 46/70, 인프라 23/30, 오탐 0/110. 남은 누락은 대부분 명단에 없는 사람 이름입니다.

유출된 키는 가리는 것만으로 안전해지지 않으니 반드시 폐기·재발급하세요.

License

MIT

Release files for leakgate 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for leakgate 0.2.1
File Size Uploaded
leakgate-0.2.1.tar.gz 54.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for leakgate 0.2.1
File Interpreter ABI Platform
leakgate-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 101.5 kB

Release files / leakgate-0.2.1.tar.gz

Download URL leakgate-0.2.1.tar.gz
Size 54.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1ce163bb7dc0340c07ac9c51c135e9b887b1b50620fad51ab17d55f559f2c821
BLAKE2b-256 checksum
How to use checksums
8cadb8326ad6c9f5bccc7214da2092587d869c79ac8c162976857489083b166f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / leakgate-0.2.1-py3-none-any.whl

Download URL leakgate-0.2.1-py3-none-any.whl
Size 46.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42446f0275fa3d1725e283ae0510431bb92055e04a8cb296d5f894cd073eab7b
BLAKE2b-256 checksum
How to use checksums
0a8170fd38f0239c14015d1223251e4a9c6ab11c26ae8c06ed76106306ddde22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page