Skip to content

Commit 80d966d

Browse files
waleedlatif1claude
andauthored
fix(gitlab): pin pagination cursor to configured host + consolidate isSameOrigin (#4873)
* fix(gitlab): pin pagination cursor to configured host before following it The repository-tree keyset cursor stores GitLab's verbatim rel="next" URL and re-fetches it with an Authorization: Bearer header. Assert the cursor's origin matches the configured apiBase before following it, so a tampered or corrupted fileNextUrl cannot exfiltrate the access token to an attacker-controlled host. Fails closed on mismatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * improvement(validation): generalize isSameOrigin and reuse across connectors/tools Add an optional base argument to the shared isSameOrigin (defaulting to the app base URL) so callers can pin a URL to any trusted origin. The GitLab connector's cursor host-check and the tools self-origin check now consume the shared helper instead of their own URL-parsing. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 85942a5 commit 80d966d

3 files changed

Lines changed: 13 additions & 17 deletions

File tree

apps/sim/connectors/gitlab/gitlab.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createLogger } from '@sim/logger'
22
import { getErrorMessage, toError } from '@sim/utils/errors'
33
import { GitLabIcon } from '@/components/icons'
4+
import { isSameOrigin } from '@/lib/core/utils/validation'
45
import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils'
56
import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types'
67
import { computeContentHash, joinTagArray, parseTagDate } from '@/connectors/utils'
@@ -741,6 +742,9 @@ export const gitlabConnector: ConnectorConfig = {
741742
per_page: String(PAGE_SIZE),
742743
pagination: 'keyset',
743744
})
745+
if (state.fileNextUrl && !isSameOrigin(state.fileNextUrl, apiBase)) {
746+
throw new Error('GitLab pagination cursor points to an unexpected host')
747+
}
744748
const url =
745749
state.fileNextUrl ??
746750
`${apiBase}/projects/${encodedProject}/repository/tree?${treeParams.toString()}`

apps/sim/lib/core/utils/validation.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { getBaseUrl } from './urls'
22

33
/**
4-
* Checks if a URL is same-origin with the application's base URL.
5-
* Used to prevent open redirect vulnerabilities.
4+
* Checks if a URL is same-origin with a base URL. Defaults to the application's
5+
* base URL, used to prevent open redirect vulnerabilities; pass an explicit
6+
* `base` to pin a URL to another trusted origin (e.g. a configured API host)
7+
* before following it with credentials.
68
*
79
* @param url - The URL to validate
10+
* @param base - The origin to compare against (defaults to the app base URL)
811
* @returns True if the URL is same-origin, false otherwise (secure default)
912
*/
10-
export function isSameOrigin(url: string): boolean {
13+
export function isSameOrigin(url: string, base: string = getBaseUrl()): boolean {
1114
try {
12-
const targetUrl = new URL(url)
13-
const appUrl = new URL(getBaseUrl())
14-
return targetUrl.origin === appUrl.origin
15+
return new URL(url).origin === new URL(base).origin
1516
} catch {
1617
return false
1718
}

apps/sim/tools/index.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@/lib/core/utils/stream-limits'
2020
import { getBaseUrl, getInternalApiBaseUrl } from '@/lib/core/utils/urls'
2121
import { isUserFile } from '@/lib/core/utils/user-file'
22+
import { isSameOrigin } from '@/lib/core/utils/validation'
2223
import { SIM_VIA_HEADER, serializeCallChain } from '@/lib/execution/call-chain'
2324
import { parseMcpToolId } from '@/lib/mcp/utils'
2425
import { resolveWorkspaceFileReference } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
@@ -1364,17 +1365,7 @@ function isErrorResponse(
13641365
* the platform's own workflow execution endpoints via absolute URL.
13651366
*/
13661367
function isSelfOriginUrl(url: string): boolean {
1367-
try {
1368-
const targetOrigin = new URL(url).origin
1369-
const publicOrigin = new URL(getBaseUrl()).origin
1370-
if (targetOrigin === publicOrigin) return true
1371-
1372-
const internalOrigin = new URL(getInternalApiBaseUrl()).origin
1373-
if (targetOrigin === internalOrigin) return true
1374-
} catch {
1375-
return false
1376-
}
1377-
return false
1368+
return isSameOrigin(url, getBaseUrl()) || isSameOrigin(url, getInternalApiBaseUrl())
13781369
}
13791370

13801371
/**

0 commit comments

Comments
 (0)