Skip to content

MEMBERS-DIRECTORY-001K — Validate saved WebP responses through the Account UI boundary #639

Description

@daliu

Parent: #504. Connected release remains owned by #507.

Officer impact: No live officer workflow changes. In the preserved future My Account interface, clearly mislabeled saved-thumbnail response bytes will be rejected before reaching the page, while structurally WebP bytes that the browser cannot display will keep the existing recoverable Photo unavailable fallback and Remove action.

Officer documentation: Update SYSTEM_DESIGN.md, IMPLEMENTATION_PLAN.md, SECURITY.md, and only the optional-directory one-sentence text alternative plus one no-terminal source-evidence procedure in docs/officers/EVENTS_SHOP_MEMBERS.md. The data-flow diagram does not change.

Deployment evidence: None at issue creation. This is source-only response-boundary hardening. memberDirectoryAvailability.ts must remain byte-for-byte false; no website, Firebase, provider, account/sign-in, production-data, deployment, or connected-live action is authorized.

Atomic outcome

Validate structurally WebP saved-photo responses at the actual Account component→service boundary without changing outbound photo-upload semantics.

Defect

On exact released main 7877babc96c78f54d11685b4ffb91496c3bf6945, memberDirectoryService.ts validates returned profile photos for exact fields, image/webp, canonical bounded base64, 256×256 dimensions, and UUID version, but accepts arbitrary decoded bytes. The server stored-photo boundary and officer-search client additionally require at least 12 bytes with RIFF at bytes 0–3 and WEBP at bytes 8–11. Existing profile component tests mock the service, so they cannot prove malformed callable bytes stop before the Account DOM.

Invariant

  • Only an exact returned photo object with canonical decoded bytes between 12 and 65,536 bytes, RIFF at bytes 0–3, and WEBP at bytes 8–11 may become a MemberDirectoryProfile.photo.
  • Failure uses only the fixed Invalid member directory response. error; no raw bytes, provider values, filename, or caught detail is rendered or logged.
  • Structural admission is not full image decoding. Header-valid bytes may still fail browser decode; the existing version-scoped byte-free Photo unavailable fallback must replace the image while Remove current saved photo remains available.
  • The structural check applies only to returned saved-photo state. Outbound JPEG, PNG, and WebP upload admission and exact bytes remain unchanged.
  • The actual component→actual service→mocked firebase/functions bridge is tested in the existing service test file; no real Firebase/network/account/data access.
  • Availability stays false; the default branch remains inert and zero-call.
  • No photo query, facial recognition, matching, embedding, similarity, biometric processing, roster authority, or membership proof.

Exact ownership

  • src/services/account/memberDirectoryService.ts
    • only its private canonical-base64 helper return value and inbound returned-photo RIFF/WEBP structural admission.
  • src/services/account/memberDirectoryService.test.ts
    • one separately named MEMBERS-DIRECTORY-001K block covering the response matrix and a real MemberDirectoryProfile→actual service→mocked firebase/functions bridge; reuse this existing file, with no rename/new test file.
  • SYSTEM_DESIGN.md
    • one separately named 001K source-only paragraph.
  • IMPLEMENTATION_PLAN.md
    • one separately named 001K current-source paragraph.
  • SECURITY.md
  • docs/officers/EVENTS_SHOP_MEMBERS.md
    • only the optional-directory one-sentence text alternative and one separately named no-terminal 001K evidence procedure; no Mermaid change.

Required tests

  • Capture trustworthy RED on old runtime for canonical non-WebP bytes resolving and reaching the actual connected component image path.
  • Accept an exact 12-byte RIFF/WEBP structural envelope.
  • Accept an exact 65,536-byte structurally valid envelope.
  • Reject empty, truncated (<12), wrong-RIFF, wrong-WEBP, noncanonical, and 65,537-byte responses with the one fixed error.
  • Preserve exact MIME/dimension/version/object/accessor/hostile-value validation.
  • Prove canonical non-RIFF outbound WebP upload bytes still reach the callable unchanged.
  • Through the real component and real service with only firebase/functions mocked, prove mislabeled bytes render the generic unavailable state and never create a saved-thumbnail img or data URL.
  • Through that bridge, prove admitted-but-browser-undecodable bytes initially reach the fixed-type saved image; a synthetic error then removes its data URL, renders the accessible Photo unavailable fallback, keeps Remove enabled, and creates no extra callable.
  • Default unavailable branch remains zero-context, zero-ID, and zero-call.
  • Focused/full frontend, TypeScript, lint baseline, diagnostic build, workflow/security tests, diff check, and independent security/frontend/operations reviews pass.

Explicit exclusions

Migration and residual risk

No migration. Client magic validation is structural admission, not full decoding, authenticity, server-state proof, provider authorization, or deployment evidence. Browser decode fallback remains necessary. #507 retains all connection/release gates.

Claim protocol

Assign the issue and post a timestamped CLAIMED marker naming the exact branch and released base before any source edit. The claim remains active through reviewed merge, exact-main audit, closure, and explicit release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:membershipMembership lifecycle and reconciliationarea:privacyPersonal data, consent, minimization, retention, and privacy operationsarea:webWeb application and hostingpriority:P1High-priority follow-upsize:SSmall focused issuetype:reliabilityReliability and recoverytype:securitySecurity or privacy boundarytype:testingTest infrastructure and quality gates

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions