Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ Image Metadata Inspector & Cleaner is available at `/tools/image/metadata/` on t

- Accepts exactly one signature-validated JPEG, PNG, or WebP file and enforces the application’s 50 MiB limit before full inspection.
- Uses the manually pinned, same-origin `secure-metadata v0.1.0` browser artifact. No npm package, CDN, runtime GitHub request, or automatic version check is used.
- Separates decoded values from opaque detected containers and presents `metadata-partial` as successful but non-exhaustive. “No supported metadata detected” is not a claim that the file contains no metadata.
- Shows the local source thumbnail, detected format, size, and a keyboard-accessible remove/reset path. Decoded values are primary; opaque containers, coverage, and diagnostics remain available in a native details disclosure.
- Presents `metadata-partial` as successful but non-exhaustive. “No supported metadata detected” is not a claim that the file contains no metadata.
- Privacy Clean calls the library’s authoritative default policy: supported EXIF, XMP, IPTC, comments, PNG text metadata, and timestamps are removed while ICC color profiles are preserved.
- Customize exposes only supported class-level removal controls for the detected format. Unselected supported classes and unknown structures are preserved; individual metadata-value editing is not offered.
- Keeps source bytes unchanged and never decodes pixels, creates Canvas, resizes, converts, changes quality, or re-encodes the image.
- Calls `verifyMetadata` on cleaned bytes and requires a valid result with every policy check passing before saving. Invalid, incomplete, truncated, or mismatched results fail closed with no output write.
- Derives MIME and the normalized `_clean` filename from the detected image format, not the supplied MIME type or extension.
Expand Down Expand Up @@ -281,9 +283,10 @@ PDF Metadata Inspector & Cleaner is available at `/tools/pdf/metadata/` for one

- The explicit metadata model inspects Title, Author, Subject, Keywords, Creator, Producer, Creation Date, and Modification Date from the standard PDF document-info dictionary.
- Values remain raw in application state while the UI formats dates with `Intl.DateTimeFormat`, safely replaces surfaced null characters for display, and limits individual rendered values to 2,000 characters. Cleaning still targets the complete underlying field.
- Users can remove one or more selected fields or choose the explicit “Remove all supported metadata” path. Missing fields are shown consistently and cannot be selected.
- Shows a compact source card and decoded values first, with all eight supported document-info fields available in a native details disclosure.
- Privacy Clean removes every present supported document-info field. Customize exposes only the same eight class-level field controls; missing fields remain disabled and individual value editing is not offered.
- Cleaning edits the loaded PDF with `pdf-lib` using `updateMetadata: false`; pages are not rasterized, copied from screenshots, or reconstructed.
- After serialization, the tool reloads the produced bytes, inspects all supported fields again, and reports cleared or retained selections from that serialized output. Page count, dimensions, and rotation must also match before the result is offered as successful.
- After serialization, the tool reloads the produced bytes and inspects all supported fields again. Every requested field must be absent, and page count, dimensions, and rotation must match before any output is written; a retained requested field fails closed.
- Repeated cleaning continues from the previously verified output bytes. Loading or clearing a source releases the prior model, comparison, and retained byte references.
- The original PDF is never modified. Saving uses the shared File System Access picker where available and the revoking Blob-download fallback elsewhere.

Expand Down
2 changes: 2 additions & 0 deletions docs/image-metadata-privacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Inspection reports only structures supported by `secure-metadata v0.1.0`. Decode

Privacy Clean uses the library’s exported `DEFAULT_CLEANING_POLICY` directly. It removes supported EXIF, XMP, IPTC, comments, ordinary PNG text metadata, and standalone timestamps while preserving ICC color profiles. Unknown structures are not guessed away. The original source bytes remain unchanged.

Customize exposes only the same supported metadata classes that apply to the detected JPEG, PNG, or WebP format. The user may choose which classes to remove and whether to preserve ICC, but cannot edit individual values or target unknown structures. Verification expectations are derived from that explicit policy, so only requested removals are required to be absent and intentionally preserved supported classes may remain.

The produced bytes are passed to `verifyMetadata` before any write or download. Every returned policy check must pass, the verification result must be valid, and inspection of the result must not be partial or truncated. Otherwise the operation fails closed and no output bytes are saved. This verifies only the metadata categories targeted by the supported policy; it does not establish anonymity, complete privacy, provenance, pixel privacy, steganography detection, or malware safety.

## Pinned dependency
Expand Down
8 changes: 8 additions & 0 deletions docs/ux-consistency-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,11 @@ Interactive browser QA was retried on 2026-08-26 against a temporary localhost s
Sprint 17 localized shared brand, primary-navigation, footer-navigation, category-navigation, and local-processing-summary accessible names across all six interface languages. Homepage proof copy now states the bounded invariant—no production file-content upload—rather than the absolute word “Nothing,” and production PDF/Image category notes no longer mention planned cards that are not present. Static i18n and UX tests enforce these contracts across all 19 production pages.

Interactive browser QA was retried on 2026-08-26 against a temporary localhost server. Browser control failed before navigation with `windows sandbox failed: helper_unknown_error: apply deny-read ACLs`. Visual rendering, responsive interaction, keyboard operation, browser-native save/download, and Network-panel checks remain **BLOCKED**, not passed. The complete open matrix is maintained in [v2 promotion QA](./v2-release-qa.md).

