PS Product SecurityKnowledge Base

Reusable GitLab Includes and Components

Reusable GitLab Includes and Components

Intro: Shared pipeline logic is powerful because it eliminates drift. It is also risky because it creates a dependency chain that can silently change how dozens of pipelines behave. Treat reusable pipeline configuration the way you treat shared libraries: version it, review it, and pin it intentionally.

What this page includes

  • how include and components fit into GitLab pipeline design
  • examples of reusable security scanner jobs
  • safer consumption patterns for platform-owned pipeline building blocks
  • comments on pinning, trust, secrets, and runner isolation

Working assumptions

  • shared pipeline code should reduce duplication without hiding security logic from the teams that consume it

Reuse options at a glance

Mechanism Best use Security note
include:local reuse within the same repo easiest to review in one MR
include:project central platform-owned templates protect the source project and ref
include:remote public external YAML highest integrity risk; prefer pinned or internally mirrored sources
include:template GitLab-provided templates review before adopting blindly
include:component versioned reusable catalog items pin to a specific version or SHA-like stable reference

Example: local include for shared scanner jobs

Root .gitlab-ci.yml:

include:
  - local: .gitlab/ci/security.yml

stages:
  - build
  - security
  - deploy

.gitlab/ci/security.yml:

semgrep_scan:
  stage: security
  image: semgrep/semgrep:latest
  script:
    - semgrep scan --config p/default --json --output semgrep.json
  artifacts:
    paths: [semgrep.json]

bandit_scan:
  stage: security
  image: python:3.12-alpine
  script:
    - pip install bandit
    - bandit -r app -f json -o bandit.json
  artifacts:
    paths: [bandit.json]

Local include is simple and transparent. Use it first before building a central platform abstraction.

Example: project include for a platform-managed gate

include:
  - project: platform/ci-templates
    ref: v2.3.1
    file:
      - /security/common-gates.yml
      - /security/release-evidence.yml

Why it is useful:

  • one team maintains common patterns centrally;
  • consuming repos pin to an explicit version;
  • upgrades can be tested and rolled out deliberately.

Example: component include with inputs

include:
  - component: $CI_SERVER_FQDN/platform/security-components/semgrep-gate@1.4.0
    inputs:
      stage: security
      config: p/default
      fail_on_severity: high

Components are useful when you want a reusable unit with parameters instead of a monolithic shared YAML file.

Security review checklist for reused pipeline logic

Before consuming a shared include or component, check:

  • where the source repo lives and who can change it;
  • whether you pin a stable version;
  • whether the included jobs request secrets, tokens, or privileged runners;
  • whether the included jobs publish artifacts externally;
  • whether any remote scripts or images are pulled at runtime;
  • whether the component changes merge behavior, deploy behavior, or environment targeting.

Example: a reusable Semgrep component contract

Consumer-side usage:

include:
  - project: platform/security-components
    ref: v1.0.4
    file: /components/semgrep.yml

variables:
  SEMGREP_RULESET: "p/default"

semgrep_gate:
  extends: .platform_semgrep_gate

Platform-side component:

.platform_semgrep_gate:
  stage: security
  image: semgrep/semgrep:1.84.0
  script:
    - semgrep scan --config "${SEMGREP_RULESET:-p/default}" --json --output semgrep.json
  artifacts:
    paths: [semgrep.json]
    expire_in: 14 days

This keeps the consumer YAML thin without making behavior mysterious.

Conditional include example

include:
  - local: .gitlab/ci/python-security.yml
    rules:
      - exists:
          - requirements.txt
          - pyproject.toml

This lets a monorepo or mixed-codebase project include only the relevant pipeline logic.

Safe versioning guidance

Prefer:

  • exact release tags for internal, trusted component projects;
  • commit-SHA-style pinning when the source is especially sensitive;
  • explicit upgrade MRs rather than silent drift.

Avoid:

  • latest;
  • remote includes from uncontrolled locations;
  • hidden dependency changes that alter release or security semantics.

Secret and token discipline

A shared component should not assume broad secret access.

Safer patterns:

  • require inputs rather than hardcoding environment names;
  • scope variables to protected refs and environments;
  • keep deploy credentials outside generic scanner components;
  • document required runner tags and environment expectations.

Example: release evidence component pack

include:
  - project: platform/security-components
    ref: v1.2.0
    file: /components/release-evidence.yml

release_evidence:
  extends: .platform_release_evidence
  variables:
    EVIDENCE_FILES: "semgrep.json,bandit.json,dependency-check-report.xml,zap-report.json"

The component can standardize packaging and evidence collection while each repo decides which reports to produce.

Component governance model

A healthy ownership model usually includes:

  • protected default and release branches in the component project;
  • mandatory merge request approvals;
  • signed commits or at least stricter provenance checks;
  • changelog entries for user-facing behavior changes;
  • a test project that validates the component before release.

Footer