Skip to content

Add C2PA Monitor experiment#459

Draft
lnispel wants to merge 5 commits intoWordPress:developfrom
OpenVerifiable:feature/c2pa-monitor
Draft

Add C2PA Monitor experiment#459
lnispel wants to merge 5 commits intoWordPress:developfrom
OpenVerifiable:feature/c2pa-monitor

Conversation

@lnispel
Copy link
Copy Markdown

@lnispel lnispel commented Apr 22, 2026

Read-only feature that detects C2PA Content Credentials in uploaded JPEG/PNG/WebP images at the add_attachment hook, captures the raw manifest store to a sidecar file under wp-content/uploads/ai-c2pa/, and persists a structured _wpai_monitor_record postmeta entry for downstream consumers.

Scope (PR 1):

  • Format detection via streaming magic-byte/segment walks (JPEG APP11/JUMBF, PNG caBX, WebP RIFF C2PA) with hard byte caps.
  • JPEG APP11 detection tracks Box Instance Numbers across segments so manifests fragmented across multiple APP11 markers (per ISO 19566-5) are reassembled correctly. Continuation segments do not need the c2pa/jumb token in their first 64 bytes.
  • Raw manifest capture: SHA-256 + length + sidecar path stored in postmeta; bytes streamed straight to disk under uploads/ai-c2pa/.
  • Sidecar dir is created on demand with .htaccess (Apache deny) and index.php hardening; nginx operators must add a deny rule manually (documented).
  • Fail-open boundary: every error is captured into the record and the upload itself is never blocked.
  • No external dependencies, no Composer additions, no outbound HTTP.

Test coverage:

  • Format_Detector: magic bytes, single-segment APP11, multi-segment reassembly, interleaved APP0/APP1/APP2 around C2PA, generic JUMBF (non-C2PA) ignored, truncated input, JPEG_MAX_SEGMENTS cap (positive and negative), PNG/caBX, WebP simple + extended (VP8X) + odd-length padding.
  • Manifest_Reader: byte-exact roundtrip for JPEG/PNG/WebP, multi-segment reassembly, deterministic sha256, MAX_MANIFEST_BYTES rejection, missing file, empty segments, bad offsets.
  • Sidecar_Writer: write + roundtrip, hardening files, format sanitization, overwrite, multi-attachment coexistence, custom .htaccess preserved across ensure_dir().
  • Record: roundtrip, defaults on empty input, JSON-not-serialize storage format, null on corrupt JSON, null when absent.
  • C2pa_Monitor end-to-end: JPEG/PNG/WebP present, JPEG absent, unsupported MIME, fail-open on bogus ID, truncated JPEG, duration_ms recorded, add_attachment hook actually fires, file-deleted-on-disk produces errors[0].stage = 'resolve_path'.

Synthetic fixtures are generated at runtime so no binary blobs land in the repo and there is no third-party fixture licensing question.

Deferred (out of scope for PR 1):

  • JUMBF box reader and CBOR decoder; populating c2pa.decoded claim summary (claim generator, digital source type, action history).
  • Admin UI, media library badge, CR icon (gated on C2PA conformance).
  • Cryptographic verification.
  • Preserving manifests through WordPress's image processing pipeline.

Made-with: Cursor

What?

Closes

Why?

How?

Use of AI Tools

Testing Instructions

Screenshots or screencast

Before After

Changelog Entry

Added - New feature.
Changed - Existing functionality.
Deprecated - Soon-to-be removed feature.
Removed - Feature.
Fixed - Bug fix.
Security - Vulnerability.
Development Update - Development related updates.

Open WordPress Playground Preview

lnispel added 2 commits April 22, 2026 09:19
Read-only feature that detects C2PA Content Credentials in uploaded
JPEG/PNG/WebP images at the `add_attachment` hook, captures the raw
manifest store to a sidecar file under `wp-content/uploads/ai-c2pa/`,
and persists a structured `_wpai_monitor_record` postmeta entry for
downstream consumers.

Scope (PR 1):
- Format detection via streaming magic-byte/segment walks (JPEG APP11/JUMBF,
  PNG caBX, WebP RIFF C2PA) with hard byte caps.
- JPEG APP11 detection tracks Box Instance Numbers across segments so
  manifests fragmented across multiple APP11 markers (per ISO 19566-5)
  are reassembled correctly. Continuation segments do not need the
  c2pa/jumb token in their first 64 bytes.
