{"id":"GO-2026-6448","revision":null,"records":[{"revision":"7edc8e3dd8c84bd994900935e01c03e5","observedAt":"2026-10-28T17:46:48.824+00:00","statement":{"source":"cve-program","id":"CVE-2026-59185","aliases":[],"title":"identrail: Cross-tenant GitHub installation binding without ownership verification","description":"identrail is a machine identity security platform for AWS, GitHub/OIDC, and Kubernetes. Prior to 1.0.2, the default-on `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete` endpoint in `internal/api/router.go` accepts `installation_id` from the request body or `X-GitHub-Installation-ID` header without verifying the installation belongs to the caller's workspace. `CompleteGitHubConnection` in `internal/api/github_connect.go` rigorously binds `state` to the caller's tenant, workspace, and project, but checks only that the installation ID is positive before persisting it as the caller's connection. `ListInstallationRepositories` in `internal/connectors/github/repositories.go` invokes token creation through `internal/connectors/github/app.go` and `POST /app/installations/{installationId}/access_tokens`, using the application's JWT. An authenticated tenant who starts a legitimate connection can supply another customer's non-secret installation ID to read the victim organization's private repository inventory, content, and metadata or drive posture scans, provided identrail's GitHub App is installed there. The default-off V2 `CompleteGitHubConnector` path shares the installation-ownership gap and additionally matches pending `state` without rechecking caller scope. This issue is fixed in version 1.0.2.","state":0,"publishedAt":"2026-10-28T13:15:53.456+00:00","modifiedAt":"2026-10-28T16:07:10.634+00:00","affected":[{"ecosystem":"Vendor product","name":"identrail","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"identrail\",\n                    \"product\": \"identrail\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 1.0.2\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","score":8.5,"severity":"HIGH"}],"references":["https://github.com/identrail/identrail/security/advisories/GHSA-cp3j-m783-3ph5","https://github.com/identrail/identrail/commit/835e40517509d6ef5405c27fbf14f579bedff0e7","https://github.com/identrail/identrail/releases/tag/v1.0.2"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-59185","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-59185","identifiers":["CVE-2026-59185"]},"retained":false,"historicalAffected":[]},{"revision":"f3e09f60d4d34f138e511f01ea7cbd92","observedAt":"2026-10-30T10:50:57.621+00:00","statement":{"source":"euvd","id":"EUVD-2026-112081","aliases":["CVE-2026-59185","GHSA-cp3j-m783-3ph5"],"title":"identrail is a machine identity security platform for AWS, GitHub/OIDC, and Kubernetes. Prior to 1.0.2, the default-on `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete` endpoint in `internal/api/router.go` acc…","description":"identrail is a machine identity security platform for AWS, GitHub/OIDC, and Kubernetes. Prior to 1.0.2, the default-on `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete` endpoint in `internal/api/router.go` accepts `installation_id` from the request body or `X-GitHub-Installation-ID` header without verifying the installation belongs to the caller's workspace. `CompleteGitHubConnection` in `internal/api/github_connect.go` rigorously binds `state` to the caller's tenant, workspace, and project, but checks only that the installation ID is positive before persisting it as the caller's connection. `ListInstallationRepositories` in `internal/connectors/github/repositories.go` invokes token creation through `internal/connectors/github/app.go` and `POST /app/installations/{installationId}/access_tokens`, using the application's JWT. An authenticated tenant who starts a legitimate connection can supply another customer's non-secret installation ID to read the victim organization's private repository inventory, content, and metadata or drive posture scans, provided identrail's GitHub App is installed there. The default-off V2 `CompleteGitHubConnector` path shares the installation-ownership gap and additionally matches pending `state` without rechecking caller scope. This issue is fixed in version 1.0.2.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","score":8.5,"severity":null}],"references":["https://github.com/identrail/identrail/security/advisories/GHSA-cp3j-m783-3ph5","https://github.com/identrail/identrail/commit/835e40517509d6ef5405c27fbf14f579bedff0e7","https://github.com/identrail/identrail/releases/tag/v1.0.2"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-112081","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 28, 2026, 1:15:53 PM","dateUpdated":"Oct 28, 2026, 4:07:10 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-112081","identifiers":["EUVD-2026-112081","CVE-2026-59185","GHSA-cp3j-m783-3ph5"]},"retained":false,"historicalAffected":[]},{"revision":"db71668cb0df4da6af75a98d9a65dc30","observedAt":"2026-10-29T16:15:34.206+00:00","statement":{"source":"nvd","id":"CVE-2026-59185","aliases":[],"title":"identrail is a machine identity security platform for AWS, GitHub/OIDC, and Kubernetes. Prior to 1.0.2, the default-on `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete` endpoint in `internal/api/router.go` acc…","description":"identrail is a machine identity security platform for AWS, GitHub/OIDC, and Kubernetes. Prior to 1.0.2, the default-on `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete` endpoint in `internal/api/router.go` accepts `installation_id` from the request body or `X-GitHub-Installation-ID` header without verifying the installation belongs to the caller's workspace. `CompleteGitHubConnection` in `internal/api/github_connect.go` rigorously binds `state` to the caller's tenant, workspace, and project, but checks only that the installation ID is positive before persisting it as the caller's connection. `ListInstallationRepositories` in `internal/connectors/github/repositories.go` invokes token creation through `internal/connectors/github/app.go` and `POST /app/installations/{installationId}/access_tokens`, using the application's JWT. An authenticated tenant who starts a legitimate connection can supply another customer's non-secret installation ID to read the victim organization's private repository inventory, content, and metadata or drive posture scans, provided identrail's GitHub App is installed there. The default-off V2 `CompleteGitHubConnector` path shares the installation-ownership gap and additionally matches pending `state` without rechecking caller scope. This issue is fixed in version 1.0.2.","state":0,"publishedAt":"2026-10-28T14:17:05.07+00:00","modifiedAt":"2026-10-28T16:17:08.51+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","score":8.5,"severity":"HIGH"}],"references":["https://github.com/identrail/identrail/commit/835e40517509d6ef5405c27fbf14f579bedff0e7","https://github.com/identrail/identrail/releases/tag/v1.0.2","https://github.com/identrail/identrail/security/advisories/GHSA-cp3j-m783-3ph5"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-59185","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-639","CWE-862"],"productCriteria":[],"sourceDates":{"published":"2026-10-28T14:17:05.070","lastModified":"2026-10-28T16:17:08.510"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-59185","identifiers":["CVE-2026-59185"]},"retained":false,"historicalAffected":[]},{"revision":"1daea80894ed44a4aee24d2f5938e73e","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-cp3j-m783-3ph5","aliases":["CVE-2026-59185","GO-2026-6448"],"title":"Identrail Cross-tenant IDOR: Client-supplied GitHub App installation_id is bound to the caller's workspace without ownership verification","description":"## Summary\n\nidentrail's GitHub App connection-completion endpoint binds a fully client-supplied `installation_id` to the caller's workspace without verifying that the installation belongs to, or was installed by, the workspace that initiated the connect flow. identrail then mints a GitHub App installation access token for the supplied `installation_id` using the app's own JWT, so an authenticated tenant can link any other identrail customer's GitHub App installation into their own workspace and read that victim organization's private repositories.\n\n## Affected code (default-on path, no feature flag)\n\n- Route: `internal/api/router.go:3590` — `POST /v1/workspaces/:workspace_id/projects/:project_id/github/connect/complete`. `installation_id` is read from the JSON body or the attacker-controlled `X-GitHub-Installation-ID` header (`router.go:3582-3588`).\n- Service: `internal/api/github_connect.go:781` `CompleteGitHubConnection`. The `state` token IS rigorously bound to the caller's scope (`github_connect.go:818`: `if stateRecord.TenantID != scope.TenantID || stateRecord.WorkspaceID != project.WorkspaceID || stateRecord.ProjectID != project.ProjectID { ... }`), but the only check on `installation_id` is `request.InstallationID <= 0` (`:794`). The raw client value is persisted as the workspace's connection (`:840`).\n- Cross-tenant read primitive: `internal/connectors/github/repositories.go:39` `ListInstallationRepositories` → `internal/connectors/github/app.go:170` mints a token via `POST /app/installations/{installationId}/access_tokens` signed with the App JWT, succeeding for any installation where identrail's app is installed.\n\nThe feature-flagged V2 path `CompleteGitHubConnector` (`github_connect.go:447`, default off) shares the gap and is additionally weaker (matches pending connector by `state` value alone with no caller-scope re-check); a single fix should cover both.\n\n## Intent proof\n\nThe code binds `state` to `{TenantID, WorkspaceID, ProjectID}` and re-verifies it at completion (`:818`), demonstrating it understands binding is required. The asymmetry — `state` bound, `installation_id` unbound — is the missed check.\n\n## Exploitation\n\n1. Attacker is any authenticated identrail tenant; they call `StartGitHubConnection` for their own workspace and receive a `state`.\n2. Attacker calls the completion route with `{state: <their state>, installation_id: V}` where V is a victim org's identrail GitHub App installation id (installation ids are not secret — they appear in post-install redirect URLs, webhook payloads, the org's GitHub App settings, and are enumerable integers).\n3. `CompleteGitHubConnection` accepts (state matches attacker scope), `ListInstallationRepositories(V)` mints a token for V and lists the victim org's private repos, and the connection is persisted under the attacker's workspace.\n4. Attacker now reads the victim org's private repository inventory and can drive posture scans/repo reads via their own workspace.\n\nImpact: cross-tenant disclosure of another customer organization's private GitHub repositories and metadata.\n\n## Remediation\n\nBind `installation_id` with the same rigor as `state`: capture it from GitHub's signed post-install redirect (`setup_url`/`callback_url`) and pin it to the pending state at start time, and/or after minting verify the installation's `account` matches the org the initiating workspace is authorized for.","state":0,"publishedAt":"2026-09-09T23:52:14+00:00","modifiedAt":"2026-09-17T17:40:57.2507456+00:00","affected":[{"ecosystem":"Go","name":"github.com/identrail/identrail","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"1.0.2"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/identrail/identrail/security/advisories/GHSA-cp3j-m783-3ph5","https://github.com/identrail/identrail/commit/835e40517509d6ef5405c27fbf14f579bedff0e7","https://github.com/identrail/identrail","https://github.com/identrail/identrail/releases/tag/v1.0.2"],"sourceUrl":"https://github.com/advisories/GHSA-cp3j-m783-3ph5","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-cp3j-m783-3ph5","identifiers":["GHSA-cp3j-m783-3ph5","CVE-2026-59185","GO-2026-6448"]},"retained":false,"historicalAffected":[]},{"revision":"fb800b3fb89642efaaf0026f3acdd65d","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-6448","aliases":["CVE-2026-59185","GHSA-cp3j-m783-3ph5"],"title":"Identrail Cross-tenant IDOR: Client-supplied GitHub App installation_id is bound to the caller's workspace without ownership verification in github.com/identrail/identrail","description":"Identrail Cross-tenant IDOR: Client-supplied GitHub App installation_id is bound to the caller's workspace without ownership verification in github.com/identrail/identrail","state":0,"publishedAt":"2026-09-16T16:56:22+00:00","modifiedAt":"2026-09-17T17:30:11.2514591+00:00","affected":[{"ecosystem":"Go","name":"github.com/identrail/identrail","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"1.0.2"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/identrail/identrail/security/advisories/GHSA-cp3j-m783-3ph5","https://github.com/identrail/identrail/commit/835e40517509d6ef5405c27fbf14f579bedff0e7","https://github.com/identrail/identrail/releases/tag/v1.0.2"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-6448","license":"OSV · upstream source license applies","key":"osv:GO-2026-6448","identifiers":["GO-2026-6448","CVE-2026-59185","GHSA-cp3j-m783-3ph5"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-31T03:47:09.5766450+00:00","complete":true,"records":1766,"lastAttemptAt":"2026-10-31T03:47:09.576645+00:00","lastSuccessAt":"2026-10-31T03:47:09.7314526+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:09.7314536+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-9266d7b009bf9c4a0ccdcb5cbdea7f905cb3c4d92fd3b41581ece16b2cfdc4e7-caecc587a0b75cf7beeacae1ff36f646a3aea789f7a0588a337b601f31e67ed5","complete":true,"records":417488,"lastAttemptAt":"2026-10-31T03:45:32.9429043+00:00","lastSuccessAt":"2026-10-31T03:45:32.9235987+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:45:32.9429043+00:00","snapshotPublishedAt":"2026-10-31T03:25:40+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c5eb069e059248ab9c1b8cc754ce0885","complete":true,"records":13565,"lastAttemptAt":"2026-10-31T03:47:38.2106586+00:00","lastSuccessAt":"2026-10-31T03:47:38.206+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:38.206+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13637,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-31T03:47:09.5766450+00:00","complete":true,"records":1094,"lastAttemptAt":"2026-10-31T03:47:09.576645+00:00","lastSuccessAt":"2026-10-31T03:47:09.732237+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:09.7322372+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-388d7f08b33a4ea9a69648d0e90f07eb","complete":false,"records":13577,"lastAttemptAt":"2026-10-31T03:47:34.917396+00:00","lastSuccessAt":"2026-10-31T03:47:34.914+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:34.914+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13547,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-21dcd6662970dfea02e4ea60ee637115e8d92664947484d2b9276b3f6568a84f","complete":true,"records":9815,"lastAttemptAt":"2026-10-31T03:47:09.5192979+00:00","lastSuccessAt":"2026-10-30T23:47:34.1817587+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:09.5192979+00:00","snapshotPublishedAt":"2026-10-30T23:33:17+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-10-31T03:45:32.9996003+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:45:32.9996003+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f95f76cd406ae5bae3de632ad37fd2d5160b44f5e6b51748ac6f8f0c0c88e72b","complete":true,"records":3028,"lastAttemptAt":"2026-10-31T03:47:09.537336+00:00","lastSuccessAt":"2026-10-30T23:47:38.4914494+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:09.537336+00:00","snapshotPublishedAt":"2026-10-30T23:18:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-3ce2e3336768d0abd5b020f6f12bd5502a5deb02700e9d89d279597dce6e72ae","complete":true,"records":230992,"lastAttemptAt":"2026-10-31T03:47:09.3912134+00:00","lastSuccessAt":"2026-10-31T03:47:08.0119269+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:09.3912134+00:00","snapshotPublishedAt":"2026-10-31T03:03:11+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}