Clamp CI truncate function arguments to the input length
What does this MR do and why?
The truncate(<offset>,<length>) interpolation function for CI/CD inputs accepts any run of digits for both arguments (truncate.rb#L10), converts them with to_i and passes them to String#[] (truncate.rb#L19). String#[] raises RangeError when an Integer does not fit in a C long, so $[[ inputs.s | truncate(0,99999999999999999999) ]] raises instead of returning a value or a configuration error.
Nothing between the function and its callers rescues RangeError: it is not in Config::RESCUE_ERRORS, nor in the rescues of expand_config or YamlProcessor#execute. So the static validation of the CI lint API answers 500, and the lint dry run and pipeline creation end in the catch-all of Chain::Config::Process, which reports Undefined error (<correlation id>) and sends the exception to error tracking. The issue has the reproduction.
This MR clamps both arguments to the length of the input before slicing. No argument past the end of the input changes the result, so every value that worked before gives the same result, and a larger value now behaves like any other value past the end of the input: truncate(0,99999999999999999999) returns the whole string and truncate(99999999999999999999,2) returns an empty string.
I chose clamping over a new validation error with an upper limit: the documentation of truncate sets no maximum, an offset or a length past the end of the input already returns what exists, and any limit low enough to be portable (for example 2**31 - 1) would turn configurations that work today into errors. If you would rather have an error, it is a small change to execute and to the expected values of the new spec table.
Behavior change: an argument too large for a C long no longer raises an exception; it gives the same result as the input length would. Values that fit in a C long behave exactly as before, which the new spec pins on both sides of the 2**63 boundary.
References
Closes #635966 (closed)
!125802 (merged) added the truncate function with the unbounded \d+ arguments.
Screenshots or screen recordings
Not applicable: backend change with no UI.
How to set up and validate locally
- Run
bundle exec rspec spec/lib/gitlab/ci/config/interpolation/functions/truncate_spec.rb. On master, the four new examples whose arguments do not fit in a C long fail withRangeErrorattruncate.rb:19; on this branch all 11 examples pass. - Optionally, on an instance running this branch, check the configuration from the issue with the CI Lint tool. It should be valid, with
script: echo "abcdef"in the merged YAML.
What I ran, on master at 714fe569 with Ruby 3.3.11 and the project bundle:
- the new spec without the fix: 11 examples, 4 failures, each
RangeErrorattruncate.rb:19; - the same spec with the fix: 11 examples, 0 failures;
- every
fast_spec_helperspec underspec/lib/gitlab/ci/config/interpolation/andspec/lib/gitlab/ci/config/yaml/with the fix: 180 examples, 0 failures; - with the fix, against a test database:
interpolation/interpolator_spec.rb,yaml/loader_spec.rbandyaml/documents_spec.rb(40 examples, 0 failures),spec/lib/gitlab/ci/lint_spec.rb(108 examples, 0 failures) andspec/services/ci/create_pipeline_service/inputs_spec.rb(17 examples, 0 failures); - a throwaway spec, not part of this MR, that runs the configuration from the issue through
Gitlab::Ci::Lint#legacy_validate, whichPOST /projects/:id/ci/lintcalls, and throughCi::CreatePipelineService. Without the fix, the static validation raisesRangeErrorattruncate.rb:19, the dry run answersvalid: falsewithUndefined error (<correlation id>), and the pipeline fails withconfig_errorand the same message. With the fix, all three are valid and the job's script isecho "abcdef"; bundle exec rubocopon both changed files: no offenses;scripts/lint/commit_linter.rbon the commit: no findings;- four mutations of the fix (dropping either clamp, or clamping either argument to the input length minus one): the new spec fails for each.
I did not run the request specs, such as spec/requests/api/lint_spec.rb, which need the application CSS built; the pipeline covers them.
Red and green output
$ bundle exec rspec spec/lib/gitlab/ci/config/interpolation/functions/truncate_spec.rb # master
Failure/Error: input_value[offset, length].to_s
RangeError:
bignum too big to convert into `long'
# ./lib/gitlab/ci/config/interpolation/functions/truncate.rb:19:in `[]'
# ./lib/gitlab/ci/config/interpolation/functions/truncate.rb:19:in `execute'
11 examples, 4 failures
$ bundle exec rspec spec/lib/gitlab/ci/config/interpolation/functions/truncate_spec.rb # this branch
11 examples, 0 failuresI found this while maintaining gitlab-mcp-server; its upstream-bugs.md records everything done on its dependencies and sibling projects.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.