xlsx-provenance
Fingerprint .xlsx files for authorship and authenticity. Tells you whether a workbook was actually authored in Excel, or generated by a library (openpyxl, xlsxwriter, Aspose, ClosedXML, EPPlus, SheetJS, ...) or another office suite (LibreOffice, OnlyOffice, WPS, ...).
Stdlib-only Python. No third-party dependencies.
Why
An .xlsx is a zip of XML. Every authoring tool leaves fingerprints: different Application strings, different fileVersion attributes, different presence/absence of calcChain.xml, theme1.xml, printerSettings*.bin, <HeadingPairs>, <TitlesOfParts>, cellXfs count="" attributes, and so on.
This tool reads those fingerprints and gives you a per-file verdict you can act on.
Install
git clone https://github.com/jtannahill/xlsx-provenance.git ~/xlsx-provenance
ln -s ~/xlsx-provenance/xlsx-provenance ~/bin/xlsx-provenance
# ensure ~/bin is on PATH
Or just run the script directly:
~/xlsx-provenance/xlsx-provenance some-file.xlsx
Usage
xlsx-provenance file1.xlsx [file2.xlsx ...]
xlsx-provenance *.xlsx
xlsx-provenance --json file.xlsx # machine-readable
xlsx-provenance -v file.xlsx # show full signal breakdown
xlsx-provenance -q *.xlsx # one line per file
xlsx-provenance --no-color file.xlsx # plain output
Exit code is 0 if every file is verdict EXCEL_* or a non-Excel office suite (LIBREOFFICE, ONLYOFFICE, etc.). It is 1 if any file came back from a programmatic library (OPENPYXL, XLSXWRITER, ...) or as SUSPECT / UNKNOWN / MISSING / INVALID. Useful in CI.
Verdicts
| Verdict | Meaning |
|---|---|
EXCEL_MAC |
Microsoft Macintosh Excel. Authentic Excel for Mac |
EXCEL_WIN |
Microsoft Excel. Authentic Excel for Windows or Online |
EXCEL_OTHER |
Some other Excel variant string, but Excel fileVersion confirmed |
EXCEL_LIKELY |
No explicit Application, but enough Excel-only artifacts (calcChain, fileVersion appName=xl, real theme, printerSettings, VBA, threaded comments, etc.) to be confident |
OPENPYXL, XLSXWRITER, ASPOSE, CLOSEDXML, EPPLUS, OPENXML_SDK, SHEETJS, SPREADJS, SYNCFUSION, GEMBOX, SPIRE, LUCKYSHEET, PYTHON_XLSX |
Application string explicitly declared a generation library |
LIBREOFFICE, ONLYOFFICE, OPENOFFICE, GNUMERIC, CALLIGRA, WPS_OFFICE, APPLE_NUMBERS, GOOGLE_SHEETS |
Authentic but non-Excel office suite |
SUSPECT |
No Application and no fileVersion. Looks tampered or hand-built |
UNKNOWN |
Couldn't classify |
MISSING |
File doesn't exist |
INVALID |
Not a valid zip / corrupted |
Signals examined
docProps/app.xml:Application,AppVersion,Company,Manager,DocSecurity, presence ofHeadingPairs+TitlesOfParts(Excel-only, openpyxl skips)docProps/core.xml:creator,lastModifiedBy,created/modifiedtimestamps (and their delta; < 1s smells automated),lastPrintedxl/workbook.xml:<fileVersion appName="xl" rupBuild="...">(Excel-only),workbookPr/@codeName, defined names, sheet count- Zip artifacts:
calcChain.xml(Excel writes, libraries usually skip),theme/theme1.xmlsize (Excel: ~6796–8390 B; openpyxl: < 4 KB),printerSettings*.bin,vbaProject.bin,pivotTables/,pivotCache/,connections.xml,externalLinks/,charts/,drawings/,comments*.xml,threadedComments(Excel 365),tables/,queryTables/ xl/styles.xml: Excel often omitscount="N"on<cellXfs>; openpyxl always includes it. Excel writes<tableStyles>,<indexedColors>.xl/sharedStrings.xml:uniqueCountattribute presence[Content_Types].xml: number of overrides, presence of theme override
The verdict combines a hard match on the Application string with a soft score (0–14) over the structural signals. High score with no library declaration → EXCEL_LIKELY.
Examples
Pretty (default):
=== model.xlsx ===
[EXCEL_MAC] Excel signal score 11/14 (confidence: high)
application Microsoft Macintosh Excel / AppVersion 16.0300
identity creator='' lastModifiedBy='james tannahill'
fileVersion appName='xl' lastEdited='7' lowestEdited='7' rupBuild='10503'
workbook sheets=11 definedNames=0 codeName=None
artifacts calcChain, theme1(6798B), printerSettings, drawings
styles.xml count attr omitted, tableStyles, indexedColors
content-types 18 overrides, theme=True
zip 22 entries
Library tell:
=== generated.xlsx ===
[OPENPYXL] Application explicitly declares openpyxl (confidence: high)
application Microsoft Excel Compatible / Openpyxl 3.1.5 / AppVersion 3.1
identity creator='' lastModifiedBy='someone'
fileVersion <missing>
...
JSON for piping into other tooling:
xlsx-provenance --json *.xlsx | jq '.[] | select(.verdict == "OPENPYXL") | .path'
How to "fix" an openpyxl-generated file
Open it in real Excel and File → Save As (overwrite or new name). Excel rewrites every metadata field (Application, fileVersion, calcChain, theme) to its native fingerprint. Editing docProps/app.xml by hand only fixes the visible Application string and leaves the deeper structural tells intact.
License
MIT
Metadata
Release files for xlsx-provenance 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xlsx_provenance-1.0.0.tar.gz | 9.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xlsx_provenance-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.2 kB
Release files / xlsx_provenance-1.0.0.tar.gz
| Download URL | xlsx_provenance-1.0.0.tar.gz |
|---|---|
| Size | 9.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cd39405c3098f5a581427c125e53f7a23b463de0667e8790ec66d981eda39362
|
|
BLAKE2b-256 checksum How to use checksums |
8613d62818ac6f0af88343d8957584b7f91221395cfc7ebaf29cd8527c45f10c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / xlsx_provenance-1.0.0-py3-none-any.whl
| Download URL | xlsx_provenance-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7d223d6c2abb2f5e89bbc779cbacede54e4b4ad5843f306195c98487276e9985
|
|
BLAKE2b-256 checksum How to use checksums |
e190c1bbff1bb74bf7e53a49b9aaf5c0b515b6e34119c426acabdf1bd05d3667
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|