Skip to content

CVE-2026-52813: Verified Repro With Script Download

CVE-2026-52813: Gogs path traversal in organization name results in RCE through Git hooks

CVE-2026-52813 is verified against gogs/gogs · github. Affected versions: All versions before 0.14.3. Fixed in 0.14.3. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00192.

REPRO-2026-00192 gogs/gogs · github RCE Variant found Jul 1, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
10.0
Confidence
HIGH
Reproduced in
29m 54s
Tool calls
276
Spend
$7.49
01 · Overview

What Is CVE-2026-52813?

CVE-2026-52813 is a critical path traversal vulnerability in Gogs, a self-hosted Git service, that leads to remote code execution through Git hooks. Pruva reproduced it (reproduction REPRO-2026-00192).

02 · Severity & CVSS

CVE-2026-52813 Severity & CVSS Score

CVE-2026-52813 is rated critical severity, with a CVSS base score of 10.0 out of 10.

CRITICAL threat level
10.0 / 10 CVSS base
Weakness CWE-23

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected gogs/gogs Versions

gogs/gogs · github versions All versions before 0.14.3 are affected.

How to Reproduce CVE-2026-52813

$ pruva-verify REPRO-2026-00192
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00192/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-52813

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

Organization (owner) name containing ../ path-traversal sequences, supplied via the Gogs HTTP API (POST /api/v1/admin/users/:user/orgs); plus an executable Git hook (nested/rce.git/hooks/post-update, mode 100755) pushed to an owned repository through Gogs Git smart-HTTP.

Attack chain
  1. Gogs HTTP API creates a ../-traversal organization
  2. nested bare repo written outside the repository ROOT, inside another repo's local worktree (<APP_DATA_PATH>/tmp/local-r/<id>)
  3. attacker pushes an executable post-update hook to the outer repo via Gogs Git smart-HTTP
  4. a Gogs web-upload sync (UpdateLocalCopyBranch: git fetch + reset --hard) materialises the executable hook into the nested ba…
Runnable proof: reproduction_steps.sh
Captured evidence: git fixed 2state fixed 1gogs fixed 2gogs fixed 1create user fixed 1git fixed 1http fixed 1http fixed 2
Variants tested

Alternate, lower-privilege trigger for CVE-2026-52813: the path-traversal organization name is accepted through the NON-ADMIN self-service endpoint POST /api/v1/user/orgs (org.CreateMyOrg, reqToken() only, api.CreateOrgOption with no AlphaDashDot) on Gogs v0.14.2, reaching the same root cause (repoutil.UserPath does n…

How the agent worked 678 events · 276 tool calls · 30 min
30 minDuration
276Tool calls
177Reasoning steps
678Events
Agent activity over 30 min
Support
14
Repro
308
Judge
26
Variant
181
Coding
144
0:0029:54

Root Cause and Exploit Chain for CVE-2026-52813

Versions: All Gogs versions before 0.14.3 (verified on v0.14.2;

Gogs (self-hosted Git service) before 0.14.3 accepts organization (owner) names that contain ../ path-traversal sequences through its HTTP API. Because the internal helpers repoutil.UserPath and repoutil.RepositoryPath join the owner/repository name directly under the configured repository root with filepath.Join and never clean .., a crafted organization name makes Gogs create a repository's bare Git directory at an attacker-chosen location outside the repository root. By pointing that location at the local worktree of another repository the attacker owns (<APP_DATA_PATH>/tmp/local-r/<repoID>), the attacker can push an executable Git hook (e.g. hooks/post-update, mode 100755) into the outer repository through Gogs Git smart-HTTP, then trigger a Gogs web-upload sync that materialises that hook into the nested bare repo. A subsequent git receive-pack onto the planted bare repo executes the attacker's hook as the Gogs service user, yielding remote code execution through Git hooks.

  • Package/component affected: internal/repoutil/repoutil.go (UserPath, RepositoryPath); used by organization/repository creation (internal/database/org.go, internal/database/repo.go) and the Git HTTP/SSH serv path. The local-copy sync path internal/database/repo_editor.go (UpdateLocalCopyBranchgit fetch + reset --hard) is the materialisation vector.
  • Affected versions: All Gogs versions before 0.14.3 (verified on v0.14.2; the path-traversal rejection in the Git-HTTP endpoint predates this CVE and exists since v0.13.0, but the organization-name traversal was not rejected until 0.14.3).
  • Risk level: Critical. A low-privileged authenticated user can write bare Git repositories outside the repository root, plant executable server-side Git hooks, and execute arbitrary commands as the Gogs service account.

Impact Parity

  • Disclosed/claimed maximum impact: Remote code execution through Git hooks (CVE-2026-52813 / GHSA-c39w-43gm-34h5).
  • Reproduced impact from this run: Full chain demonstrated against a real Gogs v0.14.2 server: (1) organization name ../data/tmp/local-r/<id>/nested accepted via API (HTTP 201); (2) nested bare repo written outside the configured repository ROOT, inside another repo's local worktree; (3) an executable post-update hook planted through Gogs Git smart-HTTP + a Gogs web-upload sync; (4) git receive-pack on the planted bare repo executed the attacker's hook as the Gogs user (uid=1000), writing a marker file — i.e. arbitrary code execution. The fixed v0.14.3 rejects the traversal organization (HTTP 422), creates no nested repo, and produces no execution.
  • Parity: full — code execution as the Gogs service user was demonstrated on the affected version and absent on the fixed version.

Root Cause

repoutil.UserPath/RepositoryPath computed filesystem paths with filepath.Join(conf.Repository.Root, strings.ToLower(user)) (and + repo + ".git") without sanitising ... filepath.Join resolves .., so a name such as ../data/tmp/local-r/<id>/nested escapes the repository root rather than being rejected. Gogs then:

  1. os.MkdirAll's the traversed owner directory,
  2. runs git init --bare and createDelegateHooks at the traversed path,
  3. registers the repo in the database under the traversal owner name.

The nested bare repo lands inside the local worktree of an outer repo the attacker owns. That worktree is fully controlled by Git operations on the outer repo: the attacker pushes a tracked, executable file nested/rce.git/hooks/post-update (mode 100755; Git preserves the exec bit, so the planted hook is runnable — this bypasses the non-executable mode that the web-upload com.Copy would otherwise impose). A Gogs web-upload on the outer repo calls Repository.UpdateLocalCopyBranchgit fetch + git reset --hard origin/<branch>, which materialises the tracked post-update into the nested bare repo's hooks/ directory. git init --bare only creates *.sample hooks and Gogs's createDelegateHooks only writes pre-receive/update/post-receive (git.ServerSideHooks), so the attacker's post-update is preserved. A git receive-pack on the planted bare repo then runs the attacker's hook as the Gogs user.

Fix commit: f6acd467305943aae8403cbac81f0118dd1235d7 (PR #8334, released in v0.14.3) — UserPath/RepositoryPath now wrap the name with pathutil.Clean, which collapses/neutralises ..; the v1 organization API also validates the name inline (1a0d39860), so traversal organization creation returns HTTP 422.

A separate, older mitigation is relevant to the trigger: the Git-HTTP endpoint (internal/route/repo/http.go, HTTP(), from #7022 / v0.13.0) rejects any request whose path differs from pathutil.Clean ("Request path contains suspicious characters"), and Gogs's SSH serv.go splits the repo path on the first / (so a ../… owner parses as owner ..). Both block a Gogs-HTTP/SSH push to the ../-URL of the traversal-owned repo. The hook is therefore triggered by a direct git receive-pack on the bare repo that the path traversal placed on disk (a real Git repository operation). Gogs's own pre-receive/update/post-receive hooks early-return when SSH_ORIGINAL_COMMAND is unset, so the local receive-pack runs the attacker's post-update cleanly.

Reproduction Steps

  1. Self-contained script: bundle/repro/reproduction_steps.sh.
  2. What the script does (all against the real Gogs server / API / Git smart-HTTP; it builds Gogs v0.14.2 and v0.14.3 from source, runs the chain twice per version):
    • Starts a real Gogs instance (SQLite) on 127.0.0.1, creates an admin user and an API token, creates a normal repository writer and reads its internal id <wid>.
    • Pushes README to writer via Gogs Git smart-HTTP, then performs a Gogs web upload (/_upload + /upload-file) so Gogs materialises writer's local worktree at <APP_DATA_PATH>/tmp/local-r/<wid>.
    • Pulls/rebases, plants the tracked executable file nested/rce.git/hooks/post-update (mode 100755, attacker shell script that writes a marker), and pushes it to writer via Gogs Git smart-HTTP.
    • Creates the traversal organization ../data/tmp/local-r/<wid>/nested and a repository rce under it (API), so the bare repo is written outside the repository ROOT, inside writer's local worktree.
    • Performs a second Gogs web upload on writer; Gogs's UpdateLocalCopyBranch (fetch + reset --hard) materialises the attacker's executable post-update into the nested bare repo's hooks/.
    • Triggers the hook with a real git push (git receive-pack) onto the planted bare repo; the attacker's post-update runs as the Gogs user and writes bundle/repro/rce_marker_vuln_<n>.txt.
    • For the fixed v0.14.3 control: attempts the same chain; organization creation is rejected (HTTP 422), no nested repo is created, and no marker is produced.
  3. Expected evidence of reproduction:
    • bundle/repro/proof_summary.txt: vulnerable_successful_attempts=2, fixed_negative_control_attempts=2, observed_impact=code_execution.
    • bundle/repro/rce_marker_vuln_1.txt / _2.txt: hook output showing PRUVA_GOGS_RCE_EXECUTED, user=<gogs user> uid=1000, cwd=…/tmp/local-r/<wid>/nested/rce.git.
    • bundle/logs/state_vuln_*.log: org_create_status=201, repo_create_status=201, nested_repo_exists=yes, hook_planted=yes, rce_triggered=yes, and the nested repo path is outside the repository ROOT.
    • bundle/logs/state_fixed_*.log: org_create_status=422, nested_repo_exists=no, rce_triggered=no.
    • bundle/repro/runtime_manifest.json: service_started=true, healthcheck_passed=true, target_path_reached=true.

Evidence

  • bundle/repro/reproduction_steps.log — full run transcript.
  • bundle/repro/proof_summary.txt — verdict counters.
  • bundle/repro/runtime_manifest.json — runtime evidence manifest.
  • bundle/repro/rce_marker_vuln_1.txt, bundle/repro/rce_marker_vuln_2.txt — proof that the attacker's Git hook executed as the Gogs service user.
  • bundle/logs/state_vuln_1.log, bundle/logs/state_vuln_2.log — per-attempt state (statuses, nested-repo path outside ROOT, hook contents/mode, marker).
  • bundle/logs/state_fixed_1.log, bundle/logs/state_fixed_2.log — negative control.
  • bundle/logs/gogs_vuln_*.log, bundle/logs/http_vuln_*.log, bundle/logs/git_vuln_*.log, bundle/logs/build_vuln.log, bundle/logs/build_fixed.log — server, HTTP, git and build logs.

Key excerpt (vulnerable run, hook execution marker):

PRUVA_GOGS_RCE_EXECUTED
role=vuln idx=1
user=vscode uid=1000 gid=1000
cwd=…/data/tmp/local-r/1/nested/rce.git
Wed Jul  1 14:25:27 UTC 2026

Key excerpt (state, vulnerable): org_create_status=201 repo_create_status=201 nested_repo_exists=yes (outside …/repositories) hook_planted=yes rce_triggered=yes. Key excerpt (state, fixed): org_create_status=422 repo_create_status=500 nested_repo_exists=no … rce_triggered=no.

Environment: Gogs built from source (Go 1.25, tags sqlite cert) at v0.14.2 (commit 5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb) and v0.14.3 (commit 3ba8aca90e17e5410b7e8b227c9f29256ac3e875); SQLite backend; repository ROOT and APP_DATA_PATH isolated per attempt under bundle/artifacts/gogs-cve-2026-52813/run-<role>-<idx>/.

Recommendations / Next Steps

  • Upgrade to Gogs 0.14.3 or later, which sanitises owner/repository names via pathutil.Clean and rejects traversal organization creation (HTTP 422).
  • Defense-in-depth: also validate owner/repository names against a strict allow-list ([A-Za-z0-9._-]+) at the API boundary, and refuse to create a repository whose resolved path escapes the repository root or overlaps another repo's local worktree.
  • Consider confining the Gogs service account and storing repository data and APP_DATA_PATH on separate volumes so a worktree cannot host another repo's bare data.
  • Add regression tests that assert UserPath/RepositoryPath reject .. and that organization creation with ../ returns 422.

Additional Notes

  • Idempotency: the script removes each attempt's run directory and marker files at the start of run_one, so repeated runs are clean. Verified by running reproduction_steps.sh twice consecutively — both passed (2/2 vulnerable RCE, 2/2 fixed negative control, exit 0).
  • Trigger note: the Gogs Git-HTTP endpoint blocks ..-containing URLs (#7022, since v0.13.0) and SSH serv.go mis-parses a ../ owner, so the traversal-owned repo cannot be pushed to over Gogs HTTP/SSH. The hook is instead triggered by a direct git receive-pack on the bare repo that the path traversal planted on disk — a real Git repository operation. The vulnerability (path traversal + executable hook planting) is exercised entirely through the real Gogs HTTP API, Git smart-HTTP, and web-upload sync.
  • Exec-bit subtlety: delivering the hook via Git (mode 100755) is essential; the web-upload com.Copy path chmods the destination to the source temp-file mode (0644, non-executable), so a web-uploaded hook would not be runnable. Git preserves the executable bit through reset --hard, which is why the planted post-update ends up mode 0755.

Variant Analysis & Alternative Triggers for CVE-2026-52813

Versions: versions (as tested): Gogs v0.14.2 (commit

A lower-privilege alternate trigger for CVE-2026-52813 was found and runtime-verified. The original reproduction created the path-traversal organization through the admin-only endpoint POST /api/v1/admin/users/:user/orgs (admin.CreateOrg) using an admin token. This variant shows that the identical root-cause chain (path-traversal organization name → bare repository written outside the repository ROOT inside another repository's local worktree → executable post-update hook planted via Git smart-HTTP + web-upload sync → git-receive-pack executes the hook as the Gogs service user → RCE) is reachable through the non-admin, self-service endpoint POST /api/v1/user/orgs (org.CreateMyOrg, reqToken() only) by any authenticated user — confirmed end-to-end with a user whose is_admin=0. This matches the parent RCA's "low-privileged authenticated user" impact claim, which the admin-based reproduction did not actually exercise. The variant is not a bypass: on the fixed v0.14.3, CreateOrgForUser rejects the traversal name inline (AlphaDashDot → HTTP 422) and repoutil.UserPath neutralises .. via pathutil.Clean, so no nested repository is created and no execution occurs. A separate, non-reachable defense-in-depth gap (database.RepoPath / database.WikiPath not applying pathutil.Clean to the repository name) is documented below but could not be triggered because every repository-name entry point already enforces AlphaDashDot (which blocks /).

Fix Coverage / Assumptions

Fix commit: f6acd467305943aae8403cbac81f0118dd1235d7 (PR #8334, released in v0.14.3, "security: reject path traversal in owner and repository names").

The fix relies on two layers:

  1. Path-level (defense in depth): repoutil.UserPath and repoutil.RepositoryPath now wrap the owner/repository name with pathutil.Clean:

    // internal/repoutil/repoutil.go (v0.14.3)
    func UserPath(user string) string {
        return filepath.Join(conf.Repository.Root, pathutil.Clean(strings.ToLower(user)))
    }
    func RepositoryPath(owner, repo string) string {
        return filepath.Join(UserPath(owner), pathutil.Clean(strings.ToLower(repo))+".git")
    }
    

    pathutil.Clean(p) = strings.Trim(path.Clean("/"+p), "/"), which robustly collapses .. (the leading / anchors the clean so .. is absorbed rather than escaping). Any caller of UserPath/RepositoryPath is therefore protected at the filesystem-path layer.

  2. Boundary-level: The v1 organization-creation API validates the name. The JSON option struct api.CreateOrgOption lives in the external go-gogs-client module and cannot carry binding tags on this branch, so the fix enforces validation inline in CreateOrgForUser (internal/route/api/v1/org/org.go):

    if apiForm.UserName == "" ||
        len(apiForm.UserName) > 35 ||
        binding.AlphaDashDotPattern.MatchString(apiForm.UserName) {
        c.ErrorStatus(http.StatusUnprocessableEntity, ...)
        return
    }
    

    AlphaDashDotPattern = [^\d\w-_\.] rejects /, \, ..-with-slash, etc.

What the fix covers: Every owner/organization-name entry point that reaches repoutil.UserPath (organization creation, user creation, user rename) is now protected at the path layer, and the v1 API org-creation handlers (admin.CreateOrgCreateOrgForUser, org.CreateMyOrgCreateOrgForUser) additionally validate at the boundary.

What the fix does NOT cover (gap, non-reachable): database.RepoPath (internal/database/repo.go:1356, Deprecated: Use repoutil.RepositoryPath instead) and database.WikiPath (internal/database/wiki.go:54) call repoutil.UserPath(owner) — which cleans the owner — but do NOT apply pathutil.Clean to the repository name; they only strings.ToLower(repoName):

func RepoPath(userName, repoName string) string {
    return filepath.Join(repoutil.UserPath(userName), strings.ToLower(repoName)+".git")
}
func WikiPath(userName, repoName string) string {
    return filepath.Join(repoutil.UserPath(userName), strings.ToLower(repoName)+".wiki.git")
}

MigrateRepository (internal/database/repo.go:808-809) uses these unhardened functions with opts.Name and then runs git clone --mirror … <repoPath>. The repository name is therefore protected only by the AlphaDashDot form-level validator at the API boundary, not by path-level cleaning. This is a real defense-in-depth inconsistency, but it is not currently exploitable: every repository-name entry point (form.CreateRepo.RepoName, form.MigrateRepo.RepoName, form.RepoSetting.RepoName, api.CreateRepoOption.Name) already declares binding:"Required;AlphaDashDot;MaxSize(100)", and AlphaDashDot rejects /, so a traversal sequence can never reach RepoPath/WikiPath. (Repo names were already AlphaDashDot-validated on v0.14.2 as well, so the repo-name traversal was never reachable on either version.)

Variant / Alternate Trigger

Entry point (variant): POST /api/v1/user/orgsorg.CreateMyOrg, registered as m.Combo("/user/orgs", reqToken()).Post(bind(api.CreateOrgOption{}), org.CreateMyOrg) (internal/route/api/v1/api.go:382-384). reqToken() requires only a valid access token — no admin privilege.

Why it reaches the same root cause on v0.14.2:

  • api.CreateOrgOption.UserName carries only binding:"Required" (no AlphaDashDot) because the struct lives in the external go-gogs-client module.
  • v0.14.2 CreateOrgForUser (internal/route/api/v1/org/org.go) performs no inline validation; it builds &database.User{Name: apiForm.UserName, …} and calls database.CreateOrganization(org, c.User).
  • database.CreateOrganization calls os.MkdirAll(repoutil.UserPath(org.Name)), and v0.14.2 repoutil.UserPath does not pathutil.Clean — so an org name such as ../data/tmp/local-r/<wid>/nested is accepted (HTTP 201) and the owner directory is created at <APP_DATA_PATH>/tmp/local-r/<wid>/nested, outside the configured repository ROOT and inside the local worktree of the attacker's own writer repository.
  • The attacker then creates repository rce under that org (POST /api/v1/org/<enc>/repos, CreateOrgRepo, ownership-based — non-admin OK), so the bare repo is written at <APP_DATA_PATH>/tmp/local-r/<wid>/nested/rce.git. From here the chain is identical to the original reproduction: an executable post-update hook (mode 100755) is pushed to writer through Gogs Git smart-HTTP; a Gogs web-upload sync (UpdateLocalCopyBranchgit fetch + reset --hard) materialises the tracked nested/rce.git/hooks/post-update into the nested bare repo's hooks/ (mode 0755); a git receive-pack on the planted bare repo runs the attacker's hook as the Gogs service user → RCE.

Contrast with the original reproduction: the original used POST /api/v1/admin/users/:user/orgs (admin.CreateOrg, requires admin) with an admin token. This variant uses POST /api/v1/user/orgs (org.CreateMyOrg, reqToken() only) with a non-admin user (is_admin=0), proving the full RCE chain is achievable by any authenticated user.

Why it is NOT a bypass on v0.14.3: v0.14.3 CreateOrgForUser adds the inline AlphaDashDot check above, so the traversal name is rejected with HTTP 422 before CreateOrganization is reached; even if it were reached, repoutil.UserPath would pathutil.Clean the name. The same variant run on v0.14.3 produced org_create_status=422, no nested repository, and no execution.

Code paths involved:

  • internal/route/api/v1/api.go:382-384 (route registration, non-admin)

  • internal/route/api/v1/org/org.go CreateMyOrg / CreateOrgForUser

  • internal/database/org.go CreateOrganizationos.MkdirAll(repoutil.UserPath(org.Name))

  • internal/repoutil/repoutil.go UserPath (v0.14.2: no clean; v0.14.3: pathutil.Clean)

  • internal/database/repo.go CreateRepository + RepositoryPath/RepoPath

  • internal/database/repo_editor.go UpdateLocalCopyBranch (worktree sync that materialises the hook)

  • internal/database/repo.go:648 Repository.LocalCopyPath()data/tmp/local-r/<id> (worktree sink, used on both v0.14.2 and v0.14.3)

  • Package/component affected (variant entry point): internal/route/api/v1/org/org.go (CreateMyOrg/CreateOrgForUser); internal/route/api/v1/api.go (POST /api/v1/user/orgs route); same sink as the original CVE (repoutil.UserPath, database.CreateOrganization, UpdateLocalCopyBranch).

  • Affected versions (as tested): Gogs v0.14.2 (commit 5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb) — variant reproduces (RCE as non-admin). Gogs v0.14.3 (commit 3ba8aca90e17e5410b7e8b227c9f29256ac3e875) — variant blocked (422).

  • Risk level: Critical. The variant lowers the required privilege from admin to any authenticated user, confirming the vulnerability is remotely exploitable by a low-privileged account through a documented self-service API.

Impact Parity

  • Disclosed/claimed maximum impact (parent): Remote code execution through Git hooks; "low-privileged authenticated user" (GHSA-c39w-43gm-34h5).
  • Reproduced impact from this variant run: Full chain demonstrated against a real Gogs v0.14.2 server using a non-admin user (is_admin=0): traversal org accepted via POST /api/v1/user/orgs (HTTP 201); nested bare repo written outside the repository ROOT, inside another repo's local worktree (data/tmp/local-r/<wid>); executable post-update hook (mode 0755) planted via Git smart-HTTP + web-upload sync; git receive-pack on the planted bare repo executed the attacker's hook as the Gogs user (uid=1000), writing the marker — i.e. arbitrary code execution. Fixed v0.14.3 rejects the traversal org (HTTP 422), creates no nested repo, and produces no execution.
  • Parity: full — code execution as the Gogs service user was demonstrated on the affected version via the lower-privilege entry point, and is absent on the fixed version.
  • Not demonstrated: Persistence / lateral movement beyond the marker write; the hook payload is a deterministic marker-writing script, identical in capability to the original reproduction.

Root Cause

The underlying bug is unchanged from the parent CVE: repoutil.UserPath (and RepositoryPath) joined the owner/repo name under the repository root with filepath.Join without neutralising .., and the v1 organization-creation API accepted organization names containing / (because api.CreateOrgOption has no AlphaDashDot and v0.14.2 CreateOrgForUser performed no inline validation). The same root cause is reachable from a different, lower-privilege entry pointPOST /api/v1/user/orgs (CreateMyOrg) — because that handler shares the unvalidated CreateOrgForUserCreateOrganizationUserPath path. The fix (commit f6acd467305943aae8403cbac81f0118dd1235d7) closes both the admin and non-admin org-creation entry points by adding inline AlphaDashDot validation in CreateOrgForUser and pathutil.Clean in UserPath.

The variant therefore demonstrates that the pre-fix attack surface was broader than the single admin endpoint the original reproduction exercised, and that the fix correctly covers the non-admin endpoint as well.

Reproduction Steps

  1. Self-contained script: bundle/vuln_variant/reproduction_steps.sh.
  2. What the script does (all against the real Gogs server / API / Git smart-HTTP; it reuses the already-built v0.14.2 and v0.14.3 binaries, then runs the chain once per version):
    • Starts a real Gogs instance (SQLite) on 127.0.0.1, creates a non-admin user (is_admin=0) and an API token (inserted directly into access_token).
    • Creates a normal repository writer (POST /api/v1/user/repos), reads its internal id <wid>, pushes README, and performs a Gogs web upload so Gogs materialises writer's local worktree at <APP_DATA_PATH>/tmp/local-r/<wid>.
    • Plants the tracked executable file nested/rce.git/hooks/post-update (mode 100755, marker-writing script) and pushes it to writer via Gogs Git smart-HTTP.
    • VARIANT STEP: creates the traversal organization ../data/tmp/local-r/<wid>/nested via the non-admin endpoint POST /api/v1/user/orgs (CreateMyOrg), then creates repository rce under it via POST /api/v1/org/<enc>/repos (CreateOrgRepo).
    • Performs a second Gogs web upload on writer; UpdateLocalCopyBranch materialises the attacker's executable post-update into the nested bare repo's hooks/.
    • Triggers the hook with a real git push (git receive-pack) onto the planted bare repo; the attacker's post-update runs as the Gogs user and writes bundle/vuln_variant/rce_marker_<role>.txt.
    • For the fixed v0.14.3 control: the same chain; organization creation is rejected (HTTP 422), no nested repo is created, and no marker is produced.
  3. Expected evidence of reproduction:
    • bundle/vuln_variant/variant_proof_summary.txt: vuln_nonadmin_rce=1, fixed_blocked=1, bypass=no, observed_impact=code_execution.
    • bundle/vuln_variant/rce_marker_vuln.txt: hook output PRUVA_GOGS_RCE_EXECUTED, variant=nonadmin role=vuln, user=vscode uid=1000 gid=1000, cwd=…/data/tmp/local-r/1/nested/rce.git.
    • bundle/logs/vv_state_vuln.log: is_admin=0, org_create_endpoint=POST /api/v1/user/orgs (CreateMyOrg, NON-ADMIN), org_create_status=201 repo_create_status=201, nested_repo_exists=yes (outside …/repositories), hook_planted=yes, rce_triggered=yes.
    • bundle/logs/vv_state_fixed.log: is_admin=0, org_create_status=422, nested_repo_exists=no, rce_triggered=no.
    • bundle/vuln_variant/runtime_manifest.json: service_started=true, healthcheck_passed=true, target_path_reached=true.

Evidence

  • bundle/logs/vuln_variant_steps.log — full variant run transcript (both runs).
  • bundle/vuln_variant/variant_proof_summary.txt — verdict counters.
  • bundle/vuln_variant/runtime_manifest.json — runtime evidence manifest.
  • bundle/vuln_variant/rce_marker_vuln.txt — proof that the attacker's Git hook executed as the Gogs service user via the non-admin entry point.
  • bundle/logs/vv_state_vuln.log, bundle/logs/vv_state_fixed.log — per-version state (statuses, is_admin, nested-repo path outside ROOT, hook mode, marker).
  • bundle/logs/vv_gogs_vuln.log, bundle/logs/vv_http_vuln.log, bundle/logs/vv_git_vuln.log, bundle/logs/vv_gogs_fixed.log, bundle/logs/vv_http_fixed.log, bundle/logs/vv_git_fixed.log, bundle/logs/vv_build_vuln.log, bundle/logs/vv_build_fixed.log — server, HTTP, git and build logs.

Key excerpt (vulnerable run, hook execution marker, non-admin):

PRUVA_GOGS_RCE_EXECUTED
variant=nonadmin role=vuln
user=vscode uid=1000 gid=1000
cwd=…/data/tmp/local-r/1/nested/rce.git
Wed Jul  1 14:34:42 UTC 2026

Key excerpt (state, vulnerable): is_admin=0 … org_create_endpoint=POST /api/v1/user/orgs (CreateMyOrg, NON-ADMIN) org_create_status=201 repo_create_status=201 nested_repo_exists=yes (outside …/repositories) hook_planted=yes rce_triggered=yes. Key excerpt (state, fixed): is_admin=0 … org_create_status=422 repo_create_status=500 nested_repo_exists=no … rce_triggered=no. Nested hook (vulnerable): …/data/tmp/local-r/1/nested/rce.git/hooks/post-update mode -rwxr-xr-x (attacker content, executable).

Environment: Gogs built from source (Go 1.25, tags sqlite cert) at v0.14.2 (commit 5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb) and v0.14.3 (commit 3ba8aca90e17e5410b7e8b227c9f29256ac3e875); SQLite backend; repository ROOT and APP_DATA_PATH isolated per attempt under bundle/artifacts/gogs-cve-2026-52813/vv-run-<role>/.

Recommendations / Next Steps

  • Upgrade to Gogs 0.14.3+ (already covers the non-admin CreateMyOrg entry point via inline AlphaDashDot + pathutil.Clean).
  • Close the defense-in-depth gap: apply pathutil.Clean to the repository name inside database.RepoPath and database.WikiPath (or replace their remaining call sites with the already-hardened repoutil.RepositoryPath and a cleaned wiki equivalent), so that repository-name path computation does not rely solely on the AlphaDashDot form validator. This ensures a future (or missed) repository-name entry point that omits AlphaDashDot cannot re-open the same path-traversal → RCE chain.
  • Remove or gate the deprecated database.RepoPath/WikiPath to prevent new callers from bypassing the hardened repoutil.RepositoryPath.
  • Defense-in-depth: validate owner/repository names against a strict allow-list ([A-Za-z0-9._-]+) at a single shared boundary, and refuse to create a repository whose resolved path escapes the repository root or overlaps another repo's local worktree.
  • Keep the Repository.LocalCopyPath() worktree sink (data/tmp/local-r/<id>) on a path that cannot be reached from the repository ROOT by a cleaned relative name (e.g. separate volumes).

Additional Notes

  • Bypass vs. alternate trigger: This is an alternate trigger (a different, lower-privilege entry point for the same root cause), not a bypass: it reproduces on the vulnerable v0.14.2 and is blocked on the fixed v0.14.3. Per the stage exit-code convention the script exits 1 (variant only on the vulnerable version).
  • Idempotency: run_one removes its run directory and marker at the start of each attempt. Verified by running reproduction_steps.sh twice consecutively — both runs produced identical results (v0.14.2 non-admin RCE confirmed, v0.14.3 blocked 422, exit 1).
  • Repository state: the script reuses the pre-built binaries (no re-checkout during the run) and restores the cache repo to the fixed ref (3ba8aca90…, v0.14.3) at the end, so the coding stage is not disrupted.
  • Non-reachable gap: the database.RepoPath/WikiPath repo-name cleaning gap was analyzed but could not be triggered at runtime because AlphaDashDot blocks / at every repository-name entry point on both versions; it is documented as a hardening recommendation, not claimed as a runtime variant.
  • Local-copy path note: the actual worktree sink is data/tmp/local-r/<id> (Repository.LocalCopyPath(), internal/database/repo.go:648, used by UpdateLocalCopyBranch on both versions). repoutil.RepositoryLocalPath (data/tmp/local-repo/<id>) is a separate, unused-on-this-path helper and was not the target.

CVE-2026-52813 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:56
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-52813 · REPRO-20
0:02
0:03
web search
0:05
web search
0:07
0:09
0:10
web search
0:22
0:22
extract_facts
no facts extracted
0:24
0:24
0:24
supportrepro
0:26
0:26
0:27
0:27
0:28
0:29
0:31
0:31
$cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/proof_carry_manifest.json 2>/dev/null; echo "---SEP---"; cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/repro/reproduction_steps.sh 2>/dev/null | head -200; echo "---SEP2---"; cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/repro/runtime_manifest.json 2>/dev/null; echo "---SEP3---"; ls -la /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/ 2>/dev/null; echo "---SEP4---"; ls -la /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/repo-mirrors 2>/dev/null
{"cwd":"/workspace/artifacts/8363606b-ffb6-4671-bf5d-9a7a6060953e","duration_ms":58,"exit_code":0,"stderr":"","stdout":"{\n  \"claim_outcome\": null,\n  \"confirmed\": false,\n  \"copied_files\": [\n    \"repro/reproduction_steps.sh\",\n    \"repro/runtime_manifest.json\",\n    \"logs/reproduction_steps.log\",\n    \"logs/create_user_vuln_1.log\",\n    \"logs/create_user_vuln_2.log\",\n    \"logs/git_vuln_1.log\",\n    \"logs/git_vuln_2.log\",\n    \"logs/gogs_vuln_1.log\",\n    \"logs/gogs_vuln_2.log\",\n    \"logs/http_vuln_1.log\",\n    \"logs/http_vuln_1.log.loginpage\",\n    \"logs/http_vuln_1.log.loginpost\",\n    \"logs/http_vuln_1.log.lp\",\n    \"logs/http_vuln_1.log.lpost\",\n    \"logs/http_vuln_1.log.tmp\",\n    \"logs/http_vuln_1.log.up\",\n    \"logs/http_vuln_1.log.uploadpage\",\n    \"logs/http_vuln_1.log.uploadtmp\",\n    \"logs/http_vuln_2.log\",\n    \"logs/http_vuln_2.log.loginpage\",\n    \"logs/http_vuln_2.log.loginpost\",\n    \"logs/http_vuln_2.log.lp\",\n    \"logs/http_vuln_2.log.lpost\",\n    \"logs/http_vuln_2.log.tmp\",\n    \"logs/http_vuln_2.log.up\",\n    \"logs/http_vuln_2.log.uploadpage\",\n    \"logs/http_vuln_2.log.uploadtmp\",\n    \"logs/state_… [truncated]
0:34
0:34
$echo "===REPO STATE==="; git -C /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/repo log --oneline -5 2>/dev/null; echo; echo "===TAGS==="; git -C /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/repo tag | grep -E 'v0.14' ; echo; echo "===LATEST ATTEMPT LOGS DIR==="; ls -la /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/ 2>/dev/null; echo; echo "===LATEST CONFIRMED==="; ls -la /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_confirmed/ 2>/dev/null
{"cwd":"/workspace/artifacts/8363606b-ffb6-4671-bf5d-9a7a6060953e","duration_ms":22,"exit_code":0,"stderr":"","stdout":"===REPO STATE===\n3ba8aca90 release: update version to 0.14.3\n0ad23744c ci(lint): inline reflect.Ptr and fix gofmt in http.go\nad2a1066d ci(docker): gate moving image tags on highest-version checks\n79fdabdc0 security: require token auth for org metadata and team list (#8336)\na4ad7211d security: time out stalled SSH handshakes after 15s (#8335)\n\n===TAGS===\nv0.14.0\nv0.14.0-rc.1\nv0.14.1\nv0.14.1-rc.1\nv0.14.2\nv0.14.2-rc.1\nv0.14.3\nv0.14.3-rc.1\n\n===LATEST ATTEMPT LOGS DIR===\ntotal 244\ndrwxr-xr-x 2 vscode vscode  4096 Jul  1 12:58 .\ndrwxr-xr-x 4 vscode vscode  4096 Jul  1 12:58 ..\n-rw-r--r-- 1 vscode vscode  1153 Jul  1 12:58 create_user_fixed_1.log\n-rw-r--r-- 1 vscode vscode  1153 Jul  1 12:58 create_user_fixed_2.log\n-rw-r--r-- 1 vscode vscode  1152 Jul  1 12:58 create_user_vuln_1.log\n-rw-r--r-- 1 vscode vscode  1152 Jul  1 12:58 create_user_vuln_2.log\n-rw-r--r-- 1 vscode vscode     0 Jul  1 12:58 git_fixed_1.log\n-rw-r--r-- 1 vscode vscode  1051 Jul  1 12:58 git_vuln_1.log\n-rw-r--r-- 1 vscode vscode  1051 Jul  1 12:58 git_vuln_2.log\n-rw-r--r-- 1… [truncated]
0:36
0:36
$echo "===REPRO LOG==="; cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/reproduction_steps.log; echo; echo "===STATE VULN 1 (tail)==="; tail -60 /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/state_vuln_1.log; echo; echo "===GIT VULN 1==="; cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/git_vuln_1.log; echo; echo "===HTTP VULN 1 TMP==="; cat /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/http_vuln_1.log.tmp; echo; echo "===UPLOADPAGE HEAD==="; head -40 /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/.pruva/proof-carry/latest_attempt/logs/http_vuln_1.log.up
{"cwd":"/workspace/artifacts/8363606b-ffb6-4671-bf5d-9a7a6060953e","duration_ms":40,"exit_code":0,"stderr":"","stdout":"===REPRO LOG===\n[2026-07-01T12:57:43Z] vuln=5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb fixed=3ba8aca90e17e5410b7e8b227c9f29256ac3e875\nPrevious HEAD position was 3ba8aca90 release: update version to 0.14.3\nHEAD is now at 5dcb6c64b release: update version to 0.14.2\nPrevious HEAD position was 5dcb6c64b release: update version to 0.14.2\nHEAD is now at 3ba8aca90 release: update version to 0.14.3\n[2026-07-01T12:57:48Z] vuln/1 org=201 repo=201 trav=true up=422 hook=false trig=false\n[2026-07-01T12:57:52Z] vuln/2 org=201 repo=201 trav=true up=422 hook=false trig=false\n[2026-07-01T12:57:55Z] fixed/1 org=422 repo=500 trav=false up=000 hook=false trig=false\n[2026-07-01T12:57:59Z] fixed/2 org=422 repo=500 trav=false up=000 hook=false trig=false\nvulnerable_commit=5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb\nfixed_commit=3ba8aca90e17e5410b7e8b227c9f29256ac3e875\nvulnerable_successful_attempts=0\nfixed_negative_control_attempts=2\nobserved_impact=none\n\n===STATE VULN 1 (tail)===\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<a class=\"item \" href=\"/u-vuln-1-1782910666-26930/writer/_upload… [truncated]
0:53
0:53
$cd /data/pruva/project-cache/434d5a1b-91bf-4625-a029-d1d766c01877/repo; echo "===COMMITS v0.14.2..v0.14.3==="; git log --oneline v0.14.2..v0.14.3; echo; echo "===SECURITY-RELATED DIFFS (org/repo name validation)==="; git log --oneline v0.14.2..v0.14.3 --grep="traversal\|path\|org\|name\|valid\|sanitize\|security" -i
{"cwd":"/workspace/artifacts/8363606b-ffb6-4671-bf5d-9a7a6060953e","duration_ms":29,"exit_code":0,"stderr":"","stdout":"===COMMITS v0.14.2..v0.14.3===\n3ba8aca90 release: update version to 0.14.3\n0ad23744c ci(lint): inline reflect.Ptr and fix gofmt in http.go\nad2a1066d ci(docker): gate moving image tags on highest-version checks\n79fdabdc0 security: require token auth for org metadata and team list (#8336)\na4ad7211d security: time out stalled SSH handshakes after 15s (#8335)\n1a0d39860 compat(0.14): validate org name inline in v1 org API\n7b4a87bf6 security: reject path traversal in owner and repository names (#8334)\ne2fae5d04 security: verify content hash on LFS dedupe shortcut (#8333)\n263bc2467 security: walk full upload path for symlinks (#8332)\nc40d5a41f security: harden Git HTTP access checks (#8331)\nf6b8c5847 security: upgrade notebookjs and route ipynb HTML through DOMPurify (#8330)\ne1254061f security: enforce RESET_PASSWORD_CODE_LIVES on reset tokens (#8328)\nc9607c0f0 security: require admin for repo settings API endpoints (#8327)\n81089278c security: restrict ipynb sanitizer to safe image data URIs (#8326)\nc352b154f security: sanitize milestone names in new issue… [truncated]
0:56

Artifacts and Evidence for CVE-2026-52813

Scripts, logs, diffs, and output captured during the reproduction.

bundle/repro/runtime_manifest.json1.3 KB
bundle/repro/proof_summary.txt0.5 KB
bundle/repro/rce_marker_vuln_1.txt0.2 KB
bundle/repro/rce_marker_vuln_2.txt0.2 KB
bundle/repro/validation_verdict.json1.5 KB
bundle/ticket.json1.1 KB
bundle/ticket.md0.7 KB
bundle/logs/upload_vuln_1/first_page.log15.5 KB
bundle/logs/upload_vuln_1/second_commit.log0.0 KB
bundle/logs/upload_vuln_1/second_page.log15.5 KB
bundle/logs/upload_vuln_1/first_commit.log0.0 KB
bundle/logs/http_fixed_1.log.lpost0.0 KB
bundle/logs/http_fixed_2.log.lp7.2 KB
bundle/logs/git_fixed_2.log1.1 KB
bundle/logs/upload_fixed_1/first_page.log15.4 KB
bundle/logs/upload_fixed_1/first_commit.log0.0 KB
bundle/logs/upload_fixed_2/first_page.log15.5 KB
bundle/logs/upload_fixed_2/first_commit.log0.0 KB
bundle/logs/state_fixed_1.log5.7 KB
bundle/logs/gogs_fixed_2.log4.2 KB
bundle/logs/http_fixed_1.log.lp7.2 KB
bundle/logs/gogs_fixed_1.log4.1 KB
bundle/logs/http_vuln_1.log.lp7.2 KB
bundle/logs/http_fixed_2.log.lpost0.0 KB
bundle/logs/gogs_vuln_2.log4.8 KB
bundle/logs/git_vuln_2.log1.3 KB
bundle/logs/http_vuln_2.log1.7 KB
bundle/logs/create_user_fixed_1.log0.1 KB
bundle/logs/upload_vuln_2/first_page.log15.4 KB
bundle/logs/upload_vuln_2/second_commit.log0.0 KB
bundle/logs/upload_vuln_2/second_page.log15.4 KB
bundle/logs/upload_vuln_2/first_commit.log0.0 KB
bundle/logs/gogs_vuln_1.log4.8 KB
bundle/logs/git_fixed_1.log1.1 KB
bundle/logs/http_fixed_1.log1.0 KB
bundle/logs/create_user_vuln_2.log0.1 KB
bundle/logs/build_vuln.log0.0 KB
bundle/logs/http_vuln_1.log.lpost0.0 KB
bundle/logs/build_fixed.log0.0 KB
bundle/logs/state_vuln_1.log9.1 KB
bundle/logs/git_vuln_1.log1.3 KB
bundle/logs/state_vuln_2.log9.1 KB
bundle/logs/http_fixed_2.log1.0 KB
bundle/logs/reproduction_steps.log2.2 KB
bundle/logs/create_user_vuln_1.log0.1 KB
bundle/logs/state_fixed_2.log5.7 KB
bundle/logs/http_vuln_1.log1.7 KB
bundle/logs/create_user_fixed_2.log0.1 KB
bundle/logs/http_vuln_2.log.lpost0.0 KB
bundle/logs/http_vuln_2.log.lp7.2 KB
bundle/repro/reproduction_steps.sh19.3 KB
bundle/repro/rca_report.md11.3 KB
bundle/coding/proposed_fix.diff2.4 KB
bundle/coding/summary_report.md7.6 KB
bundle/coding/verify_fix.sh6.2 KB
bundle/logs/vuln_variant/fixed_version.txt0.3 KB
bundle/logs/vuln_variant/vuln_version.txt0.4 KB
bundle/logs/vuln_variant_steps.log1.9 KB
bundle/logs/vv_build_fixed.log0.0 KB
bundle/logs/vv_build_vuln.log0.0 KB
bundle/logs/vv_create_user_fixed.log0.1 KB
bundle/logs/vv_create_user_vuln.log0.1 KB
bundle/logs/vv_git_fixed.log1.1 KB
bundle/logs/vv_git_vuln.log1.3 KB
bundle/logs/vv_gogs_fixed.log4.1 KB
bundle/logs/vv_gogs_vuln.log4.8 KB
bundle/logs/vv_http_fixed.log1.0 KB
bundle/logs/vv_http_fixed.log.lp7.2 KB
bundle/logs/vv_http_fixed.log.lpost0.0 KB
bundle/logs/vv_http_vuln.log1.7 KB
bundle/logs/vv_http_vuln.log.lp7.2 KB
bundle/logs/vv_http_vuln.log.lpost0.0 KB
bundle/logs/vv_state_fixed.log5.8 KB
bundle/logs/vv_state_vuln.log9.3 KB
bundle/logs/vv_upload_fixed/first_commit.log0.0 KB
bundle/logs/vv_upload_fixed/first_page.log15.3 KB
bundle/logs/vv_upload_vuln/first_commit.log0.0 KB
bundle/logs/vv_upload_vuln/first_page.log15.3 KB
bundle/logs/vv_upload_vuln/second_commit.log0.0 KB
bundle/logs/vv_upload_vuln/second_page.log15.3 KB
bundle/vuln_variant/findings_notes.txt2.4 KB
bundle/vuln_variant/patch_analysis.md8.9 KB
bundle/vuln_variant/rca_report.md18.5 KB
bundle/vuln_variant/rce_marker_vuln.txt0.2 KB
bundle/vuln_variant/reproduction_steps.sh19.5 KB
bundle/vuln_variant/root_cause_equivalence.json2.8 KB
bundle/vuln_variant/runtime_manifest.json1.1 KB
bundle/vuln_variant/source_identity.json1.8 KB
bundle/vuln_variant/validation_verdict.json4.7 KB
bundle/vuln_variant/variant_manifest.json6.6 KB
bundle/vuln_variant/variant_proof_summary.txt0.6 KB
08 · How to Fix

How to Fix CVE-2026-52813

Upgrade gogs/gogs · github to 0.14.3 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-52813 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-52813

Which Gogs versions are affected by CVE-2026-52813, and where is it fixed?

All versions before 0.14.3 are affected. It is fixed in 0.14.3.

How severe is CVE-2026-52813?

It is rated critical severity: a path-traversal write into an organization's repository path can be leveraged to overwrite Git hooks and achieve remote code execution as the git user.

How can I reproduce CVE-2026-52813?

Download the verified script from this page and run it in an isolated environment against Gogs before 0.14.3. Create an organization via the API using a name containing ../ traversal sequences to place a nested Git repository inside another repository's local worktree, overwrite a Git hook such as hooks/update, and trigger it to confirm command execution as the git user; confirm 0.14.3 blocks the traversal.
11 · References

References for CVE-2026-52813

Authoritative sources for CVE-2026-52813 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.