Releases: PyModel/watermark-remover
Release list
v0.2.0 — architecture consolidation and hardening
Architecture consolidation release. Every entry point now routes through a small set of owned seams instead of re-implementing routing, subprocess execution, PNG traversal, and HTTP transport in each module. The externally visible payoff is in the hardening below; the layer capabilities are unchanged from v0.1.0.
Security
- Argument injection in external command templates is fixed. v0.1.0 built commands as
shlex.split(template.format(**values))— substituting first, then splitting — so a path containing spaces or quotes could inject additional argv elements into--inpaint-commandand--detect-command. Templates are now tokenized first and values substituted per-token, so a value can never become more than one argument. - External processes are bounded and cannot outlive their deadline. One end-to-end timeout, bounded stdout/stderr tails with explicit truncation reporting, and POSIX process-group termination so a descendant cannot survive a timeout or hold the output pipes open after the leader exits.
- Layer B HTTP follows same-origin redirects only, so credentials are never forwarded to another host. Endpoints carrying inline credentials, a query, a fragment, or control characters are rejected before any connection; responses are size-bounded with
Content-LengthandContent-Typevalidation; transport errors never embed the endpoint, headers, or response body.
Fixed
- ZIP-based containers (DOCX, ODT) are detected when the sniff prefix truncates the archive's central directory — detection falls back to a metadata-only read of the on-disk archive instead of reporting
unknown. - PNG structure is validated strictly on inspection: per-chunk CRC, IHDR first and single and 13 bytes, IDAT present and consecutive, and an empty terminal IEND.
Changed
These alter existing command-line behavior:
- Symlinked input sources are rejected with an error instead of being silently skipped.
--visible-backend simple|externalor--inpaint-commandwithout a localization source now errors instead of silently doing nothing.- Explicit file arguments are no longer filtered out by the output root. Passing a file that lives under
-o <dir>now exits 2 with an aliasing error rather than skipping that file. Directory sources are unaffected. - Invalid-source diagnostics are combined into a single line, and
no matching input filesis now prefixedinvalid input selection:. Exit codes are unchanged at 2.
Internal
Five new shared modules — asset_kind, clean_asset, external_command, layer_b_http, png_chunks — with clean_file, inspect_file, and demo.py routing through them. Rewrite configuration moved to immutable RewritePlan objects. Test suite grew from 14 to 21 files; 326 tests pass on Python 3.10, 3.12, and 3.14.
Install
Python 3.10+, standard library only. Networks, models, GPUs, and system binaries (exiftool, c2patool, pypdf) sit behind opt-in adapters.
git clone https://github.com/PyModel/watermark-remover.git
cd watermark-remover
python skills/remove-ai-marks/scripts/clean_text.py --helpHonesty contract: deterministic cleaners report exactly what they removed. Rewrite, inpainting, and detector-evasion methods are labeled best-effort. Nothing here certifies that a vendor detector will fail.
Full changelog: v0.1.0...v0.2.0
v0.1.0 — first release
First public release. Tools for finding and removing AI provenance signals from files you own, across four channels.
Layers
- A — hidden Unicode (
clean_text): context-aware deterministic scrub of zero-width characters, bidi controls, Unicode tags, and exotic spaces. Verifiable: reports exactly what was removed. - B — token-distribution text watermarks (
rewrite): paraphrase, back-translation, structural rewrite, and TSAPA-style evolutionary search over a local Ollama or OpenAI-compatible endpoint. Best-effort, labeled as such. - V — visible image marks (
remove_visible): MorphoMod masking, hole fill, dilation, texture-patch synthesis, and optional external inpainting (LaMa, MI-GAN, diffusion). Mask removal verifiable; fidelity best-effort. - M — metadata (
clean_image,clean_file): format-aware C2PA, EXIF, and XMP rewrite for JPEG, PNG, HEIF/AVIF, PDF, and OOXML/ODF containers. - Soft binding (
inspect_soft_binding): detects embedded and remote C2PA manifests. Detection and warning only. - SynthID: optional external adapter, scoring only.
Also in this release
- Batch processing with glob validation and safe input-to-output mapping
- Atomic writes, guarded backups, and bounded reads throughout; in-place edits validate the full transform before overwriting
- Symlink rejection on input roots and outputs
- Gradio demo (
demo.py) wrapping the cleaning pipeline --disable-thinking/--allow-thinkingfor reasoning models, withWATERMARKS_REWRITE_DISABLE_THINKINGenv fallback
Install
Python 3.10+, standard library only. Networks, models, GPUs, and system binaries (exiftool, c2patool, pypdf) sit behind opt-in adapters.
git clone https://github.com/Pythoughts-labs/watermark-remover.git
cd watermark-remover
python skills/remove-ai-marks/scripts/clean_text.py --helpHonesty contract: deterministic cleaners report exactly what they removed. Rewrite, inpainting, and detector-evasion methods are labeled best-effort. Nothing here certifies that a vendor detector will fail.