← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-39970 · CWE-79 · Disclosed 2026-05-22

TypeBot is a chatbot builder tool

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a booby-trapped badge photo pinned to a public bulletin board, not a master key to the building

CVE-2026-39970 is a stored XSS flaw in Typebot's profile-picture upload flow: affected builds <= 3.15.2 let an authenticated user upload a malicious SVG as an avatar, and the file is then publicly retrievable. The bug is persistence, not code execution on the server; the attacker plants active browser content inside an uploaded SVG and waits for someone to open the hosted file.

The original advisory language calls this *critical*, but that overshoots the real deployment story. In practice the chain needs authenticated upload rights, a victim who opens the raw SVG URL, and useful security context on the file-serving origin; on Typebot Cloud the sample payload is hosted from s3.typebot.io, which materially limits blast radius versus a same-origin app XSS. That combination keeps this out of the emergency lane even though the underlying bug is valid.

"Authenticated SVG upload plus a victim click on a separate file-hosting origin makes this real, but not urgent."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Get low-privileged builder access with Burp or a browser

The attacker needs a legitimate Typebot account that can reach the profile picture form in the builder UI. Using a normal browser workflow or an intercepting proxy like Burp Suite, they upload an SVG containing scriptable payloads instead of a benign image.
Conditions required:
  • Authenticated access to a vulnerable Typebot instance
  • Version <= 3.15.2
  • Profile/avatar upload allowed for the account
Where this breaks in practice:
  • This is not unauthenticated internet-to-RCE; the attacker already has an account
  • Many enterprise Typebot deployments are admin-only or tightly scoped to internal staff
Detection/coverage: Authenticated DAST or manual testing will find it; unauthenticated perimeter scanners generally will not.
STEP 02

Land the payload on public object storage

The vulnerable flow stores the SVG and exposes it at a stable public URL. The weaponized file becomes a persistent hosted object the attacker can reuse in phishing, chat, ticketing, or internal collaboration messages.
Conditions required:
  • Upload succeeds without SVG sanitization
  • Uploaded object is retrievable over HTTP(S)
Where this breaks in practice:
  • The advisory sample shows storage on s3.typebot.io, not the main app origin
  • If the browser treats the file as an image in an <img> context, script usually does not execute
Detection/coverage: S3/object-store logs, reverse-proxy logs, and content-type anomaly checks can catch suspicious .svg uploads and fetches.
STEP 03

Social-engineer the victim into opening the raw SVG

The payload needs a victim to directly open the hosted SVG or otherwise render it in a script-capable context. Typical tooling is plain delivery: email, chat, issue tracker comments, or a link pasted into a Typebot-adjacent workflow.
Conditions required:
  • Victim receives the exact file URL
  • Victim opens it in a browser context that executes SVG script
Where this breaks in practice:
  • This is a *user-action* bug, not an auto-trigger from merely existing in storage
  • Secure email gateways, browser isolation, and user skepticism break a lot of these campaigns
Detection/coverage: URL-click telemetry and web proxy logs provide the best visibility; vulnerability scanners do not see this step.
STEP 04

Execute JavaScript on the file origin

If the victim opens the SVG in a permissive browser context, attacker-controlled JavaScript runs with the origin of the hosted file. Tools are trivial here: inline <script>, external fetches, or credential-harvesting DOM overlays.
Conditions required:
  • Browser executes active content in the delivered SVG
  • Response headers and embedding context do not suppress execution
Where this breaks in practice:
  • Running on a storage origin is not the same as running on app.typebot.io
  • Same-origin protections block direct access to app cookies/local storage unless cookie scoping or self-hosting choices collapse the boundary
Detection/coverage: Browser EDR, secure web gateways, and CSP/reporting can surface abnormal outbound requests from opened SVG content.
STEP 05

Monetize only if origin boundaries are weak

The best-case attacker outcome is phishing, token theft from the file origin, or abuse of any shared parent-domain trust. In self-hosted deployments that serve uploads from the same origin, or that scope cookies too broadly, impact can climb sharply; on default separated hosting, the likely outcome is narrower client-side abuse rather than clean account takeover.
Conditions required:
  • Shared auth scope, weak cookie domain settings, or same-origin file serving
  • Victim has useful privileges
Where this breaks in practice:
  • Default cloud-style split hosting heavily reduces value
  • HTTPOnly cookies, origin separation, and modern browser controls limit post-execution leverage
Detection/coverage: Review cookie domain settings, storage origin architecture, and any CSP violation reports; those determine whether impact is nuisance-level or business-impacting.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public evidence of active exploitation found as of 2026-05-29 in the sources reviewed; not KEV-listed.
Proof-of-concept availabilityPublic PoC is effectively included in the GitHub advisory: malicious SVG upload plus a public object URL and demo artifact. That makes reproduction straightforward for anyone with a low-priv account.
EPSSUser-supplied EPSS is 0.00052. That is extremely low and lines up with the real-world friction in the exploit chain.
KEV statusNo entry in CISA KEV. No KEV due date applies.
Affected versionsGitHub advisory lists Typebot <= 3.15.2 as affected.
Fixed versionPatched in 3.16.0. Release notes also mention fix XSS on Rating and file upload inputs.
Severity baselineThere is no authoritative vendor CVSS baseline on the advisory/NVD pages reviewed, so this rating is first-principles assessed, not compared.
Practical vectorRealistically this behaves like authenticated remote upload + victim interaction + client-side execution on the file-hosting origin. That is materially weaker than a same-origin stored XSS in the main app.
Exposure/scanning realityInternet search engines are the wrong lens here: the risky step is an authenticated upload workflow followed by public object access, not a distinct exposed service banner. Public exposure volume therefore depends more on who can get builder accounts than on Shodan/Censys discoverability.
Disclosure and creditPublished 2026-05-22 via GitHub Security Advisory GHSA-jj87-c343-26vp; reporter credited as bugshunter68-lgtm.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to MEDIUM (4.8/10)

The decisive downgrade from the advisory's *critical* tone is attacker position: this starts with authenticated upload rights and usually ends on a separate file-hosting origin, not the main application origin. That combination slashes both reachable population and post-execution blast radius in real enterprise deployments.

HIGH Affected version range and fixed version
MEDIUM Exploit-chain friction on Typebot Cloud/default separated hosting
LOW Worst-case impact on unusual self-hosted deployments with shared auth scope

Why this verdict

  • Requires authenticated remote access: the attacker must already hold a valid Typebot account that can hit the avatar upload flow. That is immediate downward pressure because this is post-account-acquisition, not perimeter compromise.
  • Needs victim interaction: the payload is not a worm sitting in the main UI; a victim must open the raw SVG or equivalent active rendering context. Email security, browser isolation, and ordinary user skepticism stop a meaningful slice of attempts.
  • Default blast radius is usually origin-limited: the advisory's own sample shows hosting from s3.typebot.io, which means script execution is likely on the storage origin rather than app.typebot.io. Unless self-hosting choices collapse that boundary, the attacker gets less than a normal same-origin stored XSS.

Why not higher?

It is not higher because the chain stacks three big frictions: authenticated access, victim click/open, and likely origin separation. Those are compounding brakes, not minor caveats. A same-origin admin-panel stored XSS with unauthenticated planting or automatic trigger behavior would be a different conversation.

Why not lower?

It is not lower because the bug is persistent, trivially reproducible, and weaponizable on a trusted vendor domain. If your deployment serves uploads from the same origin, uses broad cookie domains, or exposes builder access to many semi-trusted users, impact can jump from annoyance to real credential or session abuse.

05 · Compensating Control

What to do — in priority order.

  1. Block SVG avatar uploads — Enforce MIME and extension restrictions for profile/avatar uploads so only non-active image types like PNG/JPEG are accepted. If you cannot patch immediately, deploy this as your first compensating control; for a MEDIUM verdict there is no mitigation SLA, but it is still the cleanest temporary risk reduction.
  2. Force downloads for uploaded SVG — Serve any existing .svg objects with Content-Disposition: attachment and a non-executable content type where your stack allows it. This reduces script-capable rendering while you move to 3.16.0 within the remediation window.
  3. Separate auth and file origins — Keep uploaded content on an origin that does not share cookies, local storage, or parent-domain auth scope with the builder app. This matters most on self-hosted deployments and should be validated before your 365-day remediation deadline.
  4. Audit builder-account sprawl — Review who can create or edit workspaces and who can upload profile assets. Because exploitation starts with a valid account, pruning stale or over-privileged builder users meaningfully lowers exposure while you schedule the patch.
  5. Hunt for existing malicious SVGs — Search your object store or upload directory for .svg avatars and unusual XML/script content. Removing already-planted payloads limits persistence even if some nodes remain unpatched during the remediation window.
What doesn't work
  • A generic network IPS does not solve this because the dangerous payload is a valid file upload and later a normal HTTPS object fetch.
  • Relying on perimeter internet-exposure scans does not solve this because the exploitable step is behind authentication and user workflow, not a naked bannered service.
  • Assuming HttpOnly alone makes it harmless does not solve this because attackers can still phish, abuse DOM APIs on the file origin, or exploit weak self-hosted origin/cookie layouts.
06 · Verification

Crowdsourced verification payload.

Run this on the Typebot host or inside the Typebot container where the application files are present. Invoke it as sudo bash check_cve_2026_39970.sh /opt/typebot or docker exec -it <typebot-container> bash /tmp/check_cve_2026_39970.sh /app; it needs read access to package.json only.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_39970.sh
# Determine whether a local Typebot installation is affected by CVE-2026-39970.
# Usage: bash check_cve_2026_39970.sh /path/to/typebot
# Exit codes:
#   0 = PATCHED
#   1 = VULNERABLE
#   2 = UNKNOWN / error

set -euo pipefail

TARGET_DIR="${1:-}"
if [[ -z "$TARGET_DIR" ]]; then
  echo "UNKNOWN: missing path argument"
  exit 2
fi

PKG="$TARGET_DIR/package.json"
if [[ ! -f "$PKG" ]]; then
  echo "UNKNOWN: package.json not found at $PKG"
  exit 2
fi

if ! command -v python3 >/dev/null 2>&1; then
  echo "UNKNOWN: python3 is required for version comparison"
  exit 2
fi

VERSION=$(python3 - <<'PY' "$PKG"
import json, sys
p = sys.argv[1]
try:
    with open(p, 'r', encoding='utf-8') as f:
        data = json.load(f)
    v = data.get('version')
    if not v:
        print('')
    else:
        print(str(v).strip())
except Exception:
    print('')
PY
)

if [[ -z "$VERSION" ]]; then
  echo "UNKNOWN: could not read version from $PKG"
  exit 2
fi

python3 - <<'PY' "$VERSION"
import re, sys
from itertools import zip_longest

version = sys.argv[1].strip()

# Normalize strings like v3.15.2, 3.15.2-beta, etc.
def normalize(v):
    v = v.lstrip('vV')
    main = re.split(r'[-+]', v, 1)[0]
    parts = [int(x) for x in main.split('.') if x.isdigit()]
    return parts

def cmp(a, b):
    for x, y in zip_longest(a, b, fillvalue=0):
        if x < y:
            return -1
        if x > y:
            return 1
    return 0

cur = normalize(version)
if not cur:
    print('UNKNOWN: unparseable version')
    sys.exit(2)

vuln_max = normalize('3.15.2')
patched_min = normalize('3.16.0')

if cmp(cur, vuln_max) <= 0:
    print(f'VULNERABLE: Typebot {version} <= 3.15.2')
    sys.exit(1)
elif cmp(cur, patched_min) >= 0:
    print(f'PATCHED: Typebot {version} >= 3.16.0')
    sys.exit(0)
else:
    print(f'UNKNOWN: Typebot {version} falls in an unexpected comparison state')
    sys.exit(2)
PY
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: identify every self-hosted Typebot instance at <= 3.15.2, verify whether uploads are served from a separate origin, and restrict or strip SVG avatar uploads anywhere untrusted users can get builder accounts. For a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window; plan the actual upgrade to 3.16.0 within the noisgate remediation SLA of 365 days, but pull that forward if your deployment serves uploads same-origin or shares cookies across the file and app domains.

Sources

  1. GitHub Security Advisory GHSA-jj87-c343-26vp
  2. Typebot release v3.16.0
  3. NVD CVE-2026-39970
  4. Typebot repository README
  5. Typebot docs overview
  6. Typebot settings docs
  7. CISA Known Exploited Vulnerabilities Catalog
  8. FIRST EPSS data documentation
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.