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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
6.7 KiB
6.7 KiB
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 designer’s 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
.envbackups 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;childrenpluschildIds), increasing divergence risk. - Undo/redo stores full snapshots and clones
propsvia JSON serialization, which is expensive and can change values if state contains non-JSON primitives orundefinedin arrays. - Invoice template AST style tokens/classes are emitted into CSS without identifier validation.
Goals
- Ensure
.envbackups and similar credential files cannot be accidentally tracked again. - Make the designer’s generic patch/mutation API explicitly safe:
- Reject prototype-pollution keys in patch paths.
- Define deterministic semantics for array
unset.
- Make
node.propsandnode.childrenthe 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
- Repo secrets hygiene:
- Add gitignore patterns for local env backups so they don’t get staged.
- Add a lightweight guard (test or CI check) that fails if known env-backup patterns are tracked.
- Generic patch/mutation API hardening (explicit requirement):
setNodeProp(nodeId, path, value, commit?)andunsetNodeProp(nodeId, path, commit?)must reject unsafe path segments (__proto__,prototype,constructor) and perform no mutation on rejection.- Patch operations must be deterministic and must not introduce non-JSON values into canonical designer state.
- Patch operations must preserve immutability (return structurally-shared copies, no in-place mutation of existing nodes/state).
- Canonical designer node representation:
- Canonical fields:
node.propsandnode.children. - 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.
- Canonical fields:
- Undo/redo semantics:
- Undo/redo must restore the exact canonical JSON state that was committed.
- Array unset behavior must be defined and tested (see Non-functional requirements).
- Invoice template AST CSS identifier safety:
styles.classeskeys,styles.tokens.*.id, andnode.style.tokenIdsmust be validated against a safe identifier rule.- Invalid identifiers must cause schema validation failure (and should surface as diagnostics rather than producing malformed CSS).
Non-functional Requirements
- Canonical designer node state must remain JSON-serializable.
- Array unsetting semantics decision:
unsetfor an array index at the leaf path must not produceundefinedholes.- Preferred implementation: leaf-array
unsetperforms asplice(removes the element, shifting indices).
- Patch path parsing must have a small, well-defined grammar (dot-separated segments; integer segments for arrays).
- Any performance impact must be bounded:
- Continuous interactions (drag/resize/typing) must use
commit=falsefor interim updates andcommit=trueonly on completion. - History snapshot creation must not occur on every pointer-move.
- Continuous interactions (drag/resize/typing) must use
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:
.gitignoreprevents 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
propsreads.
Acceptance Criteria (Definition of Done)
- No
.env.local.bak*(or similar) files are tracked by git, and guardrails exist to prevent recurrence. setNodeProp/unsetNodePropreject__proto__/prototype/constructorpath segments and cannot pollute prototypes.- Designer node data has a single source of truth:
propsis canonical for name/layout/style/metadata.childrenis canonical for hierarchy; nochildIdsreliance remains.
- Undo/redo restores canonical JSON state without
undefinedhole issues, and continuous interactions do not spam history entries. - Invoice template AST validation rejects unsafe style identifiers; renderer does not emit malformed CSS selectors/variables for style identifiers.