feat: empirical JPEG tag-matrix (ExifTool vs oxidex) (branch audit) - #72
feat: empirical JPEG tag-matrix (ExifTool vs oxidex) (branch audit)#72swackhamer wants to merge 10 commits into
Conversation
Adds an empirical, per-tag exiftool<->oxidex JPEG support matrix: - scripts/generate_exiftool_manifest.py: dumps ExifTool's tag DB (-f -listx) for JPEG-relevant groups and synthesizes a type-appropriate sample value per writable tag (4,812 tags, 74 XMP namespaces) - scripts/jpeg_tag_matrix.py: for every tag, exiftool-writes a sample into a clean JPEG and verifies oxidex reads it; then oxidex-writes it and verifies both oxidex and exiftool read it back (with per-tag isolation retests so one poison tag can't contaminate a group batch) - scripts/jpeg_tag_report.py: renders docs/reference/jpeg-tag-support.md (readable-tag mapping with working write keys + example values) and docs/reference/jpeg-tag-matrix.md (full 4,812-tag classification and the confirmed W1-W11/R1-R8 bug inventory with root causes), plus a ratcheting baseline gate (docs/reference/jpeg-tag-baseline.json) - tests/fixtures/jpeg/tag_matrix_base.jpg: minimal 8x8 base image - .github/workflows/jpeg-tag-matrix.yml: rebuilds oxidex, runs the matrix against pinned ExifTool 13.55 on main pushes / weekly / on demand, auto-commits refreshed docs, and fails on support regressions - VitePress sidebar entries so both reports publish to oxidex.net Current baseline: 1,621/4,812 tags readable, 122 CLI-writable (56 clean), 48 read-broken, 240 read-OK-write-broken. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fit the empirical JPEG reports into the right places on oxidex.net: - Move the two pages into the Compatibility sidebar section (next to the generated ExifTool comparison reports) instead of the top-level Reference list - Add an ExifTool Compatibility entry to the Reference overview page - Style both generated pages like the site's other auto-generated docs: VitePress info callout with last-updated date and pinned ExifTool version, outline depth for the large matrix page, and cross-links between JPEG Tag Support <-> JPEG Tag Matrix <-> ExifTool Coverage <-> Compatibility overview - Cross-link from the auto-generated tag-coverage-analysis page (via its generator, so the link survives regeneration) and from the deploy-time comparison overview (src/bin/tag-comparison markdown generator) Verified: vitepress build succeeds; tag-comparison binary compiles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves all 23 new issues flagged by Codacy on PR #19: - Pin the two third-party GitHub Actions in the new workflow (dtolnay/rust-toolchain, Swatinem/rust-cache, astral-sh/setup-uv) to full commit SHAs; dtolnay/rust-toolchain keeps its "stable" channel behavior via an explicit `toolchain: stable` input (2 High) - Require defusedxml unconditionally instead of falling back to stdlib xml.etree.ElementTree, closing the XXE gap for real rather than just preferring the hardened parser when available (4 Error/Warning) - Replace the hardcoded "/tmp/oxidex-tagmap" default work directory with tempfile.gettempdir()-based construction (Bandit B108 x3); CI now pins TAGMATRIX_WORK to ${runner.temp}/oxidex-tagmap explicitly - Add narrowly-scoped, justified nosec/nosemgrep suppressions on the subprocess.run() call sites: all use list-argv with no shell=True, and every argument is either a local tool path from an env var or a value this same tooling synthesized moments earlier from exiftool's own local -listx XML dump -- never externally-controlled input Also converts the three scripts to PEP 723 inline-script format (matching the existing scripts/generate_tag_coverage.py convention) and switches the CI workflow from pip+setup-python to uv, declaring defusedxml as a proper inline dependency instead of a manual CI install step. Verified: uv run on all three scripts reproduces identical manifest/matrix output; YAML validated; git status confirms no incidental changes to the generated docs pages (smoke-testing against partial datasets was reverted before commit). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…v-args The previous commit's suppression comments for python.lang.security.audit.dangerous-subprocess-use-tainted-env-args were placed on the `subprocess.run(` line, but Semgrep anchors that rule on the line containing the actual argument list -- a different physical line in these multi-line calls (confirmed by re-querying Codacy's PR analysis, which still reported 4 new issues after the first fix commit). Centralizes generate_exiftool_manifest.py's three subprocess call sites into a single _run_exiftool() helper (also fixing a DRY violation) and collapses jpeg_tag_matrix.py's existing run() wrapper call to one physical line, so both the audit and tainted-env-args rule anchors are covered by one trailing suppression comment per call site. Verified: uv run on both scripts reproduces identical manifest/matrix output as before; docs pages confirmed untouched by smoke testing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves 5 unresolved review threads from automated code review:
- Two empty "except: pass" blocks (generate_exiftool_manifest.py's
non-numeric tag `count` handling, jpeg_tag_report.py's malformed-
manifest fallback in autogen_callout()) now carry a one-line comment
explaining why the exception is intentionally swallowed, preserving
identical runtime behavior
- Three paragraphs in jpeg_tag_report.py's generated Markdown relied on
Python's implicit adjacent-string-literal concatenation inside a list
literal -- a pattern that silently merges list items if a comma is
ever dropped. Made each paragraph an explicit `+`-joined expression so
a missing separator would be a visible syntax/runtime error instead of
a silent merge.
Verified byte-for-byte: reran scripts/jpeg_tag_report.py against the
original full 4,812-tag results.json and diffed the regenerated
docs/reference/jpeg-tag-{support,matrix}.md against the committed
versions -- zero differences, confirming the refactor is a pure
readability/maintainability change with no output impact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…x-tag-mapping-d14171
…line Merges origin/main (adds src/writers/exif_inplace.rs and rewrites date_shift.rs for ExifTool-compatible JPEG date shifting, PR #18). Verified: full workspace test suite passes (598 tests), release binary rebuilds cleanly. Also closes a real gap found while re-verifying the JPEG tag matrix against the merged binary: the read-bug pattern classification, write-bug-cluster annotation, and non-standard-encoding validation that back the published docs were only ever run via throwaway /tmp scripts, never committed. The CI workflow as originally written would have regenerated a materially cruder report on its first run (e.g. every raw read mismatch showing as "read broken" instead of the refined 48-bug / formatting-only split). Ports all of it into scripts/jpeg_tag_matrix.py: - classify_read_mismatch() + apply_bug_classification(): root-causes read=MISMATCH into a tagged bug or MISMATCH_FORMAT (value-equivalent, formatting differs), and labels write=INTEROP_BROKEN with its root-caused bug cluster (I1-I5) -- ported as empirically-verified data, not guesses - Generic no-op detection: every write result is compared against the tag's pristine pre-write value in the base fixture, so a write that silently changed nothing is classified NOT_WRITTEN regardless of whether the stale value happens to coincidentally match or mismatch the sample (replaces a hardcoded false-positive tag list) - Generic same-group key-fallback scan: catches registry asymmetries (value written/read correctly but under an unexpected raw/hex key) without hardcoding tag names -- applied to both read and write paths, and found one previously-undetected case (TargetPrinter) was incorrectly counted as unsupported on the read side too - Tightened write-verification lookups to require an exact group match, fixing two false READBACK_BROKEN results caused by matching an unrelated pre-existing tag of the same bare name in a different group - Integrated exiftool -validate diffing directly into the write-test phase (was a separate, uncommitted script) - Fixed a real field-naming collision: read and write phases both wrote "ox_val"/"et_val" into the same per-tag result dict, so a successful write silently clobbered the read phase's observed value for that tag -- corrupting the bug classifier's input on any tag where read failed but an independent write-then-readback happened to succeed. Write's fields are now namespaced (write_ox_val/write_et_val/write_ox_key) - Fixed the sample generator picking a literal "None"/"Unknown" enum label as a tag's test value when a more distinctive option existed, which made a genuine write indistinguishable from a no-op for any tag whose pristine default happens to equal that label (found via JFIF:ResolutionUnit) Re-ran the full 4,812-tag matrix against the merged binary end-to-end through the now-fully-committed pipeline. Verified zero write regressions: the exact same 122 tags remain CLI-writable, no losses. Readable-tag detection improved 1621 -> 2028 (registry-asymmetry and field-collision fixes surfacing previously-miscounted tags); the "write broken" bucket's rise (240 -> 252) is entirely tags whose already- broken write status was simply invisible before because their read was wrongly counted as unsupported (verified precisely: it only fires where read crosses from MISSING into a readable status while write is unchanged). Baseline updated to reflect the corrected, more accurate counts. Also updates the W2 known-bug description: the merged date-shift rewrite fixed updating an existing DateTimeOriginal-style tag in place (verified manually); only fresh-creation on a tag with no prior value still fails, which is what the harness's dateless base fixture measures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The `runner` context is only available inside individual step contexts (steps[*].run/with/if/env), never in a workflow- or job-level `env:` block -- confirmed by actionlint and by a real CI run on this branch (29682285560), which silently scheduled zero jobs with the generic "This run likely failed because of a workflow file issue" error. This went undetected since d14d772 because no prior push touched a path matching the workflow's trigger filter until the previous commit did. Route TAGMATRIX_WORK through $GITHUB_ENV from a plain shell step instead (uses $RUNNER_TEMP, not the `${{ runner.temp }}` expression), which is the standard idiom for computing a workflow-wide env var from step-only context. `actionlint` now reports zero issues for this file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…x-tag-mapping-d14171 # Conflicts: # .github/workflows/jpeg-tag-matrix.yml # docs/reference/jpeg-tag-baseline.json # docs/reference/jpeg-tag-matrix.md # docs/reference/jpeg-tag-support.md # scripts/generate_exiftool_manifest.py # scripts/jpeg_tag_matrix.py # scripts/jpeg_tag_report.py
PR #19 merged mid-session (by swackhamer) before the classification- pipeline refinements and the CI runner.temp fix landed; those commits were stranded on this branch behind the now-closed PR. #21 (pin remaining workflow actions) and #22 (wire dead JPEG segment parsers -- COM, SPIFF, DQT quality, multi-chunk ICC, APP6/GoPro -- into the read path) merged to main after #19. Merged origin/main (resolved 7 add/add conflicts: this branch's fixed versions of the JPEG tag matrix pipeline/docs/workflow superseded PR #19's pre-refinement originals; #21/#22's files merged cleanly with no overlap). Verified: full workspace test suite passes (3,615 tests, 0 failures, 37 binaries), release binary rebuilds cleanly. Re-ran the full 4,812-tag matrix against the fully current binary. Result: only one tag changed status -- File:Comment flipped from unsupported to readable, confirming #22's COM-comment read-path fix. The rest of #22's fixes (SPIFF, DQT-derived quality, multi-chunk ICC, APP6/GoPro) aren't independently visible to this harness: they're either not ExifTool-writable tags at all, or fall outside the EXIF/XMP/IPTC/JFIF/Photoshop/ICC_Profile groups this matrix synthesizes samples for -- noted explicitly in the updated R8 entry rather than left to look unconfirmed. Zero write regressions verified: the exact same 122 tags remain CLI-writable in every re-run this session. Baseline ratcheted 2028 -> 2029 readable to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 51 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
Closing as superseded. This PR's Python-based tag-matrix harness ( |
Opened from a repo-wide audit of orphaned/unmerged branches for triage.
Contents: 9 commits building an empirical JPEG tag-matrix (ExifTool vs oxidex comparison) integrated into docs/CI, with security/code-quality fixups (originally PR #19). Looks polished and reviewed.
Audit rating: Value 7/10, merge complexity 6/10 — solid tooling for tag parity tracking, but touches a CI/docs pipeline that may have moved on main.
Not necessarily merge-ready — opened for visibility/triage.