Skip to main content
Cynative’s GitLab connector is built for read-oriented source-control and repository security research. It uses an in-process per-request classifier to resolve each request to its API category and required access level, then allows it only within the configured ceiling — read-only by default, with certain sensitive categories blocked. Connector id: gitlab
Default host: gitlab.com (configurable for self-managed instances)

Quick start

Authenticate with the GitLab CLI or export a Personal Access Token:
Or with an explicit token:
No YAML is required for the default read-only mode against gitlab.com.

Credential discovery

Cynative resolves a token for the configured host (default gitlab.com) in this order:
  1. Token environment variable — GITLAB_TOKEN, then GITLAB_ACCESS_TOKEN, then OAUTH_TOKEN (glab’s precedence order). The first non-empty value is used, exec-free, and takes precedence over any glab credential.
  2. glab (glab auth credential-helper) — when no token env var is set, Cynative delegates to the pinned glab binary (requires glab v1.85.2+), which reads config.yml or the OS keyring, refreshes an expired OAuth token, and prints the credential as JSON.
Cynative reads these variables to discover the token and locate glab’s config:
  • GITLAB_TOKEN — first-precedence token.
  • GITLAB_ACCESS_TOKEN — second-precedence token.
  • OAUTH_TOKEN — third-precedence token.
  • GLAB_CONFIG_DIR — exclusive override of glab’s config directory.
  • XDG_CONFIG_HOME — adds $XDG_CONFIG_HOME/glab-cli/config.yml as a config location.
If no token env var is set and glab has no usable credential, the connector is a quiet ambient skip. If a glab config exists but the glab binary is missing or too old, the connector is skipped loudly with a steer to install/upgrade glab or set GITLAB_TOKEN. Registration and validation: When a token is discovered, Cynative validates it live at startup with a dial-guarded GET /api/v4/user before registering. An invalid, expired, or unreachable token shows the connector as unavailable with a reason rather than registering and failing every request. GitLab 18.9 or later is required for self-managed instances (when using api_reference; the connector itself still registers but api_reference is unavailable for older instances). References:

Target selection

The GitLab connector targets GitLab SaaS — https://gitlab.com, REST API /api/v4 — by default. The instance is chosen by Cynative config (connectors.gitlab.host, default gitlab.com) and host-pinned. The target is not selected per request; the URL must match the served host. To point at a self-managed instance: set connectors.gitlab.host: gitlab.example.com (or CYNATIVE_CONNECTORS_GITLAB_HOST). Optionally set connectors.gitlab.api_host when API calls go to a different host than the login host, append :port for a non-443 instance, set allow_private_network: true for a private network, and ca_cert for a private CA.

Request usage

Use auth_provider: "gitlab". No connector-specific auth object is required.
For self-managed instances, replace gitlab.com with your configured host (or api_host).

Hardening

Host and port pinning

Host pinning allows only the configured host (default gitlab.com), or api_host when set. The TLS port is pinned too — the request URL’s port must match the configured port (or 443 when none is configured). Redirects are never followed. The connector serves the /api/v4 REST API only. A request whose path is not under /api/v4 is not category-classifiable and fails closed (ErrUnclassifiable).

Per-category exposure classification

The in-process classifier resolves each REST request to its GitLab category and required access level (read or write), then allows it only when the configured connectors.gitlab.permissions ceiling permits that level for that category.
  • The check runs before the token is attached.
  • Reads are GET, HEAD, OPTIONS (plus the POST /api/v4/markdown documentation-render endpoint); everything else is a write.
  • The ci-variables category is denied by the secure baseline (even for reads) because its read endpoints return plaintext CI/CD variable values. Any OpenAPI template whose path contains a variables segment is mapped to the ci-variables category at table-distillation time.
  • The GraphQL API (/api/graphql) is not supported and is always denied.
  • A path with a . or .. segment, raw or percent-encoded, is denied.

Model-supplied-credential rejection

The connector is the sole setter of credential material. The following are rejected: Private-Token, Job-Token, Deploy-Token, X-Gitlab-Static-Object-Token, Authorization, Proxy-Authorization, X-Ms-Authorization-Auxiliary, and Cookie headers; URL userinfo; the GitLab request-parameter credentials token, private_token, access_token, job_token (in query string, urlencoded, multipart, or JSON body); the feed_token / rss_token feed credentials; and the sudo impersonation control. After all checks pass, Cynative injects Authorization: Bearer <token>.

Dial-time IP authorization

By default, internal IP ranges (RFC1918, loopback, link-local including cloud metadata, ULA, IPv4-mapped forms) are denied. Set allow_private_network: true for self-managed GitLab instances on private networks. An unconditional floor remains denied even with the opt-in: loopback, link-local, and all private (ULA) IPv6 (fc00::/7) — ULA IPv6 stays denied because every major cloud parks its IPv6 instance-metadata service there.

CA certificate (self-managed with a private CA)

Set connectors.gitlab.ca_cert to the path of a PEM CA certificate file. skip_tls_verify is not supported — TLS validation cannot be bypassed.

Cynative configuration

Target and connection settings

host is the login host (the key glab uses in config.yml). Append :port to either host or api_host for a non-443 instance (e.g. host: gitlab.internal:8443).

Exposure and authorization

connectors.gitlab.permissions is a category → read|write|none ceiling. Omit it entirely for the secure default (read everything except ci-variables). write implies read; none denies even reads.
A non-empty environment value replaces the config-file map wholesale. A blank value is treated as unset. When the ceiling is widened (default=write, an opened ci-variables, or any category:write override), the startup inventory line is loud (⚠).

Defense at a glance

Limitations

  • CI job tokens (endpoint-restricted) are out of scope. Cynative authenticates via Authorization: Bearer, which accepts PATs, project/group access tokens, and OAuth tokens.
  • The category routing table needs egress to gitlab.com. With no network and no local cache the connector fails closed.
  • Container Registry and Pages use separate hosts and authentication paths not covered by this connector.
  • Subpath-mounted installations (e.g. https://example.com/gitlab/) are not supported.
  • A glab credential requires the glab binary (v1.85.2+) on PATH. Set GITLAB_TOKEN for a fully exec-free path.
  • One instance per run. Cynative registers at most one GitLab provider per session.
  • No runtime credential downscoping. The token’s scopes are fixed at issue time. The permissions ceiling narrows what Cynative will attempt but never expands the token’s own scopes.
  • Private (ULA) IPv6 instances are unsupported even with allow_private_network: true. Front a self-managed instance with an RFC1918 IPv4 or global-unicast IPv6 address instead.
  • A literal token field in a request body is rejected. GitLab reads params[:token] (the pipeline-trigger credential) from any body encoding, so any model-supplied token field fails closed. Use the Bearer-authenticated POST /projects/:id/pipeline endpoint instead of the trigger endpoint.
  • skip_tls_verify is not supported. Use ca_cert for self-managed instances with a private CA.