{
  "variant_id": "gogs-cve-2026-52813-nonadmin-org-create",
  "created_at": "2026-07-01T14:34:00Z",
  "variant_summary": "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 not pathutil.Clean the owner name) and the same sink (nested bare repo written outside the repository ROOT inside another repo's local worktree at data/tmp/local-r/<id>; executable post-update hook planted via Git smart-HTTP + web-upload sync; git-receive-pack runs the hook as the Gogs user -> RCE). Demonstrated end-to-end with a non-admin user (is_admin=0). NOT a bypass: fixed v0.14.3 rejects the name inline in CreateOrgForUser (AlphaDashDot -> 422) and pathutil.Clean in UserPath neutralises '..'. A non-reachable defense-in-depth gap (database.RepoPath/WikiPath not cleaning the repo name) is documented but not exploitable because AlphaDashDot blocks '/' at every repo-name entry point.",
  "relation": "newer_version_sibling",
  "origin_kind": "pruva_variant",
  "repository": "https://github.com/gogs/gogs",
  "submitted_target": {
    "target_kind": "git_tag",
    "commit_sha": "5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb",
    "version": "0.14.2",
    "ref": "v0.14.2",
    "display": "gogs v0.14.2 (5dcb6c64) — vulnerable target of CVE-2026-52813"
  },
  "variant_target": {
    "target_kind": "git_tag",
    "commit_sha": "5dcb6c64bdf61e38dbdbb941c1d69789c560d0fb",
    "version": "0.14.2",
    "ref": "v0.14.2",
    "display": "gogs v0.14.2 (5dcb6c64) — non-admin CreateMyOrg entry point confirmed to reproduce"
  },
  "same_root_cause_confidence": "high",
  "same_surface_confidence": "high",
  "claimed_surface": "api_remote",
  "validated_surface": "api_remote",
  "required_entrypoint_kind": "api_remote",
  "required_entrypoint_detail": "POST /api/v1/user/orgs (org.CreateMyOrg, reqToken() only — NON-ADMIN) binding api.CreateOrgOption{username} (binding:\"Required\", no AlphaDashDot) -> CreateOrgForUser -> database.CreateOrganization -> os.MkdirAll(repoutil.UserPath(org.Name)). Same chain as the admin reproduction but reachable by any authenticated user (is_admin=0).",
  "attacker_controlled_input": "Organization (owner) name containing ../ path-traversal sequences, supplied via the Gogs HTTP API as api.CreateOrgOption.username through POST /api/v1/user/orgs (no AlphaDashDot on this field); plus an executable Git hook (nested/rce.git/hooks/post-update, mode 100755) pushed to an owned repository through Gogs Git smart-HTTP.",
  "trigger_path": "Non-admin authenticated user -> POST /api/v1/user/orgs with username=../data/tmp/local-r/<wid>/nested (HTTP 201 on v0.14.2, since CreateOrgForUser has no inline validation and api.CreateOrgOption has no AlphaDashDot) -> CreateOrganization -> repoutil.UserPath does not clean '..' -> owner dir created at <APP_DATA_PATH>/tmp/local-r/<wid>/nested (OUTSIDE repository ROOT, INSIDE writer's local worktree) -> POST /api/v1/org/<enc>/repos creates rce -> bare repo at <APP_DATA_PATH>/tmp/local-r/<wid>/nested/rce.git -> executable post-update hook pushed to writer via Git smart-HTTP -> Gogs web-upload sync (UpdateLocalCopyBranch: git fetch + reset --hard) materialises the hook into the nested bare repo hooks/ (mode 0755) -> git-receive-pack on the planted bare repo executes the attacker's hook as the Gogs service user (RCE). Fixed v0.14.3: CreateOrgForUser inline AlphaDashDot rejects (422) + pathutil.Clean in UserPath neutralises '..' -> no nested repo, no RCE.",
  "observed_impact_class": "code_execution",
  "exploitability_confidence": "high",
  "evidence_scope": "production_path",
  "runtime_manifest_present": true,
  "end_to_end_target_reached": true,
  "inferred": false,
  "claim_block_reason": null,
  "blocking_mitigation": "Fixed in Gogs v0.14.3 (commit 3ba8aca90e17e5410b7e8b227c9f29256ac3e875): CreateOrgForUser (internal/route/api/v1/org/org.go) validates the org name inline with binding.AlphaDashDotPattern (rejects '/', '\\', etc.) and enforces MaxSize(35), returning HTTP 422; repoutil.UserPath additionally wraps the name with pathutil.Clean. Both the admin (POST /api/v1/admin/users/:user/orgs) and non-admin (POST /api/v1/user/orgs) org-creation endpoints route through CreateOrgForUser and are therefore covered. The variant is an alternate trigger on the vulnerable version, NOT a bypass of the fixed version.",
  "file_path": "internal/route/api/v1/org/org.go",
  "line_start": 16,
  "line_end": 31,
  "secondary_anchors": [
    {
      "file_path": "internal/route/api/v1/api.go",
      "line_start": 382,
      "line_end": 384
    },
    {
      "file_path": "internal/route/api/v1/org/org.go",
      "line_start": 65,
      "line_end": 67
    },
    {
      "file_path": "internal/database/org.go",
      "line_start": 165,
      "line_end": 166
    },
    {
      "file_path": "internal/repoutil/repoutil.go",
      "line_start": 52,
      "line_end": 55
    },
    {
      "file_path": "internal/database/repo.go",
      "line_start": 1356,
      "line_end": 1358,
      "comment": "Deprecated database.RepoPath — does NOT pathutil.Clean repo name (defense-in-depth gap, non-reachable due to AlphaDashDot on all repo-name entry points)"
    },
    {
      "file_path": "internal/database/wiki.go",
      "line_start": 54,
      "line_end": 56,
      "comment": "database.WikiPath — does NOT pathutil.Clean repo name (defense-in-depth gap, non-reachable)"
    },
    {
      "file_path": "internal/database/repo.go",
      "line_start": 648,
      "line_end": 650,
      "comment": "Repository.LocalCopyPath() = data/tmp/local-r/<id> — worktree sink used by UpdateLocalCopyBranch on both v0.14.2 and v0.14.3"
    }
  ],
  "review_scope_paths": [
    "internal/repoutil/repoutil.go",
    "internal/pathutil/pathutil.go",
    "internal/route/api/v1/org/org.go",
    "internal/route/api/v1/admin/org.go",
    "internal/route/api/v1/api.go",
    "internal/database/org.go",
    "internal/database/repo.go",
    "internal/database/wiki.go",
    "internal/database/repo_editor.go",
    "internal/route/repo/http.go"
  ],
  "artifact_refs": {
    "variant_manifest": "bundle/vuln_variant/variant_manifest.json",
    "validation_verdict": "bundle/vuln_variant/validation_verdict.json",
    "runtime_manifest": "bundle/vuln_variant/runtime_manifest.json",
    "repro_log": "bundle/logs/vuln_variant_steps.log",
    "root_cause_equivalence": "bundle/vuln_variant/root_cause_equivalence.json",
    "reproducer": [
      "bundle/vuln_variant/reproduction_steps.sh"
    ]
  }
}
