Skip to main content

doormat

Find the passwords people leave next to the label that says "Password".

CI Security Docs PyPI License: MIT OR Apache-2.0 Python 3.10+ Rust core Status: alpha


People hide the spare key under the doormat. They hide passwords the same way: in the spreadsheet cell right next to Password:, in a cell that reads pwd = hunter2, or inside a database connection string. doormat reads .xlsx, .xls, .ods, .csv and .tsv files, models each sheet as a 2D grid, and pairs credential labels with the values around them.

  • Spatial, not just regex. Scores every candidate value by its distance and direction from a credential label, its character mix and entropy, and nearby username or URL labels.
  • Finds what grep misses. Credentials split across cells, built with CONCAT in formulas, or tucked into cell comments.
  • Fast. Rust core with sheet-level parallelism: under 1 ms for a 5,000-cell sheet. Python bindings via PyO3, one FFI call per workbook.
  • Measured on real data. Precision and recall are reported on the public Enron spreadsheet corpus, not just synthetic fixtures. See Accuracy.
  • Offline and dependency-light. Two small runtime dependencies (openpyxl, and defusedxml to refuse malicious XML inside spreadsheet files). No network calls, no model downloads.

Documentation: https://dipan010.github.io/doormat/ (API reference, how detection works, threat model, evaluation).

Installation

pip install doormat

Optional readers for legacy formats:

pip install "doormat[xls]"   # Excel 97-2003 via xlrd
pip install "doormat[ods]"   # OpenDocument via odfpy
pip install "doormat[all]"   # both

Prebuilt wheels cover CPython 3.10 and newer on Linux (x86_64, aarch64), macOS (Intel, Apple Silicon) and Windows (x64). On other platforms pip builds from the source distribution, which needs a Rust toolchain. To work on doormat itself, see CONTRIBUTING.md.

Quickstart

import doormat

doc = doormat.load("it_handover.xlsx")
print(f"Scanned {doc.sheet_count} sheets, {doc.cell_count} cells")

for cred in doc.credentials(min_confidence=120):
    print(f"{cred.sheet}!{cred.coordinate}  {cred.key!r} -> {cred.value!r}  score={cred.confidence:.0f}")
Scanned 2 sheets, 7 cells
Servers!B3  'Password' -> 's3cRet!99'  score=210
Notes!A1  'VPN password' -> 'hunter2!'  score=250

Findings come back in reading order: sheet by sheet, then row and column. A finding's repr never includes the secret, so logging one is safe:

>>> doc.credentials(120)[0]
Relationship(key='Password', sheet='Servers', cell='B3', confidence=210)

API

Name Description
doormat.load(path, *, max_uncompressed_bytes=512 MiB) Read a spreadsheet (str or pathlib.Path) and run detection. Returns a GridDoc. Raises FileNotFoundError, or ValueError for an unsupported extension, a file whose signature does not match it, or a zip-based file that looks like a zip bomb. max_uncompressed_bytes=None lifts the size limit.
GridDoc.credentials(min_confidence=0.0) Relationships at or above a confidence score. 120 is a sensible default; scores above 200 are high-confidence inline or formula detections.
GridDoc.relationships() Every inferred key/value relationship, regardless of score.
GridDoc.sheet_count, GridDoc.cell_count Size of the scanned workbook (sheets that were read).
GridDoc.errors, GridDoc.complete A sheet that cannot be parsed does not stop the scan: it is listed in errors as a SheetError(sheet, kind, message) and the other sheets are still scanned. complete is True when every sheet was read; check it when a scan must be exhaustive.
Relationship Frozen dataclass: key, value, confidence, reason (a ;-separated breakdown of the score), and where it was found: sheet, row, col (1-based), header_row, header_col and hidden (the sheet is hidden).
Relationship.coordinate, .header_coordinate Spreadsheet references of the value and label cells, e.g. "B3" and "A3".

What it detects

Pattern Example
Value beside a label Password: in A1, the value in B1 or A2 (within a 3-cell radius)
Inline key/value Password: s3cret!, PWD=s3cret!, Password - s3cret!
Split across cells A value spread over 2 to 5 cells under a header
Built in a formula =CONCAT("password", "S3cure#1"), or a password: ... string literal in a formula
Hidden in a comment A comment on a password header, or a comment containing password: ...
Multilingual labels Password keywords in 15+ languages, including German, Spanish, French, Portuguese, Russian, Japanese, Chinese and Korean

Supported formats

Format Extensions Formulas Comments Extra
Excel (OOXML) .xlsx, .xlsm yes yes none
Excel 97-2003 .xls no no doormat[xls]
OpenDocument .ods yes yes doormat[ods]
Delimited text .csv, .tsv n/a n/a none

The reader is chosen by file extension, and binary formats are checked against their magic bytes before parsing. CSV and TSV files are treated as one sheet named after the file.

Accuracy

On 14 synthetic fixtures, one per detection pathway, doormat scores 100% precision and recall. Real spreadsheets are harder. On the public Enron spreadsheet corpus (15,929 files):

Version Precision Recall
0.1.0 16.5% 31.2%
current main 59.3% 34.7%

Precision for main is measured on files that played no part in tuning; recall is measured on files that did, so treat it as optimistic. Inline credentials are found with few false positives. Values beside a label are usually found, but a large share of those findings are still wrong pairings (usually a nearby label when the password cell is empty). The method, labelling rubric and caveats are in bench/corpus/README.md.

Performance

Rust core only, Apple Silicon, criterion.rs on synthetic workbooks:

Workbook Time
1 sheet, 1,000 cells 138 µs
1 sheet, 5,000 cells 695 µs
10 sheets, 5,000 cells each 2.6 ms

On very large real workbooks, reading the file with openpyxl dominates: about 27 s to read a 170,000-cell workbook against 0.1 s of detection.

How it works

  1. Extract. Python reads every cell, formula and comment in a single pass and sends the workbook to Rust in one call.
  2. Featurize. Each cell is normalized once and tagged with bitmask flags (character classes, header keywords via Aho-Corasick) and Shannon entropy.
  3. Select candidates. Cells with header keywords, high entropy, formulas, comments or inline patterns are kept and classified as headers or values.
  4. Group. Adjacent candidates are clustered into regions with a breadth-first search over a 7x7 neighbourhood.
  5. Pair and score. Each credential label is paired with its best-scoring neighbour using distance, character mix, entropy, region membership and nearby username or URL labels. Labels, prose and formulas are penalized.

Sheets are processed in parallel with Rayon. Scoring is heuristic; there is no trained model.

Limitations

  • Hostile files are checked, not sandboxed. XML entity attacks are refused, and .xlsx/.xlsm/.ods files that would decompress beyond 512 MiB, or with a zip-bomb compression ratio, are rejected before parsing. Those checks trust the sizes declared inside the archive, which a determined attacker can forge; run doormat with a memory limit when scanning files from untrusted sources at scale.
  • Password tables are not detected. A Password column with one credential per row is the most common pattern doormat misses today.
  • Legacy .xls files expose no formulas or comments through xlrd, so those pathways only apply to .xlsx, .xlsm and .ods.
  • Python only. Bindings for other languages are planned, as is a command-line tool.

Roadmap

  • 0.2.0: first PyPI release
  • 0.3.0: password-table detection, fewer false positives on empty form fields, faster extraction of large workbooks

All planned work is tracked in milestones; changes are listed in CHANGELOG.md.

Contributing

Bug reports and pull requests are welcome. Please follow the Code of Conduct. See CONTRIBUTING.md for building from source, running the tests and the benchmark harness, and adding a detection pathway.

Security

Please report vulnerabilities privately, as described in SECURITY.md. Never attach spreadsheets containing real credentials to issues; rebuild the layout with made-up values.

Acknowledgements

Real-world evaluation uses the Enron spreadsheet corpus by Felienne Hermans and Emerson Murphy-Hill ("Enron's Spreadsheets and Related Emails: A Dataset and Analysis", ICSE 2015), licensed CC BY 4.0. The corpus is not redistributed in this repository.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in doormat by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Metadata

Release files for doormat 0.2.0

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

Source distribution (sdist)

Source distribution for doormat 0.2.0
File Size Uploaded
doormat-0.2.0.tar.gz 65.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for doormat 0.2.0
File
doormat-0.2.0-cp310-abi3-win_arm64.whl CPython 3.10 abi3 Windows ARM64 Details
doormat-0.2.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
doormat-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl CPython 3.10 abi3 Linux musl 1.2+ x86-64 Details
doormat-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl CPython 3.10 abi3 Linux musl 1.2+ ARM64 Details
doormat-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
doormat-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
doormat-0.2.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
doormat-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 8.5 MB

Release files / doormat-0.2.0.tar.gz

Download URL doormat-0.2.0.tar.gz
Size 65.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a5e0607b0d0706f3c1d121375268f285087c21d3d0c728af6f5961dc538cdf39
BLAKE2b-256 checksum
How to use checksums
3859be3b49972028504fa188060d402ac4716a220dd648680c11de50e8915de1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-win_arm64.whl

Download URL doormat-0.2.0-cp310-abi3-win_arm64.whl
Size 798.8 kB
Tags CPython 3.10 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
a1aad49f23faf2865faab67b8e026f670806f2ddbbbfc8993170713f6cde029c
BLAKE2b-256 checksum
How to use checksums
470ba1ff7ce238507eeb938d9ce3c6aee7aed42f0f0df27ef046cffea2bf1404
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-win_amd64.whl

Download URL doormat-0.2.0-cp310-abi3-win_amd64.whl
Size 851.1 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
9e1aa4f8b4fc3d9caa665a9ea842bfd4fd6d3ace648388aecc395efb90c765bd
BLAKE2b-256 checksum
How to use checksums
0051f2696d4cb485dbbc073c7d1695ce080fc814457a916512a7ae5daced8c62
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl

Download URL doormat-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl
Size 1.3 MB
Tags CPython 3.10 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
03a395ca578f5a4e94f8a0461208ab986ba366553fea3f8d5028ae95df823e90
BLAKE2b-256 checksum
How to use checksums
74145a7a4ec4d4d837cd3ac851c820341f8e34010bc9add183f7ff9641aa699c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl

Download URL doormat-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl
Size 1.3 MB
Tags CPython 3.10 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
cdaf8bd2f6321a689dfaba4a6c8038728b7f17ea1a86778d73f51cbdd42b8d09
BLAKE2b-256 checksum
How to use checksums
0afabf73554a8a025d1b71ee0a254319a2ec84ea5fcc58991c9132991544a268
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL doormat-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.1 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
3ff9e3ed65b366f878c2de4d4111769c70b5fb4c43b9a9146920dbca4927a3d5
BLAKE2b-256 checksum
How to use checksums
1080867ada4c3fca51c82fe29e725ab49c2fef9e9248296fbe7054634728fb5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL doormat-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.1 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
05e0769f4e7af01447f73b1b860a474bc03eca05b734d626a8ca299d056b2a77
BLAKE2b-256 checksum
How to use checksums
7e504d589a3388d13e6a02bb5a12b3e023273c8b07a14ef23b8a171cf1271c41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL doormat-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Size 949.9 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
897d70ffb5b5aab49f7dc84186e2357da4ae480040ebd949d927b2efbb71d8f0
BLAKE2b-256 checksum
How to use checksums
341b182792060faf0d26eab819158834ef68be321ae48e6b2e3354d8eac8736e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / doormat-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL doormat-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 987.5 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
84127f024a762874b78c539de17f7b9d4a6f2be6cddfa780e6f6b5ece3f27c76
BLAKE2b-256 checksum
How to use checksums
12bb17b99c018e6ec5e1ca16cd64c40e9fd1321686b5b083e0242a067e358834
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

9 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