Severity: 🔴 Critical (security)
The Stripe secret API key is shipped to the browser inside the SPA bootstrap payload.
Root cause
StripeSettings.toUiPayload tries to strip secrets but removes the wrong field name:
// daikoku/app/fr/maif/daikoku/domain/tenantEntities.scala:400-403
override def toUiPayload: JsValue =
json.StripeSettingsFormat.writes(this).as[JsObject] ++ Json.obj("type" -> "Stripe")
- "publicKey" - "privateKey" // field is named "secretKey", not "privateKey"
StripeSettingsFormat.writes serializes secretKey (json.scala:4912-4918).
- "privateKey" is a no-op because no such key exists → secretKey stays in the payload (and the public key is removed instead).
Tenant.toUiPayload(env) embeds these settings unconditionally (tenantEntities.scala:554-556).
- That payload is served to the browser in the SPA bootstrap, including the unauthenticated login page (
HomeController.scala:240, LoginController.scala:1129).
For comparison, OtoroshiSettings.toUiPayload only returns id/url/host (tenantEntities.scala:677) — masking was clearly the intent here.
Impact
For any tenant with Stripe configured, the live secret key is readable by any visitor (page source / network tab). Full Stripe account compromise (charges, refunds, customer data).
Fix
- Change the strip to
- "secretKey" (keep publicKey, which is public).
- Verify the rendered
tenant_data no longer contains secretKey.
- Rotate the exposed Stripe secret key(s).
How to verify
Start Daikoku with a tenant that has StripeSettings, load the unauthenticated login page, inspect the tenant_data bundle → secretKey must be absent.
Severity: 🔴 Critical (security)
The Stripe secret API key is shipped to the browser inside the SPA bootstrap payload.
Root cause
StripeSettings.toUiPayloadtries to strip secrets but removes the wrong field name:StripeSettingsFormat.writesserializessecretKey(json.scala:4912-4918).- "privateKey"is a no-op because no such key exists →secretKeystays in the payload (and the public key is removed instead).Tenant.toUiPayload(env)embeds these settings unconditionally (tenantEntities.scala:554-556).HomeController.scala:240,LoginController.scala:1129).For comparison,
OtoroshiSettings.toUiPayloadonly returnsid/url/host(tenantEntities.scala:677) — masking was clearly the intent here.Impact
For any tenant with Stripe configured, the live secret key is readable by any visitor (page source / network tab). Full Stripe account compromise (charges, refunds, customer data).
Fix
- "secretKey"(keeppublicKey, which is public).tenant_datano longer containssecretKey.How to verify
Start Daikoku with a tenant that has
StripeSettings, load the unauthenticated login page, inspect thetenant_databundle →secretKeymust be absent.