Skip to content

MEMBERS-ROSTER-001A — Safe membership-roster CSV export projection (source only, unused) #403

Closed

Description

@daliu

Parent: #116 (MEMBERS-ROSTER-001 — officer-only member roster view and safe CSV export).

This is a source-only, unused, conservative safe-by-default scoped slice of #116 — the same pattern as the merged children of #92/#122/#124 (e.g. #401→#402 INSTAGRAM-002A, #400 EVENTS-001A). It lands the CSV-serialization mechanism for a membership roster export as one pure, deterministic, node:util-only CommonJS module plus an exhaustive negative-heavy test, imported by nothing. It does not wire any route, query, capability gate, audit event, or deploy — those remain owned by parent #116 and its dependencies.

Scope of this slice (mechanism, not policy)

functions/membershipRosterProjection.js — projectMembershipRosterCsv(evidence):

  • Column allowlist projection (allowlist, never denylist). The output columns come from a closed registry of owner-shaped column profiles. Any row key that is not in the selected profile is silently dropped from output — a protected/sensitive field that appears in a source row can never leak into the CSV, even if added to source documents later. False-negative-safe: a borderline field is withheld, never emitted.
  • Spreadsheet formula-injection neutralization (CWE-1236). Any cell whose first character is a formula lead (= + - @ TAB CR) is prefixed with a leading apostrophe so Google Sheets / Excel treat it as text.
  • RFC4180 structural safety. Every field is always-quoted and internal quotes doubled; embedded commas/quotes/newlines round-trip; records are CRLF-separated.
  • Cell content safety. Rejects NUL/other C0/DEL/C1 control chars and U+2028/U+2029 (allows only TAB/LF/CR inside quoted cells); rejects non-string cells (callers pre-stringify); over-length cells rejected.
  • Bounds (DoS guards). Max rows, max keys per row, max cell length, max total output length — all enforced.
  • Hostile-input hardening. Proxy rejection, own-enumerable-data-descriptor-only reads (getters never invoked), prototype/inherited-key rejection, array-hole rejection, exact-key envelope validation. On any invalid evidence it throws one fixed frozen typed error that never echoes input.
  • Frozen versioned surface (membershipRosterSchemaVersion, frozen error class + column-profile enum + profile registry + factory export).

Policy stays with the owner. The seed column profiles in this module are a conservative minimum. Parent #116 selects one owner-approved column profile from #110; that approved profile replaces or extends this seed registry. This contract owns the mechanism (safe serialization) and never the policy (which columns/fields/access are approved).

Explicitly NOT in this slice (stays in #116)

  • Officer-only website view, live canonical membership query + current-term rule.
  • membership_roster_view/membership_export capability, MFA / recent-authentication, stated purpose.
  • Append-only server-side audit event per export attempt.
  • Direct authenticated download / short-lived one-use capability; filename policy.
  • Firestore Rules, deployment, and runmprc.com verification.

None of #116's acceptance criteria are claimed as satisfied by this slice.

Safety / deploy posture

The module is imported by nothing (functions/index.js does not require it) — it has zero runtime behavior and nothing awaits deploy. It is landed first so the eventual #116 wiring is a mechanical hookup of an already-proven serializer rather than net-new safety judgment.

Claim protocol

Creation is not a claim on #116. This child is a source-only engineering slice; a timestamped CLAIMED comment will be posted here before the implementation files are edited, per the parent's protocol. Owner approval of roster fields/access (#110) remains a separate prerequisite for #116 production-readiness.

Activity

  1. added
    type:securitySecurity or privacy boundary
    area:membershipMembership lifecycle and reconciliation
    area:privacyPersonal data, consent, minimization, retention, and privacy operations
    on Jul 21, 2026
  2. daliu commented on Jul 21, 2026

    @daliu
    ContributorAuthor

    CLAIMED by Claude Code (daliu) at 2026-07-21T09:08:44Z; branch members-roster-001a-csv-projection; PR #404.

    Source-only engineering slice only — the pure projectMembershipRosterCsv serialization mechanism, imported by nothing. This claim does not claim parent #116, whose full implementation stays gated on its dependencies, an owner-approved column profile (#110), a scoped capability + MFA/recent-auth, per-export audit, the authenticated download flow, and deploy verification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:membershipMembership lifecycle and reconciliationarea:privacyPersonal data, consent, minimization, retention, and privacy operationstype:securitySecurity or privacy boundary

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions