Rules / GL075
GL075 include: source not on the allowlist
Severity: warn
OWASP: CICD-SEC-3 – Dependency Chain Abuse
What glsec checks
GL075 is an opt-in allowlist for the provenance of include: sources. When you configure allowed_include_sources, glsec flags any include:project, include:component, or include:remote whose source is not on the list.
Included configuration runs with full pipeline privileges, so an include from an untrusted project, component, or host is a direct config-injection / supply-chain vector. GL003 and GL041 check that includes are pinned (immutable ref); GL075 complements them by checking where the include comes from. A pinned include from an attacker-controlled namespace is clean under GL003 but flagged here:
include:
- project: attacker-group/evil-ci
ref: "1111111111111111111111111111111111111111" # pinned, but untrusted source
file: /pipeline.yml
Configuration
# .glsec.yml
allowed_include_sources:
- my-group # any project under the my-group namespace
- my-group/ci-templates # an exact project
- gitlab.com/trusted-components # a component host/namespace prefix
- templates.example.com # a remote host
Matching mirrors allowed_registries (GL065):
- An entry without a
/matches by the first path segment — the project namespace, the component host, or the remote host. - An entry with a
/matches as a host/namespace prefix (somy-group/ci-templatesallowsmy-group/ci-templatesbut notmy-group/other).
Components on the same instance ($CI_SERVER_FQDN)
GitLab documents component: $CI_SERVER_FQDN/<namespace>/<project>/<component>@<version> as the portable way to reference a component on the current instance ($CI_SERVER_HOST and the ${…} forms work too). The host is not known statically, so for these components:
- An entry without a host is matched against the namespace path:
my-groupormy-group/ci-templates. - An entry can spell the variable out:
$CI_SERVER_FQDN/my-group. - An entry that names a host (
gitlab.com/my-group) does not match, because glsec cannot tell whether the scanned instance is that host. $CI_SERVER_FQDN/$CI_PROJECT_PATH/…,$CI_PROJECT_NAMESPACE/…and$CI_PROJECT_ROOT_NAMESPACE/…are the project's own namespace and are never flagged.
include: does not see the file's own variables: block, so redefining these variables there cannot redirect the include.
Dot segments
A source whose path contains a . or .. segment, also percent-encoded (%2e), is always flagged when an allowlist is configured. Prefix matching on such a path says nothing about where it resolves: my-group/../evil/ci starts with my-group but is not in it.
What is not flagged
- No-op when unset — without
allowed_include_sources, nothing is flagged. include:localandinclude:template— in-repo and GitLab-provided sources, not external.- Variable-expanded sources (
project: $TEMPLATE_PROJECT,remote: https://$HOST/ci.yml) cannot be resolved statically. The$CI_SERVER_FQDN/$CI_SERVER_HOSTcomponent prefix is the exception, see above.
Safe alternative
Pull pipeline configuration only from namespaces, components, and hosts you control or trust, and list them in allowed_include_sources.
References
- GitLab:
include:project - GitLab:
include:component - GitLab:
include:remote - GL003:
include:with a mutable or missing ref · GL065:allowed_registries(sibling allowlist)
Documentation for glsec 1.23.0. Source of this page · Rule implementation