- Raw manifest capture: SHA-256 + length + sidecar path stored in postmeta;
  bytes streamed straight to disk under uploads/ai-c2pa/.
- Sidecar dir is created on demand with .htaccess (Apache deny) and
  index.php hardening; nginx operators must add a deny rule manually
  (documented).
- Fail-open boundary: every error is captured into the record and the
  upload itself is never blocked.
- No external dependencies, no Composer additions, no outbound HTTP.

Test coverage:
- Format_Detector: magic bytes, single-segment APP11, multi-segment
  reassembly, interleaved APP0/APP1/APP2 around C2PA, generic JUMBF
  (non-C2PA) ignored, truncated input, JPEG_MAX_SEGMENTS cap (positive
  and negative), PNG/caBX, WebP simple + extended (VP8X) + odd-length
  padding.
- Manifest_Reader: byte-exact roundtrip for JPEG/PNG/WebP, multi-segment
  reassembly, deterministic sha256, MAX_MANIFEST_BYTES rejection,
  missing file, empty segments, bad offsets.
- Sidecar_Writer: write + roundtrip, hardening files, format
  sanitization, overwrite, multi-attachment coexistence, custom
  .htaccess preserved across ensure_dir().
- Record: roundtrip, defaults on empty input, JSON-not-serialize storage
  format, null on corrupt JSON, null when absent.
- C2pa_Monitor end-to-end: JPEG/PNG/WebP present, JPEG absent,
  unsupported MIME, fail-open on bogus ID, truncated JPEG, duration_ms
  recorded, add_attachment hook actually fires, file-deleted-on-disk
  produces errors[0].stage = 'resolve_path'.

Synthetic fixtures are generated at runtime so no binary blobs land in
the repo and there is no third-party fixture licensing question.

Deferred (out of scope for PR 1):
- JUMBF box reader and CBOR decoder; populating `c2pa.decoded` claim
  summary (claim generator, digital source type, action history).
- Admin UI, media library badge, CR icon (gated on C2PA conformance).
- Cryptographic verification.
- Preserving manifests through WordPress's image processing pipeline.

Made-with: Cursor
- Skip postmeta when MIME is unsupported (should_persist guard in finally).
- Document that errors are stored in postmeta, not sent to a log.
- Classify Sidecar_Writer RuntimeException as errors[].stage sidecar_write.
- Manifest_Reader: reject locations where segment lengths do not sum to
  total_length; add regression test.

Made-with: Cursor
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 81.32780% with 90 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.95%. Comparing base (43d3bde) to head (bbb9026).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
...ludes/Experiments/C2pa_Monitor/Format_Detector.php 80.00% 38 Missing ⚠️
includes/Experiments/C2pa_Monitor/C2pa_Monitor.php 76.56% 30 Missing ⚠️
...ludes/Experiments/C2pa_Monitor/Manifest_Reader.php 80.35% 11 Missing ⚠️
...cludes/Experiments/C2pa_Monitor/Sidecar_Writer.php 86.95% 6 Missing ⚠️
includes/Experiments/C2pa_Monitor/Record.php 92.59% 4 Missing ⚠️
includes/Experiments/C2pa_Monitor/Raw_Manifest.php 87.50% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #459      +/-   ##
=============================================
+ Coverage      66.90%   68.95%   +2.04%     
- Complexity       907     1131     +224     
=============================================
  Files             59       66       +7     
  Lines           4699     5318     +619     
=============================================
+ Hits            3144     3667     +523     
- Misses          1555     1651      +96     
Flag Coverage Δ
unit 68.95% <81.32%> (+2.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

lnispel added 3 commits April 22, 2026 11:29
Align assignment operators in C2pa_Monitor. Satisfy Slevomat
(multi-constant, early exit) in Record. Use elseif for JPEG APP11
branch in Format_Detector. Targeted phpcs disables for WPCS
AlternativeFunctions and VIP RestrictedFunctions; remove error
suppression on fopen where checked. FQCN docblocks in Manifest_Reader
and Sidecar_Writer; early return in hardening. readme.txt: set
Contributors to openverifiable to avoid reserved plugin-check warning.

Made-with: Cursor
…aths

Prevents PHP warnings on fopen() for non-existent or unreadable files; PHPUnit
treats those as test errors. Behavior unchanged: return null before opening.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants