Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

6.7 KiB
Raw Permalink Blame History

PRD — Invoice Template + Designer Safety Hardening

  • Slug: invoice-template-designer-safety-hardening
  • Date: 2026-02-13
  • Status: Draft

Summary

Tighten safety and correctness around the new JSON-AST invoice renderer and the invoice designers unified “generic node + props + children” state by:

  • Preventing accidental credential leaks in the repo.
  • Hardening the generic patch/mutation API against prototype pollution and ambiguous semantics.
  • Eliminating dual-source-of-truth behavior (stop writing the same value to multiple places).
  • Making undo/redo semantics deterministic for JSON-serializable state.
  • Validating CSS identifier inputs in the invoice template AST (styles/tokens/classes).

Problem

After the invoice-template JSON AST cutover and the unified designer patch API work, the codebase has a few sharp edges:

  • Secrets/credentials can be accidentally committed (recent .env backups were tracked).
  • The generic patch API (setNodeProp/unsetNodeProp) can write attacker-controlled keys like __proto__, creating prototype pollution risk.
  • Designer state is currently duplicated during cutover (props.* plus legacy top-level fields; children plus childIds), increasing divergence risk.
  • Undo/redo stores full snapshots and clones props via JSON serialization, which is expensive and can change values if state contains non-JSON primitives or undefined in arrays.
  • Invoice template AST style tokens/classes are emitted into CSS without identifier validation.

Goals

  • Ensure .env backups and similar credential files cannot be accidentally tracked again.
  • Make the designers generic patch/mutation API explicitly safe:
    • Reject prototype-pollution keys in patch paths.
    • Define deterministic semantics for array unset.
  • Make node.props and node.children the single canonical sources of truth for designer node data/hierarchy.
  • Keep undo/redo behavior correct and deterministic for JSON-serializable designer state.
  • Ensure invoice template AST style identifiers are safe and validated before rendering.

Non-goals

  • No user-facing redesign of the invoice designer UI.
  • No migration tooling for existing customer templates (there are none).
  • No new invoice template “scripting” capabilities beyond the existing allowlisted strategy mechanism.
  • No broad performance rewrite of the designer/store beyond what is required to remove correctness/safety hazards.

Users and Primary Flows

  • Template authors (internal, for now) edit invoice layouts in the designer and preview them against sample/existing invoice data.
  • Developers iterate locally without accidentally committing secrets.

UX / UI Notes

  • No expected visual changes.
  • Any newly-added validation errors should surface as existing diagnostics/toasts (no new UI framework work).

Requirements

Functional Requirements

  1. Repo secrets hygiene:
    1. Add gitignore patterns for local env backups so they dont get staged.
    2. Add a lightweight guard (test or CI check) that fails if known env-backup patterns are tracked.
  2. Generic patch/mutation API hardening (explicit requirement):
    1. setNodeProp(nodeId, path, value, commit?) and unsetNodeProp(nodeId, path, commit?) must reject unsafe path segments (__proto__, prototype, constructor) and perform no mutation on rejection.
    2. Patch operations must be deterministic and must not introduce non-JSON values into canonical designer state.
    3. Patch operations must preserve immutability (return structurally-shared copies, no in-place mutation of existing nodes/state).
  3. Canonical designer node representation:
    1. Canonical fields: node.props and node.children.
    2. Legacy fields (name, metadata, layout, style, childIds) must not be separately mutated during the cutover. If they exist for back-compat, they must be derived from canonical fields or only used when importing legacy data.
  4. Undo/redo semantics:
    1. Undo/redo must restore the exact canonical JSON state that was committed.
    2. Array unset behavior must be defined and tested (see Non-functional requirements).
  5. Invoice template AST CSS identifier safety:
    1. styles.classes keys, styles.tokens.*.id, and node.style.tokenIds must be validated against a safe identifier rule.
    2. Invalid identifiers must cause schema validation failure (and should surface as diagnostics rather than producing malformed CSS).

Non-functional Requirements

  1. Canonical designer node state must remain JSON-serializable.
  2. Array unsetting semantics decision:
    1. unset for an array index at the leaf path must not produce undefined holes.
    2. Preferred implementation: leaf-array unset performs a splice (removes the element, shifting indices).
  3. Patch path parsing must have a small, well-defined grammar (dot-separated segments; integer segments for arrays).
  4. Any performance impact must be bounded:
    1. Continuous interactions (drag/resize/typing) must use commit=false for interim updates and commit=true only on completion.
    2. History snapshot creation must not occur on every pointer-move.

Data / API / Integrations

  • No external API changes.
  • Internal shape: designer state uses a unified JSON tree (node map + children).
  • Invoice template AST schema validation is the gate before evaluation/rendering.

Security / Permissions

  • Prevent leaking .env-like credentials via git tracking.
  • Prevent prototype pollution via patch path segments in generic mutation APIs.
  • Ensure invoice template AST style identifiers cannot inject arbitrary CSS selectors/variables.

Observability

  • Rejected patch operations should be observable in development:
    • console warning (developer-facing), and/or
    • surfaced as diagnostics in existing debug panels if available.

Rollout / Migration

  • No customer migration required.
  • For local dev: .gitignore prevents recurrence; guard ensures CI catches accidental tracking.

Open Questions

  • None required to start. Any discovered call sites depending on legacy top-level fields should be migrated to canonical props reads.

Acceptance Criteria (Definition of Done)

  1. No .env.local.bak* (or similar) files are tracked by git, and guardrails exist to prevent recurrence.
  2. setNodeProp/unsetNodeProp reject __proto__/prototype/constructor path segments and cannot pollute prototypes.
  3. Designer node data has a single source of truth:
    1. props is canonical for name/layout/style/metadata.
    2. children is canonical for hierarchy; no childIds reliance remains.
  4. Undo/redo restores canonical JSON state without undefined hole issues, and continuous interactions do not spam history entries.
  5. Invoice template AST validation rejects unsafe style identifiers; renderer does not emit malformed CSS selectors/variables for style identifiers.