Skip to content

Commit 9073bd8

Browse files
1 parent 341f25f commit 9073bd8

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-656w-6f6c-m9r6",
4+
"modified": "2026-03-09T17:29:47Z",
5+
"published": "2026-03-09T17:29:47Z",
6+
"aliases": [
7+
"CVE-2026-30920"
8+
],
9+
"summary": "OneUptime has broken access control in GitHub App installation flow that allows unauthorized project binding",
10+
"details": "### Summary\n\nOneUptime's GitHub App callback trusts attacker-controlled `state` and `installation_id` values and updates `Project.gitHubAppInstallationId` with `isRoot: true` without validating that the caller is authorized for the target project. This allows an attacker to overwrite another project's GitHub App installation binding.\n\nRelated GitHub endpoints also lack effective authorization, so a valid installation ID can be used to enumerate repositories and create `CodeRepository` records in an arbitrary project.\n\n### Details\n\nThe callback decodes unsigned base64 JSON from `state` and uses the embedded `projectId` directly:\n\n- https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L34-L112\n\nIt then writes the supplied `installation_id` into the target project with root privileges:\n\n```ts\nawait ProjectService.updateOneById({\n id: new ObjectID(projectId),\n data: { gitHubAppInstallationId: installationId },\n props: { isRoot: true },\n});\n```\n\nThe `userId` in `state` is only checked for presence, not authenticity:\n\n- https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L73-L79\n\nThe install flow also generates `state` as plain base64 JSON, not a signed or session-bound token:\n\n- https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L127-L165\n\nThe follow-on endpoints are also vulnerable:\n\n- Repository listing: https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L179-L258\n- Repository connect: https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L260-L356\n- Middleware allows requests with no token to continue as `Public`: https://github.com/OneUptime/oneuptime/blob/master/Common/Server/Middleware/UserAuthorization.ts#L205-L211\n- Installation tokens are minted from any valid installation ID: https://github.com/OneUptime/oneuptime/blob/master/Common/Server/Utils/CodeRepository/GitHub/GitHub.ts#L347-L425\n\n### PoC\n\nMinimal proof of unauthorized project tampering:\n\n```bash\nSTATE=$(printf '%s' '{\"projectId\":\"<victim-project-uuid>\",\"userId\":\"x\"}' | base64 | tr -d '\\n')\ncurl -isk \"https://<host>/api/github/auth/callback?installation_id=999999999&state=${STATE}\"\n```\n\nExpected result:\n\n- Server returns a `302` redirect to `/dashboard/<victim-project-uuid>/code-repository?installation_id=999999999`\n- The target project's `gitHubAppInstallationId` is overwritten\n\n### Impact\n\n- Unauthorized modification of `Project.gitHubAppInstallationId`\n- Temporary GitHub integration breakage if a bogus installation ID is set\n- Cross-project binding of attacker-controlled GitHub App installations\n- Repository metadata disclosure for a supplied valid installation ID\n- Unauthorized creation of `CodeRepository` records in arbitrary projects",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "@oneuptime/common"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "10.0.19"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/OneUptime/oneuptime/security/advisories/GHSA-656w-6f6c-m9r6"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/OneUptime/oneuptime"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L127-L165"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L179-L258"
54+
},
55+
{
56+
"type": "WEB",
57+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L260-L356"
58+
},
59+
{
60+
"type": "WEB",
61+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L34-L112"
62+
},
63+
{
64+
"type": "WEB",
65+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/API/GitHubAPI.ts#L73-L79"
66+
},
67+
{
68+
"type": "WEB",
69+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/Middleware/UserAuthorization.ts#L205-L211"
70+
},
71+
{
72+
"type": "WEB",
73+
"url": "https://github.com/OneUptime/oneuptime/blob/master/Common/Server/Utils/CodeRepository/GitHub/GitHub.ts#L347-L425"
74+
}
75+
],
76+
"database_specific": {
77+
"cwe_ids": [
78+
"CWE-345",
79+
"CWE-639",
80+
"CWE-862"
81+
],
82+
"severity": "HIGH",
83+
"github_reviewed": true,
84+
"github_reviewed_at": "2026-03-09T17:29:47Z",
85+
"nvd_published_at": null
86+
}
87+
}

0 commit comments

Comments
 (0)