> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cynative.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GitLab Connector: Source Control Research

> Configure Cynative's GitLab connector: token discovery, read-only enforcement, permission ceilings, and self-managed host configuration.

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:

```bash theme={null}
glab auth login
cynative -p "review my GitLab projects for exposed secrets"
```

Or with an explicit token:

```bash theme={null}
export GITLAB_TOKEN=<read_api PAT>
cynative -p "list my GitLab merge requests"
```

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:**

* [GitLab CLI (glab)](https://docs.gitlab.com/cli/) — `GITLAB_TOKEN`, `GLAB_CONFIG_DIR`; `glab auth login` and `glab auth credential-helper`.
* [GitLab personal access token scopes](https://docs.gitlab.com/user/profile/personal_access_tokens/) — `read_api` (least-privilege read), `api` (full), `read_repository`, `read_user`.

## 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.

```javascript theme={null}
const resp = await http_request({
  method: "GET",
  url: "https://gitlab.com/api/v4/projects",
  headers: [],
  body: "",
  auth_provider: "gitlab"
});
console.log(resp.body);
```

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

```yaml theme={null}
connectors:
  gitlab:
    host: gitlab.com              # optional; default is gitlab.com
    api_host: ""                  # optional; overrides the host used for API requests
    allow_private_network: false  # set true for self-managed instances on private networks
    ca_cert: ""                   # path to a PEM CA file for private-CA instances
```

```bash theme={null}
export CYNATIVE_CONNECTORS_GITLAB_HOST=gitlab.example.com
export CYNATIVE_CONNECTORS_GITLAB_API_HOST=api.gitlab.example.com
export CYNATIVE_CONNECTORS_GITLAB_ALLOW_PRIVATE_NETWORK=false
export CYNATIVE_CONNECTORS_GITLAB_CA_CERT=/path/to/ca.pem
```

`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.

```yaml theme={null}
connectors:
  gitlab:
    permissions:
      default: read             # baseline ceiling for every category
      projects: write           # widen a single category to write
      ci-variables: read        # opt back in to a category the baseline blocks
```

```bash theme={null}
export CYNATIVE_CONNECTORS_GITLAB_PERMISSIONS="default=read,projects=write,ci-variables=none"
```

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

| Control | Status |
| - | - |
| Read-only by default | ✓ |
| Enforcement model | in-process per-request classification to a GitLab category + access level, allowed only within the configured ceiling (GraphQL always denied) |
| Configurable exposure | ✓ · `connectors.gitlab.permissions`: `category` → `read\|write\|none` ceiling; default read-only, `ci-variables` blocked |
| Credential downscoping | — · GitLab has no runtime credential downscoping; token scopes are fixed at issue |
| Host pinning | ✓ (configured `host` / `api_host`; one instance per run; port pinned) |
| Dial-time IP authorization | ✓ (default internal-range deny; `allow_private_network: true` opt-in for private networks; loopback and link-local always denied) |
| Model-supplied-credential rejection | ✓ (incl. `Private-Token`, `Job-Token`, URL userinfo, body `token` field) |
| Response redaction | ✓ |

## 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.