gitlab

package
v0.88.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package gitlab implements the gitlab source plugin: lists projects ("repositories") and members under a single GitLab group and emits the cross-vendor git_repository and directory_user evidence types — suitable for SOC 2 / ISO 27001 branch-protection, code-review, and identity (MFA/admin/lifecycle) policies.

GitLab is one of several substitutable source-code platforms (GitHub, Bitbucket, future Gitea/Azure DevOps); a policy accepts the git_repository evidence type, not a vendor, so this plugin is immediately usable by every policy that already consumes GitHub repositories (the substitutability principle — docs/architecture/04a-evidence-type-registry.md).

Per the KISS-no-DRY axiom (docs/architecture/04-source-plugins.md §The plugin contract), the plugin caches nothing across Collect calls. N policies bound to this plugin → N invocations of Collect.

Test injection: the API interface mirrors the github plugin — the concrete *sdkAPI (backed by gitlab.com/gitlab-org/api/client-go) satisfies it, and unit tests inject an in-memory fake. The real SDK adapter is exercised by an httptest-backed test; live integration tests against gitlab.com are deferred to the testing revamp.

Period-scoped types: alongside the two configuration snapshots the plugin also emits pull_request (one record per merge request MERGED inside the audit window) and deployment (one record per release to a GitLab environment in the window, failed ones included). GitLab merge requests map onto the cross-vendor pull_request type deliberately — the type is vendor-neutral, so one CC8.1 / ISO A.8.32 change-review policy samples GitHub PRs and GitLab MRs through a single contract. The window arrives as period_start / period_end slot params; a caller that passes none (the conformance harness) gets a trailing one-year window ending at the injected clock, never time.Now().

directory_user (WU-2.2): the plugin also lists the group's members and emits one directory_user per member, mapping AccessLevel ≥ Maintainer (or instance-admin) → is_admin, account state → is_active, and the user's two_factor_enabled → mfa_enabled. 2FA and instance-admin status are only readable with a group-owner / instance-admin token (via the Users API); without that privilege the sdkAPI degrades gracefully and mfa_enabled is best-effort false — documented in docs/configuration.md.

Index

Constants

View Source
const EvidenceTypeDeployment = "deployment"

EvidenceTypeDeployment is the cross-vendor deployment shape — one record per release to a running environment during the period, failed ones included.

View Source
const EvidenceTypeDirectoryUser = "directory_user"

EvidenceTypeDirectoryUser is the cross-vendor directory_user shape this plugin emits for the group's members — identical to the shape the github and okta plugins emit (the same identity evidence type).

View Source
const EvidenceTypePullRequest = "pull_request"

EvidenceTypePullRequest is the cross-vendor pull_request shape — one record per change MERGED during the audit period. A GitLab merge request IS that change; the evidence type is vendor-neutral, so the github and gitlab plugins are substitutable for a change-review policy. It answers "did this change actually get reviewed?", which the git_repository configuration snapshot structurally cannot.

View Source
const EvidenceTypeRepository = "git_repository"

EvidenceTypeRepository is the cross-vendor git_repository shape this plugin emits.

View Source
const SourceID = "gitlab"

SourceID is the registered ID for the gitlab plugin instance.

Variables

This section is empty.

Functions

This section is empty.

Types

type API

type API interface {
	// ListRepos returns all projects under the configured group,
	// normalized into the git_repository contract. Implementations must
	// page transparently — callers receive the full list in one slice.
	ListRepos(ctx context.Context) ([]Repo, error)
	// ListMembers returns all members of the configured group, normalized
	// into the directory_user contract. Implementations must page
	// transparently — callers receive the full list in one slice.
	ListMembers(ctx context.Context) ([]Member, error)
	// ListMergedPullRequests returns the group's merge requests merged
	// inside [start, end], normalized into the pull_request contract.
	// Implementations must page transparently and may over-return —
	// the plugin re-applies the window, so it owns the period semantics
	// regardless of which API implementation is wired in.
	ListMergedPullRequests(ctx context.Context, start, end time.Time) ([]PullRequest, error)
	// ListDeployments returns the group's deployments created inside
	// [start, end], failed ones included, normalized into the deployment
	// contract. Implementations must page transparently and may
	// over-return; the plugin re-applies the window.
	ListDeployments(ctx context.Context, start, end time.Time) ([]Deployment, error)
}

API is the subset of the GitLab REST API the plugin uses. Defining it as an interface lets tests inject a fake without making real network calls; the concrete *sdkAPI satisfies it.

type Deployment added in v0.71.0

type Deployment struct {
	Repository string
	// ID is the vendor-native deployment identifier as a string.
	ID  string
	SHA string
	// Environment is the environment name exactly as GitLab reports it.
	Environment string
	// EnvironmentTier is GitLab's environment tier ("production",
	// "staging", …). Empty when the project never set one, in which case
	// the plugin falls back to the environment name for is_production.
	EnvironmentTier string
	Creator         string
	CreatedAt       time.Time
	// Status is the raw GitLab deployment status (created / running /
	// success / failed / canceled / blocked), normalized by the plugin
	// into the deployment type's closed vocabulary.
	Status string
}

Deployment is one release of code to a GitLab environment during the audit period, including failed ones — a failed production deployment is still a change event an auditor may ask about.

type Member added in v0.39.0

type Member struct {
	Username   string
	Name       string
	Email      string
	MFAEnabled bool
	IsAdmin    bool
	IsActive   bool
}

Member is the subset of fields the plugin extracts from a GitLab group member, normalized into the directory_user contract. Every field maps to a property the consuming policies read so none reads an absent field.