## Sprint 18 Metadata UX follow-up

Sprint 18 aligned the Image Metadata and PDF Metadata source cards, made decoded values the primary inspection surface, and moved opaque containers, coverage, diagnostics, and complete supported-field views into native details disclosures. Default Privacy Clean remains the primary action; Customize exposes only supported class-level controls and does not offer individual-value editing or broaden either tool’s documented scope. Both cleaners now require their requested verification checks to pass before any output write.

Automated tests cover source reset and object-URL cleanup, decoded/opaque grouping, format-specific Image policies and expectations, PDF default/custom selection, retained-field fail-closed behavior, six-locale parity, responsive CSS contracts, security invariants, and the release gate. `git diff --check` and `node tests/run-all.mjs` passed on 2026-08-26.

Interactive browser QA was attempted against `127.0.0.1:4173`, but the browser-control runtime terminated before navigation with `windows sandbox failed: helper_unknown_error: apply deny-read ACLs`. Visual rendering, keyboard interaction, responsive behavior, native save/download, and Network-panel observations remain **BLOCKED**, not passed. The live manual matrix is maintained in [v2 promotion QA](./v2-release-qa.md).
3 changes: 2 additions & 1 deletion docs/v2-release-qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Audited on 2026-08-26 against repository records and official npm registry metad
| Shared input, queue, source, status, cancellation, save, and recovery checks | PASS |
| Per-tool file, queue, dimension, pixel, render, and aggregate-work boundaries | PASS |
| Pull-request CI | REQUIRED BEFORE MERGE |
| Sprint 18 Metadata source cards, decoded-first disclosure, safe customization, and fail-closed verification contracts | PASS |

The Node runner reports `MODULE_TYPELESS_PACKAGE_JSON` warnings because a parent user-level package file does not declare a module type. The static repository intentionally has no package manager or build configuration; all tests execute successfully.

Expand Down Expand Up @@ -113,7 +114,7 @@ For `/`, every production tool, all five category hubs, `/privacy/`, `/about/`,
- [ ] Inspect the Network panel while adding and processing files; confirm no file-content upload and no unexpected runtime request.
- [ ] Inspect saved outputs for correct type, name, ordering, dimensions/pages, transparency behavior, and openability.
- [ ] Exercise corrupt, unsupported, oversized, encrypted/password-protected, cancellation, save-cancellation, and repeated-operation paths applicable to each tool.
- [ ] For both metadata tools, confirm partial/opaque wording remains bounded, cleaning matches the documented supported scope, verification failure prevents saving, and the source remains retryable.
- [ ] For both metadata tools, confirm the source card, remove/reset focus path, decoded-first summary, native details disclosure, and keyboard-accessible Customize controls across all six locales. Confirm partial/opaque wording remains bounded, default Privacy Clean and custom cleaning match the documented supported scope, verification failure prevents saving, and the source remains retryable.
- [ ] Confirm Privacy links route only to the two production metadata tools and Scan/OCR and Media cards remain non-interactive planned content.

## Recording manual evidence
Expand Down
4 changes: 3 additions & 1 deletion js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { imageResizeLocales } from "./locales/image-resize.js";
import { imageCompressorLocales } from "./locales/image-compressor.js";
import { imageMetadataLocales } from "./locales/image-metadata.js";
import { privacyHubLocales } from "./locales/privacy-hub.js";
import { metadataUxLocales } from "./locales/metadata-ux.js";

const STORAGE_KEY = "secure-tools-language";
const baseTranslations = { en, ko, ja, es, de, fr };
Expand All @@ -18,7 +19,8 @@ export const translations = Object.fromEntries(Object.entries(baseTranslations).
categories: { ...catalog.categories, image: { ...catalog.categories.image, metadata: imageMetadataLocales[language].categoryDescription } },
imageResize: imageResizeLocales[language].copy,
imageCompressor: imageCompressorLocales[language].copy,
imageMetadata: imageMetadataLocales[language].copy,
imageMetadata: { ...imageMetadataLocales[language].copy, source: { ...imageMetadataLocales[language].copy.source, ...metadataUxLocales[language].image.source }, inspector: { ...imageMetadataLocales[language].copy.inspector, ...metadataUxLocales[language].image.inspector }, clean: { ...imageMetadataLocales[language].copy.clean, ...metadataUxLocales[language].image.clean }, policy: metadataUxLocales[language].image.policy },
pdfMetadata: { ...catalog.pdfMetadata, source: { ...catalog.pdfMetadata.source, ...metadataUxLocales[language].pdf.source }, inspector: { ...catalog.pdfMetadata.inspector, ...metadataUxLocales[language].pdf.inspector }, actions: { ...catalog.pdfMetadata.actions, ...metadataUxLocales[language].pdf.actions }, custom: metadataUxLocales[language].pdf.custom, errors: { ...catalog.pdfMetadata.errors, ...metadataUxLocales[language].pdf.errors } },
privacyHub: privacyHubLocales[language].copy,
}]));

Expand Down
Loading
Loading