Skip to content

feature: branded highlight outro (renderer + cache)#29

Open
Flegma wants to merge 9 commits into
mainfrom
feature/branded-outro
Open

feature: branded highlight outro (renderer + cache)#29
Flegma wants to merge 9 commits into
mainfrom
feature/branded-outro

Conversation

@Flegma

@Flegma Flegma commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Part 1 of 2: game-streamer (merge FIRST). Companion: the feature/branded-outro PR in 5stackgg/api. Full design, both PRs, and merge order: 5stackgg/5stack-panel#514.

What

Makes the highlight outro render the deployment's panel branding (custom logo, brand_name wordmark, and tactical-amber accent) instead of the hardcoded 5Stack outro. The branded outro is rendered once per branding version and cached in S3; with no custom branding it stays the stock 5Stack outro, byte-for-byte (the build-time bake is unchanged). Gated by the existing clip_bake_branding toggle.

This PR is the renderer + cache-resolution half. The api PR is the orchestration half (decides hit/miss, presigns, passes env).

Changes

  • motion/src/Outro.tsx: optional logoUrl / brandName / accent props. With no props it renders today's stock outro unchanged (DEFAULT_OUTRO_PROPS untouched). With them: custom logo (objectFit: contain, gated behind logoUrl), a length-agnostic decrypt of the brand name, accent-colored throughout, 5Stack taglines hidden.
  • src/lib/outro.sh (new): resolve_outro_file resolves the outro three ways. S3 cache hit downloads it; a miss renders via Remotion and uploads to a presigned PUT; otherwise the baked stock outro. Includes an in-pod cache (render once per pod), dims re-validation, and a guaranteed baked-stock fallback on any failure (never fails the clip). outro.test.sh is a pure-bash test suite.
  • src/lib/inline-clip-render.sh: sources outro.sh; uses a cheap pre-loop predicate (outro_will_append) plus a concat-time resolve_outro_file. Fuses chip and outro into one encode only when a baked fallback exists, so a branded-outro failure at a non-baked dims/fps cannot strand the deferred player chip.
  • src/spectator/routes/render-clip.mjs: forwards allowlisted CLIP_OUTRO_* / CLIP_BRAND_* env from the api dispatch (on-demand path).

Env contract (shared with the api PR; names must match)

CLIP_OUTRO_URL (cache hit), or CLIP_OUTRO_RENDER=1 + CLIP_OUTRO_PUT_URL + CLIP_BRAND_LOGO_URL + CLIP_BRAND_NAME + CLIP_BRAND_ACCENT (miss).

Merge order

Merge this first. CI then rebuilds :latest (render pods auto-pull, imagePullPolicy: Always). Then merge the api PR to activate. Both directions are safe (stock fallback); this order avoids the api instructing an old renderer that cannot fulfil render+cache.

Verification

  • tsc --noEmit clean. Remotion render smoke: stock and branded (name+accent) both produce a valid 180-frame h264 mp4 (1280x720).
  • outro.test.sh passes (inactive, hit, miss, render-failure fallback, will-append, baked-exists, version-keying, dims-mismatch, basename sanitization).
  • bash -n and node --check clean.
  • Reviewed per task plus a whole-branch review on the most capable model; a chip-drop edge case it found was fixed in this branch.

Flegma added 2 commits June 20, 2026 22:19
- render-clip.mjs: origin-allowlist the URL-bearing outro env keys against the
  S3/MinIO endpoint (from DEMO_URL) — closes SSRF (logo <Img> via headless
  Chromium) / arbitrary-PUT via the unauthenticated render-clip endpoint;
  validate output_dims.
- outro.sh: version-key the in-pod cache off the presigned URL object basename
  (fixes stale outro after a mid-pod branding change); drop curl --show-error so
  a failed transfer cannot log a presigned URL; guard empty pin array under set -u.
- outro.test.sh: cover version-keying, dims-mismatch, and will_append URL/RENDER.
… DEMO_URL

Keeps branded outros working for faceit/external demos (whose DEMO_URL origin
differs from the S3 presign origin), and sanitizes the version-keyed cache
basename to a safe charset. Follow-up to the PR #29 security re-review.
@Flegma

Flegma commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Code + security review (multi-agent), addressed

Independent code review (Sonnet) and security review (Opus), plus a security re-review of the fix. Fix commits: 9cd026a, 13ded79.

Code review, fixed

  • Stale in-pod cache on a mid-pod branding change. The long-lived demo-session pod serves many clips; the cache was keyed on dims/fps only, so a branding change between two clips on the same pod kept serving the old outro until the pod restarted. Fixed: the in-pod cache is now version-keyed off the presigned URL's object basename (outro_<version>_<dims>_<fps>.mp4). outro.test.sh expanded to 14 cases (version-keying, dims-mismatch, will_append env cases, basename sanitization).
  • Empty-array set -u guard added. The comma-to-space CSS-syntax note on the stock path renders pixel-identically (acknowledged, no change).

Security review, fixed

  • Critical: SSRF + arbitrary-PUT via outro_env. The spec-server render-clip endpoint is unauthenticated and host-networked, so its POST body is untrusted; the allowlist passed CLIP_BRAND_LOGO_URL (which reaches a headless Chromium <Img src>) and CLIP_OUTRO_PUT_URL (which reaches curl --upload-file) through with no value check, enabling SSRF (cloud metadata / internal hosts) and arbitrary PUT. Fixed: the URL-bearing keys are origin-allowlisted to the S3/MinIO presign origin (S3_PUBLIC_ORIGIN, a trusted pod env), failing safe (reject to baked stock) when absent. Re-reviewed and confirmed closed: it resists userinfo/look-alike/port/IP-form tricks, curl uses no -L, and the only redirect surface is the trusted S3 origin.
  • Medium: output_dims is now validated (/^\d{2,4}x\d{2,4}$/). Low: dropped curl --show-error so a failed transfer cannot print a presigned (signature-bearing) URL to pod logs; cache basename sanitized to a safe charset.

Required follow-up (NOT in this PR; infra, higher priority)

The spec-server /demo/* routes (including /demo/exec, which runs arbitrary CS2 console commands) are unauthenticated and host-networked. This PR treats the request body as untrusted and allowlists it, but the endpoint itself should be authenticated and/or NetworkPolicy-restricted (or bound to 127.0.0.1, dropping hostNetwork). This is pre-existing and bigger than this feature; see 5stackgg/5stack-panel#514.

Minor edge: the URL allowlist uses https://<DEMOS_DOMAIN> (the MinIO presign origin); deployments using a non-MinIO S3 backend would fall back to the stock outro on the on-demand path (safe) until the origin is derived from the S3 config directly.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant