feature: branded highlight outro (renderer + cache)#29
Conversation
…yer chip when a branded outro fails at non-baked dims/fps)
- 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.
Code + security review (multi-agent), addressedIndependent code review (Sonnet) and security review (Opus), plus a security re-review of the fix. Fix commits: Code review, fixed
Security review, fixed
Required follow-up (NOT in this PR; infra, higher priority)The spec-server Minor edge: the URL allowlist uses |
Part 1 of 2:
game-streamer(merge FIRST). Companion: thefeature/branded-outroPR in5stackgg/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_namewordmark, 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 existingclip_bake_brandingtoggle.This PR is the renderer + cache-resolution half. The
apiPR is the orchestration half (decides hit/miss, presigns, passes env).Changes
motion/src/Outro.tsx: optionallogoUrl/brandName/accentprops. With no props it renders today's stock outro unchanged (DEFAULT_OUTRO_PROPSuntouched). With them: custom logo (objectFit: contain, gated behindlogoUrl), a length-agnostic decrypt of the brand name, accent-colored throughout, 5Stack taglines hidden.src/lib/outro.sh(new):resolve_outro_fileresolves 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.shis a pure-bash test suite.src/lib/inline-clip-render.sh: sourcesoutro.sh; uses a cheap pre-loop predicate (outro_will_append) plus a concat-timeresolve_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 allowlistedCLIP_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), orCLIP_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 --noEmitclean. Remotion render smoke: stock and branded (name+accent) both produce a valid 180-frame h264 mp4 (1280x720).outro.test.shpasses (inactive, hit, miss, render-failure fallback, will-append, baked-exists, version-keying, dims-mismatch, basename sanitization).bash -nandnode --checkclean.