From 157954834d558ba1fb6c220014fe945943099176 Mon Sep 17 00:00:00 2001 From: "Alan (Maciej) Paruszewski" Date: Wed, 3 Dec 2025 10:15:50 +0100 Subject: [PATCH 1/3] Apply variables_override to dotenv artifacts This change ensures that dotenv artifact variables respect the variables_override policy settings in Pipeline Execution Policies. Previously, variables from dotenv artifacts could bypass the variables_override restrictions, allowing them to override policy-protected variables even when variables_override.allowed was set to false. This fix applies the same VariablesOverride logic that is used for other variable sources to dotenv variables as well. Changelog: fixed EE: true --- app/models/ci/processable.rb | 2 + ee/app/models/ee/ci/processable.rb | 19 ++ .../variables_override.rb | 9 +- ee/spec/models/ee/ci/processable_spec.rb | 169 ++++++++++++++++++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 ee/app/models/ee/ci/processable.rb create mode 100644 ee/spec/models/ee/ci/processable_spec.rb diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb index 1866d24b44ee8e..4ad681d395b048 100644 --- a/app/models/ci/processable.rb +++ b/app/models/ci/processable.rb @@ -361,3 +361,5 @@ def dependencies end end end + +Ci::Processable.prepend_mod_with('Ci::Processable') diff --git a/ee/app/models/ee/ci/processable.rb b/ee/app/models/ee/ci/processable.rb new file mode 100644 index 00000000000000..1aae072037c638 --- /dev/null +++ b/ee/app/models/ee/ci/processable.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module EE + module Ci + module Processable + extend ActiveSupport::Concern + extend ::Gitlab::Utils::Override + + override :dependency_variables + def dependency_variables + dotenv_variables = super + + ::Security::PipelineExecutionPolicy::VariablesOverride + .new(project: project, job_options: options) + .apply_variables_override(dotenv_variables, source: :dotenv) + end + end + end +end diff --git a/ee/app/models/security/pipeline_execution_policy/variables_override.rb b/ee/app/models/security/pipeline_execution_policy/variables_override.rb index a766eec1690eef..a832706fd4081c 100644 --- a/ee/app/models/security/pipeline_execution_policy/variables_override.rb +++ b/ee/app/models/security/pipeline_execution_policy/variables_override.rb @@ -17,8 +17,9 @@ def apply_highest_precedence(variables, yaml_variables) variables.reject { |var| yaml_variable_keys.include?(var.key) }.concat(yaml_variables) end - def apply_variables_override(variables) + def apply_variables_override(variables, source: nil) return variables unless apply_variables_override? + return variables if source == :dotenv && !respect_dotenv_policy? if override_settings[:allowed] override_with_denylist(variables) @@ -59,6 +60,12 @@ def apply_variables_override? policy_job? && override_settings end + def respect_dotenv_policy? + # Default to respecting policy (new behavior) + # Customers can opt-out by setting dotenv: allow_override + override_settings[:dotenv] != 'allow_override' + end + # allowed:true + exceptions: [...] def override_with_denylist(variables) return variables if exceptions.blank? diff --git a/ee/spec/models/ee/ci/processable_spec.rb b/ee/spec/models/ee/ci/processable_spec.rb new file mode 100644 index 00000000000000..0e881b9dbb47c4 --- /dev/null +++ b/ee/spec/models/ee/ci/processable_spec.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::Processable, feature_category: :continuous_integration do + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + + describe '#dependency_variables' do + let_it_be(:prepare_job) { create(:ci_build, pipeline: pipeline, stage_idx: 0, name: 'prepare') } + let_it_be(:policy_job) { create(:ci_build, pipeline: pipeline, stage_idx: 1, name: 'policy_job') } + + let!(:job_artifact) { create(:ci_job_artifact, :dotenv, job: prepare_job) } + let!(:dotenv_variable) do + create(:ci_job_variable, :dotenv_source, job: prepare_job, key: 'PROTECTED_VAR', value: 'dotenv_value') + end + + subject(:dependency_variables_result) { policy_job.dependency_variables } + + before do + create(:ci_build_metadata, build: policy_job) + policy_job.reload.metadata.update!(config_options: { dependencies: [prepare_job.name] }.merge(policy_options)) + policy_job.reload # Reload to clear any memoized values + end + + context 'when job is not a policy job' do + let(:policy_options) { {} } + + it 'returns dotenv variables without filtering' do + expect(dependency_variables_result.to_runner_variables).to contain_exactly( + { key: 'PROTECTED_VAR', value: 'dotenv_value', public: false, masked: false } + ) + end + end + + context 'when job is a policy job with variables_override' do + context 'when variables_override.allowed is false' do + let(:policy_options) do + { + policy: { + variables_override: { + allowed: false, + exceptions: [] + } + } + } + end + + it 'filters out all dotenv variables' do + expect(dependency_variables_result.to_runner_variables).to be_empty + end + + context 'when variable is in exceptions list' do + let(:policy_options) do + { + policy: { + variables_override: { + allowed: false, + exceptions: ['PROTECTED_VAR'] + } + } + } + end + + it 'allows the excepted variable' do + expect(dependency_variables_result.to_runner_variables).to contain_exactly( + { key: 'PROTECTED_VAR', value: 'dotenv_value', public: false, masked: false } + ) + end + end + end + + context 'when variables_override.allowed is true' do + let(:policy_options) do + { + policy: { + variables_override: { + allowed: true, + exceptions: [] + } + } + } + end + + it 'returns all dotenv variables' do + expect(dependency_variables_result.to_runner_variables).to contain_exactly( + { key: 'PROTECTED_VAR', value: 'dotenv_value', public: false, masked: false } + ) + end + + context 'when variable is in exceptions list (denylist)' do + let(:policy_options) do + { + policy: { + variables_override: { + allowed: true, + exceptions: ['PROTECTED_VAR'] + } + } + } + end + + it 'filters out the denied variable' do + expect(dependency_variables_result.size).to eq(0) + end + end + + context 'when dotenv: allow_override is set' do + let(:policy_options) do + { + policy: { + variables_override: { + allowed: true, + exceptions: ['PROTECTED_VAR'], + dotenv: 'allow_override' + } + } + } + end + + it 'allows all dotenv variables to bypass policy restrictions' do + expect(dependency_variables_result.to_runner_variables).to contain_exactly( + { key: 'PROTECTED_VAR', value: 'dotenv_value', public: false, masked: false } + ) + end + end + end + end + + context 'when job uses legacy execution_policy_job format' do + let(:policy_options) do + { + execution_policy_job: true, + execution_policy_variables_override: { + allowed: false, + exceptions: [] + } + } + end + + it 'filters out dotenv variables using legacy format' do + expect(dependency_variables_result.size).to eq(0) + end + end + + context 'with multiple dotenv variables' do + let!(:dotenv_variable2) do + create(:ci_job_variable, :dotenv_source, job: prepare_job, key: 'ANOTHER_VAR', value: 'another_value') + end + + let(:policy_options) do + { + policy: { + variables_override: { + allowed: false, + exceptions: ['PROTECTED_VAR'] + } + } + } + end + + it 'only allows excepted variables' do + expect(dependency_variables_result.to_runner_variables).to contain_exactly( + { key: 'PROTECTED_VAR', value: 'dotenv_value', public: false, masked: false } + ) + end + end + end +end -- GitLab From 21f71ae2e238d906c7957358d7cffc916aeb139c Mon Sep 17 00:00:00 2001 From: "Alan (Maciej) Paruszewski" Date: Tue, 16 Dec 2025 11:29:02 +0100 Subject: [PATCH 2/3] Add dotenv option to variables_override JSON schema Add the new `dotenv` configuration option to the pipeline execution policy JSON schema to support the new dotenv artifact variable override behavior introduced in this MR. The `dotenv` option allows customers to control whether dotenv variables should respect the variables_override policy rules: - `respect_policy` (default): Dotenv variables respect policy rules - `allow_override`: Dotenv variables bypass policy rules (legacy behavior) --- .../json_schemas/pipeline_execution_policy_content.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ee/app/validators/json_schemas/pipeline_execution_policy_content.json b/ee/app/validators/json_schemas/pipeline_execution_policy_content.json index 22b34c2c82a403..419d0cc0f572c4 100644 --- a/ee/app/validators/json_schemas/pipeline_execution_policy_content.json +++ b/ee/app/validators/json_schemas/pipeline_execution_policy_content.json @@ -106,6 +106,14 @@ "items": { "type": "string" } + }, + "dotenv": { + "type": "string", + "description": "Defines whether dotenv artifact variables should respect the variables_override policy rules. `respect_policy` (default) applies the policy rules to dotenv variables. `allow_override` allows dotenv variables to bypass policy rules for backward compatibility.", + "enum": [ + "respect_policy", + "allow_override" + ] } }, "required": [ -- GitLab From e3edc406d8e803692697e60b7bd51cf8d6bc58db Mon Sep 17 00:00:00 2001 From: "Alan (Maciej) Paruszewski" Date: Tue, 16 Dec 2025 11:33:16 +0100 Subject: [PATCH 3/3] Add dotenv option to variables_override in security_orchestration_policy schema Update the security orchestration policy JSON schema to include the new `dotenv` configuration option in the `variables_override` section for pipeline execution policies. This ensures consistency across all policy schema files and allows proper validation of the dotenv behavior configuration. --- .../json_schemas/security_orchestration_policy.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ee/app/validators/json_schemas/security_orchestration_policy.json b/ee/app/validators/json_schemas/security_orchestration_policy.json index 785013f42d4405..dcbdd022b15a0c 100644 --- a/ee/app/validators/json_schemas/security_orchestration_policy.json +++ b/ee/app/validators/json_schemas/security_orchestration_policy.json @@ -347,6 +347,14 @@ "items": { "type": "string" } + }, + "dotenv": { + "type": "string", + "description": "Defines whether dotenv artifact variables should respect the variables_override policy rules. `respect_policy` (default) applies the policy rules to dotenv variables. `allow_override` allows dotenv variables to bypass policy rules for backward compatibility.", + "enum": [ + "respect_policy", + "allow_override" + ] } }, "required": [ -- GitLab