Skip to content

[Product Gap] Remove buyer-demo shell and hard-coded demo authority from production UI #317

Open

Description

@seonghobae

Buyer-visible outcome

Replace Clearfolio's production-facing buyer-demo authority with a real tenant-aware document workspace and fail-closed service contract. Development/demo fixtures may remain available only through an explicit non-production profile or separately packaged test/demo artifact; they must not define production identity, permissions, evidence, or success semantics.

Fresh protected-main evidence — 2026-08-10

At protected main 83ec6f7fe2b04bdcd28bf98ec350e41e55730a18 the gap exists at root UI, document-viewer JavaScript, demo JavaScript, and service layers:

  • ViewerUiController.home() returns the historical demo shell and the root page still exposes Load demo story, seeded recovery/KPI controls and demonstration-specific copy;
  • src/main/resources/static/assets/viewer/demo.js contains hard-coded buyer-demo tenant/subject/permission headers, loads /assets/viewer/demo-fixtures.json, and browser session history uses the clearfolio-demo-history-v1 key;
  • src/main/resources/static/assets/viewer/viewer.js also defines DEMO_AUTH_HEADERS with buyer-demo / buyer-demo-operator and automatically sends those headers from fetchJson() for status and viewer-bootstrap requests whenever the URL does not carry a signed artifactToken. This is the production document-viewer asset, so the demo-authority problem is not confined to the root demo shell;
  • the same viewer.js has a safer external-integration path that accepts a server-issued signed artifactToken and directly renders the artifact without tenant-header bootstrap. That proves a current no-demo-authority byte-delivery path exists, but it does not by itself supply the missing authenticated status/bootstrap workspace contract;
  • DefaultDocumentConversionService.submit(file, overrideRequest) constructs TenantContext(DEMO_TENANT_ID, DEMO_SUBJECT_ID, Set.of());
  • the three-argument submit(..., tenantContext) path silently substitutes the same demo tenant/subject when tenantContext == null;
  • current security parent fix(security): harden audit pseudonymization and refresh Netty evidence #270 still carries the legacy two-argument demo fallback, so this cannot be dismissed as stale UI-only code;
  • the demo.js/dom-utils.js paths are also the current work surface for fix(accessibility): make asynchronous viewer actions nested-safe #264's reusable nested-safe accessibility behavior and must not be raced by a competing writer;
  • viewer.js itself is not currently in fix(accessibility): make asynchronous viewer actions nested-safe #264 or fix(security): harden audit pseudonymization and refresh Netty evidence #270's changed-file set, but removing its demo headers without first supplying a real server-controlled session/bootstrap authority would break the existing unsigned status/bootstrap flow. Therefore the next source slice must establish the production auth/bootstrap contract rather than merely deleting constants and silently making the viewer unusable.

This is a product/security boundary problem, not a naming nit: a production distribution must not create identity or authorization context from hard-coded demonstration values when real authority is absent.

Active bounded work / dependency boundary

Do not race or duplicate active work:

First stabilize/integrate #270 and reconcile #264. Do not copy their source paths onto a competing branch. The production workspace may initially consume the existing signed tenant-header adapter behind a server-controlled bootstrap boundary, but browser or service code must not manufacture demo tenant IDs or permissions.

Required product architecture

Separate the surfaces explicitly:

  1. Production workspace

    • real authenticated/tenant-scoped document intake and history;
    • no seeded demo story or fake KPI/recovery evidence;
    • permissions derived from verified server-side identity/bootstrap authority, never browser constants;
    • the document viewer receives only server-authorized session/bootstrap state or a scoped signed artifact token; it must not synthesize tenant headers in JavaScript;
    • service methods that require tenant authority fail closed when it is absent rather than constructing demo authority;
    • real conversion job/status/view/download/deletion states only;
    • accessible nested-safe async controls using the integrated fix(accessibility): make asynchronous viewer actions nested-safe #264 primitive;
    • versioned API client aligned with issue [Product Gap] Publish a complete versioned API/schema and naruon compatibility contract #315;
    • deterministic controlled errors and non-enumerating tenant behavior.
  2. Development/demo fixture surface

    • opt-in non-production profile, test fixture, Storybook-like harness, or separately packaged demo asset;
    • clearly labelled synthetic data;
    • no production credentials or shared production storage;
    • cannot be accidentally enabled by omission of a secret/profile setting;
    • legacy convenience overloads that inject demo authority, if retained at all, are confined to explicit test/demo wiring and are impossible to select from production Spring composition;
    • any demo-auth header injector is confined to this explicit non-production surface, not viewer.js or another production asset;
    • excluded from production navigation and release acceptance except when explicitly testing the demo profile.
  3. Reusable primitives

    • move generic busy-state, DOM restoration, accessible naming, status rendering and API-client behavior out of demo.js naming/ownership where appropriate;
    • keep production code free of buyer-specific hard-coded identity, valuation, diligence assumptions and demo authority.

Security and privacy invariants

  • no hard-coded production tenant, subject, permission, token, secret or approver identity in browser assets or service fallbacks;
  • browser code must not mint or infer authorization claims;
  • viewer.js and other production assets must not automatically attach demo tenant/subject/permission headers;
  • null/missing tenant authority fails closed before validation, dedupe, persistence, worker enqueue, job lookup or artifact access in production entrypoints;
  • production requests use a reviewed identity/session/bootstrap contract and preserve same-tenant concealment;
  • signed artifact-token authority remains independent of general tenant authentication;
  • no raw document content, tokens, user identifiers or tenant identifiers are stored in local browser history unless the product retention/privacy contract explicitly requires it;
  • synthetic fixture data is unmistakably synthetic and isolated from real customer data;
  • development fallbacks fail closed in production rather than silently selecting demo authority.

Test-first acceptance

On the stable integrated baseline, establish RED evidence before implementation. Require at least:

  • production / does not contain buyer-demo, Load demo story, seeded diligence/KPI fixture controls, or hard-coded demo identity;
  • production viewer.js and all other production static JavaScript contain no DEMO_AUTH_HEADERS, buyer-demo, buyer-demo-operator, or equivalent hard-coded tenant/subject/permission authority;
  • unsigned viewer status/bootstrap requests use a reviewed server-controlled authenticated session/bootstrap fixture or fail closed; deleting demo headers without a replacement authority is not an acceptable GREEN;
  • signed artifactToken viewer mode continues to render the scoped artifact without requiring tenant-header synthesis and preserves token expiry/scope/revocation/checksum/read-audit authority;
  • production service submission with absent/null tenant context fails closed and does not validate, deduplicate, persist or enqueue a job under demo authority;
  • production wiring exposes no legacy overload that can silently create DEMO_TENANT_ID / DEMO_SUBJECT_ID;
  • explicit development/demo wiring still supports deterministic synthetic fixtures without being selectable by production omission/misconfiguration;
  • production workspace successfully uses a verified tenant/session/bootstrap fixture without embedding credential values in source;
  • missing/invalid production identity fails closed before document/job data is read;
  • cross-tenant job probes remain concealed;
  • real upload → poll → viewer workflow test;
  • real successful artifact access through the integrated signed-delivery contract;
  • lifecycle/deletion UI only after [Product Gap] Complete tenant-safe document lifecycle and artifact deletion #263 substrate is available, with truthful pending/failure/completion states;
  • keyboard/touch/screen-reader/focus restoration and nested concurrent-action tests using the fix(accessibility): make asynchronous viewer actions nested-safe #264 primitive;
  • production profile cannot enable the demo fixture surface accidentally;
  • browser storage tests prove tenant/session data does not leak between identities;
  • exact 100% owned production statement/branch coverage for new production code and complete beginner-readable public docs/docstrings;
  • exact-head CI, Security Scan, SAST, applicable JS coverage/accessibility tests, independent review and repository protection.

Documentation / release updates

Update canonical PRD/TRD/Architecture, UML, API contract, Security/Threat Model, Test Strategy, Operability, Traceability, Acquisition Diligence, README/AGENTS/CLAUDE and CHANGELOG with truthful maturity. Demote dated seeded buyer-demo artifacts to historical/demo evidence rather than deleting useful provenance.

Release evidence must prove the production distribution cannot silently expose either browser-side or service-layer demo authority.

Suggested implementation order

  1. Integrate/reconcile fix(security): harden audit pseudonymization and refresh Netty evidence #270 and fix(accessibility): make asynchronous viewer actions nested-safe #264; extract reusable async/DOM primitives without demo-specific ownership.
  2. Define a real server-controlled viewer/session bootstrap contract that can replace viewer.js demo header synthesis without breaking status/bootstrap access.
  3. Add RED browser/service tests for hard-coded viewer auth and missing/null tenant authority; remove/confine demo-authority paths on the stable baseline.
  4. Add the production workspace bootstrap/auth contract to the versioned API client.
  5. Move seeded browser fixtures behind an explicit non-production surface/profile.
  6. Replace the root buyer-demo shell with the production workspace.
  7. Add end-to-end tenant isolation, real upload/view, signed-token viewer, accessibility and production-profile negative tests.
  8. Align the production client with [Product Gap] Publish a complete versioned API/schema and naruon compatibility contract #315 and later [Product Gap] Add production OIDC/JWT identity federation with tenant-scoped authorization #314 without embedding provider-specific auth logic in UI.

Do not create a competing demo.js writer while #264 is active, and do not remove viewer.js demo headers as a standalone deletion patch that leaves the production viewer without a legitimate authority path.

Activity

  1. seonghobae commented on Aug 10, 2026

    @seonghobae
    CollaboratorAuthor

    Active bounded slice: PR #318 owns only ViewerUiController branding integrity and deliberately does not touch demo.js/dom-utils.js while #264 owns those paths. Test-only head 2b7483a428c54baf41afb4d5d024189ecb891317 produced the intended RED: both / and /viewer/{docId} still rendered the unrelated HYOSUNG ownership claim. Current #318 head 076574d5145640c881290f5db4aed3ae5fdf8c1f replaces only those two footer claims with Clearfolio Viewer · Apache-2.0; CI 31351475453, Security 31351475466, SAST 31351475434, and fuzz 31351475440 are green. #318 remains Draft pending independent review. This does not complete issue #317: the buyer-demo root workflow, seeded controls, browser-side demo authority, and production-workspace/bootstrap split remain dependency-gated as described above.

  2. seonghobae commented on Aug 10, 2026

    @seonghobae
    CollaboratorAuthor

    Fresh protected-main evidence expands this product-boundary gap beyond browser assets. At main 83ec6f7fe2b04bdcd28bf98ec350e41e55730a18, DefaultDocumentConversionService.submit(file, overrideRequest) constructs TenantContext(DEMO_TENANT_ID, DEMO_SUBJECT_ID, Set.of()), and the three-argument submit path also falls back to the same demo identity when tenantContext == null. Current #270 head 0f9db3903526bdec795cffb228aa76844921ac6f still contains the two-argument demo fallback. This means the production service layer itself still has a hard-coded buyer-demo authority path; it is not only a UI naming problem.

    Treat this as part of issue #317 rather than opening a duplicate issue. On the stable #270/#264 baseline, add RED tests proving production/service entrypoints cannot silently create a demo tenant/subject when tenant authority is absent, then remove or explicitly confine legacy/demo overloads behind a non-production fixture/profile boundary. Missing tenant context must fail closed in production. Keep test-only fixture convenience separate from shipped service authority, and preserve tenant-scoped authorization work already owned by #270/#268/#314.

  3. added
    area: accessibilityAccessibility and assistive-technology support
    area: authAuthentication, authorization, identity, or tenant isolation
    area: securitySecurity boundary, hardening, or vulnerability prevention
    status: triagedOpen issue has an organization taxonomy assignment
    type: featureNew or expanded product capability
    on Aug 22, 2026
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: accessibilityAccessibility and assistive-technology supportarea: authAuthentication, authorization, identity, or tenant isolationarea: dependenciesDependency or lockfile maintenancearea: securitySecurity boundary, hardening, or vulnerability preventionmaintenancepriority: mediumNormal-priority or P2 workscope: product-gapCustomer-visible product gapstatus: triagedOpen issue has an organization taxonomy assignmenttype: featureNew or expanded product capability

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions