Skip to content

⭐ gitlab: add protectedEnvironments, integrations, and personal access tokens#8904

Open
tas50 wants to merge 1 commit into
mainfrom
gitlab-env-pat-integrations
Open

⭐ gitlab: add protectedEnvironments, integrations, and personal access tokens#8904
tas50 wants to merge 1 commit into
mainfrom
gitlab-env-pat-integrations

Conversation

@tas50

@tas50 tas50 commented Jul 2, 2026

Copy link
Copy Markdown
Member

What

Three new security-relevant resources for the GitLab provider.

gitlab.project.protectedEnvironment (new)

Deployment gates on protected environments:

  • name, requiredApprovalCount, deployAccessLevels (who may deploy), and approvalRules (approver groups/users) — the deploy-side counterpart to protected branches/tags. Exposed via gitlab.project.protectedEnvironments().

gitlab.project.integration (new)

Active external integrations (Slack, Jira, generic webhook-style services) wired into a project — a data-egress / inbound-trigger surface:

  • id, title, slug, active, inherited, timestamps, and the full set of event triggers (pushEvents, mergeRequestsEvents, pipelineEvents, deploymentEvents, vulnerabilityEvents, …). Exposed via gitlab.project.integrations().

gitlab.user.personalAccessToken (new)

PAT inventory, exposed via gitlab.user.personalAccessTokens() (filtered by the user's id):

  • id, name, description, active, revoked, scopes, createdAt, expiresAt, lastUsedAt — for auditing over-scoped, stale, or non-expiring tokens. Complements the existing project/group access-token resources.

Example queries

gitlab.project.protectedEnvironments.where(requiredApprovalCount == 0) { name }
gitlab.project.integrations.where(active) { slug title pushEvents mergeRequestsEvents }
gitlab.users.flatMap(personalAccessTokens).where(expiresAt == null && active) { name scopes }
gitlab.users.flatMap(personalAccessTokens).where(scopes.contains("api")) { name }

Notes

  • All three are additive; new .lr.versions entries at 13.3.9.
  • personalAccessTokens() requires an admin token to enumerate other users' tokens (self-access otherwise); premium/permission-gated calls return an empty list on 403/404 rather than failing the resource graph.
  • protectedEnvironment deploy-access levels and approval rules are modeled as []dict (they carry groupInheritanceType and no deploy key, so they don't fit the shared protectedBranch.accessLevel row); token/secret values are never exposed.

Test

  • go build ./... (gitlab module) — clean
  • go vet ./resources/ — clean
  • go test ./resources/ — pass

…s tokens

Three new security resources.

gitlab.project.protectedEnvironment (new) — deployment gates:
- name, requiredApprovalCount, deployAccessLevels, and approvalRules
  (who may deploy and how many approvals a deployment needs).

gitlab.project.integration (new) — external services (Slack, Jira, generic
webhooks) wired into the project:
- id, title, slug, active, inherited, timestamps, and the full set of event
  triggers (push/issues/mergeRequests/pipeline/deployment/vulnerability/...).
  A data-egress and inbound-trigger surface.

gitlab.user.personalAccessToken (new) — PAT inventory via
gitlab.user.personalAccessTokens (filtered by the user's id):
- id, name, description, active, revoked, scopes, createdAt, expiresAt,
  lastUsedAt — for auditing over-scoped, stale, or non-expiring tokens.
  Requires an admin token (all users) or self-access.

Premium/permission-gated calls return an empty list on 403/404. New
.lr.versions entries at 13.3.9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@mondoo-code-review mondoo-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing pagination and error handling in integrations listing may truncate results or fail on restricted projects.

Comment on lines +551 to +554
services, _, err := conn.Client().Services.ListServices(int(p.Id.Data))
if err != nil {
return nil, err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 criticalListServices is called without pagination. If a project has more integrations than the API's default page size, results will be silently truncated. Add a pagination loop like the one used in protectedEnvironments() (line 602–614). The go-gitlab ListServices method likely accepts ListServiceOptions with ListOptions.

Comment on lines +552 to +554
if err != nil {
return nil, err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 warning — Unlike protectedEnvironments() and personalAccessTokens(), this method does not handle 403/404 gracefully — it will propagate the error and fail the resource graph. Add a resp check:

if err != nil {
    if resp != nil && (resp.StatusCode == 403 || resp.StatusCode == 404) {
        return []any{}, nil
    }
    return nil, err
}

@@ -541,6 +541,132 @@ func projectTagAccessLevels(runtime *plugin.Runtime, idPrefix string, descs []*g
}

// protectedTags lists the project's protected tags and who may create them.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 suggestion — The id() method for mqlGitlabProjectIntegration is placed above the unrelated protectedTags comment on line 541. Consider moving it closer to the integrations() method or into a separate section for better readability.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Test Results

11 253 tests   11 246 ✅  5m 23s ⏱️
   546 suites       7 💤
    40 files         0 ❌

Results for commit a83efef.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant