Move the copies of unpad, crc32, checksumPid and pidFromSerial that were
scattered across the plugin into utilities.py, and add a shared safe_join
that generalizes the Topaz extraction path-traversal fix.
- unpad: adobekey, ineptepub and ineptpdf import the shared helper. The
four bare-script key tools keep their local copies since they run
without a package context.
- checksumPid is type-preserving (bytes for kgenpids/kindlepid, str for
mobidedrm) so every call site keeps its exact behavior.
- kgenpids falls back to an absolute import because it is imported
top-level by the worker modules.
- topazextract.extractFiles uses safe_join; genbook is unchanged as it
only handles already-sanitized names.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The plugins target calibre 5/6 (Python 3) and the Poetry environment
pins Python >=3.8, so the Python 2 fallbacks are dead code. This removes
them throughout both plugins (behaviour on Python 3 is unchanged):
- Drop all `from __future__` imports.
- Collapse `if sys.version_info[0] == 2: ... else: ...` blocks to their
Python 3 branch (ineptpdf, mobidedrm, kindlekey, kgenpids, alfcrypto,
erdr2pml, ineptepub, obok, and the various unpad() helpers, etc.).
- Replace `_winreg` import fallbacks with plain `import winreg`, and
delete the py2-only adobekey_winreg_unicode module (now unreferenced).
- Drop py2 name shims: `unicode`/`unichr`, `.iteritems()`,
`from StringIO import StringIO`, `htmlentitydefs` fallback, and the
Windows CommandLineToArgvW dance in unicode_argv (py3 sys.argv is
already Unicode on every platform).
- Remove the "Calibre < 5" (py2) bugfix block from the compat header.
Verified: every .py file in both plugins still byte-compiles.
Scope: the maintained DeDRM_plugin and Obok_plugin only. The archival
standalone scripts under Other_Tools/ are left as historical snapshots.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Topaz header record "tag" is read verbatim from the untrusted book
file by bookReadString() and then used unsanitized to build the output
filename in extractFiles(). A crafted tag such as "../../foo" let a
malicious .azw/Topaz file write attacker-controlled bytes outside the
extraction directory. The payload content requires no book key, since an
unencrypted record with compressedLength == 0 is returned raw.
Strip the record name to its basename before joining it to destdir so
traversal sequences (../, /, \) can no longer escape, and add an
abspath-based containment check as defense in depth.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now the plugin ZIP file (DeDRM_plugin.zip) can be run with a normal
Python interpreter as if it were a Python file (try
`python3 DeDRM_plugin.zip --help`). This way I can begin building a
standalone version (that can run without Calibre) without having to
duplicate a ton of code.
THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility.
I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication.
Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0