praisonai-platform: JWT signing key defaults to hardcoded "dev-secret-change-me", allowing token forgery for any user when PLATFORM_ENV is unset
Date: 2026-05
Affected: pip/praisonai-platform
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: Critical
CVE: CVE-2026-47410
CWE: CWE-321
CWE-798
CVSS: 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

## Summary **Type:** Insecure default cryptographic key. The JWT signing secret defaults to the hardcoded literal `"dev-secret-change-me"` when `PLATFORM_JWT_SECRET` is unset. A safety check exists but only fires when `PLATFORM_ENV != "dev"`; the default value of `PLATFORM_ENV` is `"dev"`, so the check is silently bypassed in any deployment that does not explicitly opt out. The attacker reads the literal from this public source file, mints a JWT with arbitrary `sub` and `email` claims, and authenticates as any existing user (including workspace owners and admins). **File:** `src/praisonai-platform/praisonai_platform/services/auth_service.py`, lines 25-36 and 114-137. **Root cause:** the production-mode guard checks `os.environ.get("PLATFORM_ENV", "dev") != "dev"` — but the default is `"dev"`, so a clean deployment that just imports the package and runs `uvicorn praisonai_platform.api.app:app` proceeds with the hardcoded secret. The package documentation does not warn loudly enough that BOTH variables must be set; the guard suppresses itself when either condition is missed. JWT verification at line 129 trusts whatever the token says (`sub`, `email`, `name`) once the HMAC-SHA256 signature validates against the publicly-known secret. Since the verifier accepts forged tokens for any user_id, the attacker becomes that user across every authenticated route. ## Affected Code **File:** `src/praisonai-platform/praisonai_platform/services/auth_service.py`, lines 25-36 and 114-137.


Mitigations:

Architectural layers (MAESTRO):