WP Manifestindependent plugin directory
manifest / privacy / hm-media-pii-cleaner

HM Media PII Cleaner releases

Cleans up personally identifiable information from WordPress media file metadata

by Human Made Limited · github.com/humanmade/hm-media-pii-cleaner · website

0stars
4release downloads
0forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/humanmade/hm-media-pii-cleaner/releases/download/v0.2.0/hm-media-pii-cleaner-v0.2.0.zip

WordPress plugin that strips hidden metadata (EXIF/IPTC/XMP, PDF Info/XMP, video udta, OOXML docProps) from Media Library uploads. By default title, organisation, copyright, created date, version and language are kept (see Allowed fields); author, software, camera, GPS and similar fields are removed.

Files that can't be proven clean are flagged in the Media Library and moved out of the document root.

Covered: JPEG, PNG, WebP, GIF, SVG, PDF, MP4/QuickTime, DOCX/XLSX/PPTX. No shell access, exiftool, Imagick or GD required.

See LIMITATIONS.md for per-format behaviour, what isn't covered and the remediation workflow.

Try it in WordPress Playground (latest main, with sample media already imported).

Requirements

  • WordPress 6.2+
  • PHP 8.3+

Installation

composer install --no-dev

Or require humanmade/hm-media-pii-cleaner from a site's composer.json. If the site already autoloads setasign/fpdi, the plugin uses that instead of its own vendor/.

WP-CLI

wp hm-media-pii-cleaner sanitize-all [--include-videos] [--force] [--dry-run] [--report=<path>]
wp hm-media-pii-cleaner sanitize-pdfs|sanitize-gifs|sanitize-svgs|sanitize-videos|sanitize-ooxml|verify-images [<id>...] [--all] [--force] [--dry-run] [--report=<path>]
wp hm-media-pii-cleaner set-status <id> <status> [--detail=<text>]
wp hm-media-pii-cleaner status-report [--format=<all|pdf|image|video|ooxml>] [--output=<path>]
if (
    function_exists( '\HM\MediaPiiCleaner\RenderGuard\is_attachment_sanitized' )
    && ! \HM\MediaPiiCleaner\RenderGuard\is_attachment_sanitized( $attachment_id )
) {
    return null;
}

Allowed fields

The fields that survive sanitization are defined in inc/fields.php and can be changed with the hm_media_pii_cleaner_allowed_fields filter. Each field maps onto the metadata formats it applies to:

Key Value Used for
xmp [ namespace, local name, prefix ] list XMP in JPEG, WebP, PNG, SVG and PDF. The first entry also receives converted EXIF/IPTC values.
exif EXIF tag names, in precedence order Converted to the field's XMP property before binary EXIF is removed
iptc IPTC dataset codes, joined in order Converted to the field's XMP property (JPEG)
png PNG text keywords tEXt/zTXt/iTXt chunks kept as-is
pdf_info Title, Author, Subject, Keywords, Creator, CreationDate PDF /Info dictionary
ooxml_core [ namespace, local name ] list (dc, dcterms or cp) docProps/core.xml
ooxml_app Element names docProps/app.xml
image_meta WordPress image_meta keys Stored attachment metadata and the REST API
date true Normalizes converted EXIF/IPTC values to ISO 8601

Remove a field:

add_filter( 'hm_media_pii_cleaner_allowed_fields', function ( array $fields ) : array {
    unset( $fields['organisation'] );
    return $fields;
} );

Add a field, or change an existing one:

add_filter( 'hm_media_pii_cleaner_allowed_fields', function ( array $fields ) : array {
    $fields['credit'] = [
        'xmp'  => [ [ 'http://ns.adobe.com/photoshop/1.0/', 'Credit', 'photoshop' ] ],
        'iptc' => [ '2#110' ],
    ];
    $fields['title']['png'][] = 'Description';
    return $fields;
} );

Notes:

  • Invalid mappings are dropped and reported through _doing_it_wrong(). A filter that doesn't return an array keeps nothing.
  • Reserved XMP namespaces (rdf, x, xml), PNG's raw XMP keyword and the PDF Producer key can't be allowed. orientation is always kept in image_meta, since the image editor needs it.
  • Anything you add is published with every matching file. Author, creator and software fields usually contain usernames and version numbers.
  • The PDF verifier follows the same rules, so an allowed field doesn't cause a PDF to be flagged.
  • Changes only apply to files sanitized afterwards. Run wp hm-media-pii-cleaner sanitize-all --force to re-process existing files. Fields that were already stripped can't be restored.

Configuration

Optional constants for wp-config.php:

  • HM_MEDIA_PII_CLEANER_QUARANTINE_DIR: private directory for flagged files.
  • HM_MEDIA_PII_CLEANER_MAX_{IMAGE,PDF,VIDEO,OOXML}_BYTES, HM_MEDIA_PII_CLEANER_MEMORY_RESERVE_BYTES: size and memory ceilings.

Development

composer install
composer test
composer lint

bin/build-zip.sh builds the installable zip into build/, with production vendor/ included.

Releasing

The plugin header keeps Version: __VERSION__ in the repo. To release:

  1. Go to Releases > Draft a new release and create a new tag such as v1.2.0 on main.
  2. Write the notes (or use "Generate release notes") and publish.

release.yml then writes 1.2.0 into the plugin header, commits it onto the tag, and attaches hm-media-pii-cleaner-v1.2.0.zip to the release. That zip is the one to install. GitHub's source archives don't include vendor/, and without it every PDF is flagged and quarantined instead of cleaned.

WordPress Playground

composer install
composer playground

This starts a disposable WordPress Playground site on port 9400, or the next free port (Node.js 20.18+ required), and mounts this directory as the plugin, including vendor/. The site boots from blueprint.json: PHP 8.3, latest WordPress, WP_DEBUG_LOG on, plugin active, logged in as admin.

It also imports a sample JPEG, PNG and PDF from tests/fixtures/playground/. Each one has fake personal metadata (author, software, camera) as well as a title and copyright, and opens in the Media Library list view, where the Metadata column shows the result. Pass extra CLI flags after --, e.g. composer playground -- --port=9500 --php=8.4. To rebuild the sample files, run php tests/fixtures/playground/generate.php.

Online previews use the same blueprint.json, with the plugin installed from a zip built by bin/build-playground.sh (production vendor/ plus the sample files) instead of mounted:

  • Pull requests: playground-preview-build.yml builds the zip from the PR with read-only permissions. playground-preview-publish.yml then uploads it to the ci-artifacts prerelease and adds an "Open in Playground" button to the PR description.
  • main: playground-main.yml uploads the zip to the playground-main prerelease on every push. The README link above installs from there.

The README link carries the blueprint inline, because playground.wordpress.net can't fetch a blueprint from a GitHub release (no CORS headers). After changing blueprint.json, regenerate it with the command below; composer test fails while the link is out of date.

php bin/playground-blueprint.php --link https://github.com/humanmade/hm-media-pii-cleaner/releases/download/playground-main/hm-media-pii-cleaner.zip

Releases

3 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v0.2.0 latest
Sep 11, 2026 2d ago
hm-media-pii-cleaner-v0.2.0.zip
1
Sep 11, 2026 2d ago
pr-1-8954710499dbde9bf097571c4633810d522266ac-hm-media-pii-cleaner.zip
2
Sep 11, 2026 2d ago
hm-media-pii-cleaner.zip
1