MFAEnabled and the instance-admin component of IsAdmin require a group-owner / instance-admin token (the Users API exposes two_factor_enabled / is_admin); the sdkAPI degrades gracefully when the token can't read them, leaving MFAEnabled best-effort false.

type Options

type Options struct {
	API API
	// Now is injected so tests can produce deterministic CollectedAt
	// values. Production callers leave it nil → time.Now().UTC().
	Now func() time.Time
}

Options is the constructor input.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is the in-process gitlab source.

func New

func New(opts Options) *Plugin

New constructs a Plugin around an explicit API implementation. Callers using the real GitLab REST API should use NewFromToken.

func NewFromToken

func NewFromToken(_ context.Context, group, token, baseURL string) (*Plugin, error)

NewFromToken constructs a Plugin backed by the real GitLab REST API for the given group, using the supplied personal-access/group token. A blank baseURL defaults to gitlab.com; self-managed instances pass their own URL. Live integration tests are deferred.

func (*Plugin) Caveats added in v0.81.0

func (*Plugin) Caveats() []core.SourceCaveat

Caveats declares the fields this plugin can emit without observing them.

Two different limits live in this one list, and the Detail text says which applies, because the operator's remedy is completely different:

  • ABSOLUTE — the three pipeline-scanning booleans. GitLab configures SAST, Secret Detection and Dependency Scanning in .gitlab-ci.yml, not as a project-settings field, so there is no read-only API to ask and the plugin hardcodes false (see mapProject). Their policies therefore cannot pass on a GitLab estate — only ever fail — so the honest remedy is a documented exception or a compensating check, not a token change.
  • CONDITIONAL — mfa_enabled, and the three protection/approval fields. The read IS possible; a lesser-privileged token or a free-tier project just gets a 403/404 that the sdkAPI deliberately swallows so the listing still succeeds. What is left behind is the zero value, which is indistinguishable from a measured "not configured". Provisioning the right token (or tier) makes the value real.

The conditional ones are declared unconditionally because privilege and tier are not known at plan time: those reads happen during collection, long after the planner has to decide whether to warn. Over-warning a correctly-privileged token is the cheaper error — the operator checks the scope and moves on, where the silent case grades a control on a value nobody read.

Every field here is read by at least one shipped SOC 2 / ISO 27001 policy; a field GitLab cannot observe but no policy consumes costs nothing and is deliberately absent, so the planner's warnings stay worth reading.

func (*Plugin) Collect

func (p *Plugin) Collect(ctx context.Context, req core.SlotRequest) ([]core.EvidenceRecord, error)

Collect dispatches to the per-type collectors for whichever emitted types the slot accepts, returning their records together. A slot that accepts none of the emitted types is rejected. Records are sorted by ID within each type group; the collector splits them by Type for envelope writing.

func (*Plugin) Emits

func (*Plugin) Emits() []string

Emits returns the evidence types this plugin can produce: project metadata as git_repository, group members as directory_user, and the two period-scoped types — merged merge requests as pull_request and environment releases as deployment.

func (*Plugin) ID

func (*Plugin) ID() string

ID returns the registered plugin ID.

func (*Plugin) Init

func (*Plugin) Init(context.Context, map[string]any) error

Init is a no-op; the constructor has already received configuration.

type PullRequest added in v0.71.0

type PullRequest struct {
	// Repository is the project's full path ("group/project"), the same
	// form git_repository uses for name, so the two types join.
	Repository string
	// Number is the MR iid — the per-project number a customer quotes in
	// a waiver, not the instance-wide id.
	Number       int
	Author       string
	MergedBy     string
	TargetBranch string
	// MergeCommitSHA is the commit the MR produced on the target branch;
	// the adapter substitutes the squash commit when the project squashes
	// and GitLab therefore reports no merge commit.
	MergeCommitSHA string
	MergedAt       time.Time
	// Approvers carries the usernames GitLab reports in approved_by,
	// verbatim — the plugin de-duplicates and drops blanks. Empty when
	// the approvals endpoint could not be read (graceful degradation) or
	// when nobody approved.
	Approvers []string
	// ChecksPassed is true when the newest pipeline attached to the MR
	// succeeded. GitLab exposes no per-check aggregate comparable to
	// GitHub's check runs, so the head pipeline is the CI signal.
	ChecksPassed bool
}

PullRequest is one merge request MERGED into a project under the group during the audit period. The adapter supplies the raw vendor facts — GitLab's flat approver list and the newest MR pipeline's outcome; the plugin derives approval_count / independent_approval_count / approved_before_merge from them, so the normalization is unit-testable without a network.

type Repo

type Repo struct {
	Name            string
	DefaultBranch   string
	ProtectionOn    bool
	RequiredReviews int

	RequiresSignedCommits   bool
	RequiresLinearHistory   bool
	AllowsForcePush         bool
	DismissStaleReviews     bool
	RequireCodeOwnerReviews bool

	IsPrivate bool
	Archived  bool

	SecretScanningEnabled   bool
	PushProtectionEnabled   bool
	DependabotAlertsEnabled bool
	CodeScanningEnabled     bool
}

Repo is the subset of fields the plugin extracts from a GitLab project, normalized into the git_repository contract. Every field maps to a property of the git_repository evidence type so policies never read an absent field.

Fields without a read-only GitLab API analog are left at their zero value and documented at the mapping site (sdkAPI.ListRepos):

  • SecretScanningEnabled / CodeScanningEnabled / DependabotAlertsEnabled (GitLab pipeline SAST/Secret/Dependency scanning is configured in .gitlab-ci.yml, not exposed as a project-settings boolean).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL