Get a demo — 30 minutes →
← Back to blog
Anomity robot illustrating Anomity Integrates with GitLab
Company

Anomity Integrates with GitLab

TL;DR
  • Anomity now connects to GitLab, on GitLab.com or a self-managed instance. One connector reads one top-level group with a read-only group access token.
  • It reads GitLab Duo's governance switches straight from the group: whether Duo is locked for the whole group, whether Duo agents may call MCP servers, the prompt-injection protection level, and whether the AI Catalog is limited to your own group.
  • Default findings: Duo not locked, Duo agents allowed to call MCP servers, prompt-injection protection off or set to log only (detected, then acted on anyway), an unrestricted AI Catalog, and group access tokens with no expiry, older than 90 days or dormant.
  • It also records the Duo add-ons the group holds with the assigned seat count, and one row per group webhook, named by its target host.
  • Setup is a group access token with role Owner and scope read_api only. read_api keeps it read-only; Owner is needed only because GitLab lists webhooks and tokens to Owners.
  • Verified on GitLab.com. No self-managed instance has been swept yet, and every tier limit is reported as a note, never as a coverage gap.

Anomity now integrates with GitLab. Connect a top-level GitLab group, on GitLab.com or a self-managed instance, and Anomity reads how GitLab Duo is governed there: whether Duo is locked for the whole group, whether Duo agents may call MCP servers, how prompt-injection protection is set, and whether the AI Catalog is limited to your own group. It also reads the group access tokens automations run on and the group's webhooks. The integration is available today from the discovery connectors page in the Anomity console.

This post covers what the connector reads, what it flags, how it authenticates, and what is still to be confirmed on self-managed GitLab.

Why GitLab, and why now

Cloud discovery already reads GitHub: the OAuth apps and GitHub Apps held by AI tools, and the Copilot seat picture. GitLab was the obvious gap. When we looked, GitLab turned out to offer something GitHub's API does not: GitLab Duo's governance switches are readable straight from the group API.

Those switches matter more now that Duo is an agent platform and not only code suggestions. A Duo agent reads issues, merge requests and files, and may call MCP servers on behalf of whoever runs it. Content an agent reads is the classic route for indirect prompt injection, and it is the same pattern that let crafted comments steer coding agents in Comment and Control. GitLab ships a control for it, a prompt-injection protection level on the group. Whether that control is off, logging only or blocking is one setting, and until now finding out meant opening each group's settings by hand.

What the connector reads

Every call is a GET, plus one read-only GraphQL query for Duo seats.

From GitLabWhat Anomity records
The top-level groupThe group row: whether Duo is on and locked, whether the Duo Agent Platform is on, whether its agents may call MCP servers, the prompt-injection protection level, web search, experimental Duo features, and whether the AI Catalog is restricted to the group hierarchy.
Group access tokensFolded onto the group row rather than one row each, every token named in a note: no expiry, older than 90 days, dormant, or holding a write scope.
Group service accountsNamed in a note on the group row.
GitLab Duo add-onsA GitLab Duo row with the add-ons the group holds (Duo Core, Duo Pro, Duo Enterprise, Duo with Amazon Q) and the total of assigned seats, the counterpart of the Copilot row the GitHub connector writes.
Group webhooksOne row per webhook, named by its target host, with the events it subscribes to.

Tokens are not rows on purpose. A row per credential buries the inventory and turns one policy failure into one ticket per token. Folding them onto the group gives one finding that names every token. Duo Core is never seat-assigned, so a group with only Duo Core shows no seat count rather than a misleading zero, and individual seat holders are not read, so none is invented.

What it flags

GitLab posture runs through Anomity's cloud AI posture policy type, with a seeded template, GitLab Duo and access-token posture. Each check can be switched on or off per policy. On by default:

  • Duo not locked. Duo on or off is not locked at the top-level group, so any subgroup or project owner can switch it on for their own code without the decision reaching whoever governs the group. The group setting is a default, not a control.
  • Duo agents may call MCP servers. Duo Agent Platform agents can reach tools and data outside GitLab on behalf of whoever runs them, and nothing on the GitLab side records which servers those are.
  • Prompt-injection protection off. Content an agent reads from issues, merge requests or files is never checked for embedded instructions before the agent acts on it.
  • Prompt-injection protection set to log only. An injected instruction is detected and recorded, and the agent acts on it anyway. Interrupt is the blocking level. This check has its own switch, so a team that accepts detection alone can turn it off and keep the 'off entirely' finding.
  • AI Catalog unrestricted. People in the group can enable agents and flows published by anybody on the instance, not only ones your own group created.
  • Token hygiene. Group access tokens with no expiry (self-managed only, since GitLab.com enforces one), older than 90 days, or unused for 90 days.

Off by default, because each is the ordinary state of plenty of well-run groups: Duo web search on, experimental Duo features on, and write-scoped tokens, since an automation that pushes code needs one. The MCP and AI Catalog checks only fire where the Duo Agent Platform is not switched off. Switching Duo on is a product decision, so Duo being enabled is inventory, never a finding.

Findings route to the SIEM, Slack or Jira like every other Anomity finding, so a log-only prompt-injection setting can land as a Jira ticket for the group owner or in a Slack channel for the platform team.

What it found on a real group

We verified the connector against a live GitLab.com group on an Ultimate trial before shipping, with the real transport rather than mocks. With the default template it raised exactly three findings, each matching the group's actual settings: Duo not locked, prompt-injection protection on log only, and the AI Catalog unrestricted. Log only was the level that group was already on, which is why the check exists.

How it authenticates

  1. In GitLab, create a group access token on your top-level group with role Owner and scope read_api only. read_api keeps the token read-only whatever its role. Owner is needed only because GitLab lists group webhooks and access tokens to Owners.
  2. In Anomity, add the GitLab connector with your group path (a pasted group URL works) and, for self-managed GitLab, your instance URL.
  3. Paste the token in full, including its glpat- prefix, then test and sync.

One connector covers one top-level group. A subgroup path is refused, because Duo settings, seats and token governance live at the top and a subgroup only reports inherited values. A personal access token also works, but it belongs to a person, which is exactly the problem our guide to non-human identity governance argues against. GitLab credentials that cannot read the REST API, such as deploy, runner, trigger and feed tokens, are refused at connect time. The token is stored encrypted.

If a sync cannot read the group, the connector turns to Error with the fix in the message: the token was rejected (check it was copied in full and has not expired), it lacks read_api or a role on the group, or no such group is visible to it. We added that after console testing showed a token pasted without its prefix leaving the card green while every sync failed. A failed sync also keeps the last good inventory instead of pruning it.

For self-managed GitLab, the instance URL is checked when it is saved and on every sync. A host that resolves to a private or metadata address is refused at the socket, and redirects are never followed, so the token cannot be walked to another host.

Current limits

  • GitLab.com is verified; self-managed is not yet. No self-managed instance has been swept yet.
  • Tier limits are notes, not gaps. Duo settings, group webhooks and group access tokens are Premium and Ultimate features. A Free group refuses those reads by design, and Anomity records a note rather than a coverage finding. An absent Duo field never fires a finding: absent is not the same as off.
  • Group service accounts can be refused even to an Owner token. We saw it on an Ultimate group, so the note says GitLab refused, and never that you need a higher tier.
  • Not yet seen with real data: a non-empty list of group access tokens or webhooks. Both follow GitLab's documented shapes and are covered by tests.
  • Not collected: project access tokens, enterprise users' personal access tokens, group OAuth applications and individual Duo seat holders.
  • Rotating the token is done by deleting and reconnecting the connector.

How Anomity and GitLab fit together

GitLab owns the controls: Duo settings, the AI Catalog, prompt-injection protection and token policy. Anomity reads whether they are set the way you intend, next to the same kind of reads for GitHub Copilot, which we covered in governing GitHub Copilot in the enterprise, and for Rovo through the Atlassian integration. Then it covers what GitLab cannot see: the coding agents, MCP servers, extensions, plugins, skills, hooks, secrets, CLIs and local LLM runtimes on developer laptops, through the Endpoint Sensor, and the AI web services people use, through the Browser Sensor. On agents that expose a hook, Anomity decides allow, deny or log before a tool call runs. For teams on Cursor, the Cursor integration reads the team settings behind that agent the same way.

A group can lock Duo, block injected instructions and restrict its catalog, and a developer can still point a local agent at the same repository with none of those controls in the path. Seeing both sides is the point. Anomity collects metadata only, redacts secret values on the endpoint, and is SOC 2 Type II attested. To see it against your own GitLab group and fleet, book a 30-minute demo.

Frequently asked questions

What does the Anomity GitLab integration do?

It connects one top-level GitLab group to Anomity's cloud discovery and reads how GitLab Duo is governed there: whether Duo is on and locked, whether the Duo Agent Platform is on and its agents may call MCP servers, the prompt-injection protection level, web search, experiment features and whether the AI Catalog is restricted to the group. It also reads group access tokens, group service accounts, the Duo add-ons and assigned seat count, and group webhooks, and raises findings through Anomity's cloud AI posture policy type.

Which GitLab Duo settings does Anomity flag?

On by default: Duo not locked at the top-level group, Duo agents allowed to call MCP servers, prompt-injection protection switched off, prompt-injection protection set to log only, and an AI Catalog that is not restricted to the group hierarchy. Off by default, because each is the normal state of many well-run groups: Duo web search on and experimental Duo features on. Switching Duo on is a product decision, so Duo being enabled is inventory, never a finding.

Why is 'log only' prompt-injection protection a finding?

Because at that level GitLab detects an injected instruction in an issue, merge request or file, records it, and the agent acts on it anyway. Detection without blocking is a useful signal, but it is not a control. The blocking level is Interrupt. The log-only check has its own switch, so a team that deliberately accepts detection alone can turn it off and still keep the finding for protection switched off entirely.

What token does Anomity need, and can it change anything in GitLab?

A group access token on your top-level group, with role Owner and scope read_api only. read_api makes the token read-only whatever its role, so it cannot change the group, its projects or its settings. Owner is needed only because GitLab lists group webhooks and access tokens to Owners. Every call is a GET, plus one read-only GraphQL query for Duo seats. GitLab credentials that cannot read the REST API, such as deploy, runner and trigger tokens, are refused at connect time.

Does it work with self-managed GitLab?

Yes. GitLab.com is verified end to end. A self-managed URL is accepted, checked when it is saved and again on every sync, and a host that resolves to a private or metadata address is refused. Redirects are never followed, so the token cannot be sent to another host. No self-managed instance has been swept yet.

What happens on GitLab Free or Premium?

Duo settings, group webhooks and group access tokens are Premium and Ultimate features, so a Free group refuses those reads by design. Anomity records each refusal as a note on the group row, not as a coverage gap, so a correctly configured Free group does not carry a permanent finding. An absent Duo field never fires a finding, because absent is not the same as off.

Why are access tokens not listed as separate rows?

A row per token buries the inventory and turns one policy failure into one ticket per token. Instead, token hygiene is folded onto the group row and each token is named in a note, so a single finding lists every token with no expiry, older than 90 days or dormant, and the group owner gets one piece of work.

Does Anomity replace GitLab's own Duo controls?

No. GitLab owns the controls: Duo settings, the AI Catalog, prompt-injection protection and token policy. Anomity reads whether they are set the way you intend, alongside the other code hosts and AI platforms it reads, and covers what GitLab cannot see: the coding agents, MCP servers, skills, hooks and secrets on developer laptops, with allow, deny or log decisions at the agent hook before a tool call runs.

Ask AI about Anomity
ChatGPT Claude Perplexity Google AI Grok