Skip to content

fix: escape control characters when substituting multiline params - #2103

Open
pujitha24 wants to merge 1 commit into
tektoncd:mainfrom
pujitha24:auto/issue-1782
Open

fix: escape control characters when substituting multiline params#2103
pujitha24 wants to merge 1 commit into
tektoncd:mainfrom
pujitha24:auto/issue-1782

Conversation

@pujitha24

Copy link
Copy Markdown
Contributor

Changes

Fixes a bug where TriggerBinding param values containing literal control characters (e.g. newlines from a YAML | block scalar) broke resource creation with a couldn't unmarshal json from the TriggerTemplate: invalid character '\n' in string literal error.

applyParamToResourceTemplate substituted $(tt.params.NAME) tokens via a plain bytes.ReplaceAll with no escaping of control characters, which is invalid inside a JSON string literal per RFC 8259.

This replaces the per-param bytes.ReplaceAll with a single linear scan over the resource template (substituteParamsInResourceTemplate) that tracks whether the current position is inside a JSON string literal via a minimal quote/backslash-escape tracker. Substitutions inside a string literal now escape control characters (\n, \r, \t, and other bytes < 0x20) to valid JSON escape sequences via a new escapeJSONControlChars helper. Substitutions outside a string literal (e.g. raw JSON blobs) are left untouched, preserving prior intentional behavior. All params are now substituted in one pass over the original template bytes rather than one pass per param, since re-scanning already-substituted output per param could let an earlier param's unescaped quote desync the string-boundary tracking for a later param.

Quote and backslash pass-through behavior (default, and under the triggers.tekton.dev/old-escape-quotes annotation) is unchanged, as is the pre-existing tradeoff where a raw unescaped quote in a param value can still produce invalid JSON.

Submitter Checklist

As the author of this PR, please check off the items in this checklist:

  • Has Docs if any changes are user facing, including updates to minimum requirements e.g. Kubernetes version bumps
  • Has Tests included if any functionality added or changed
  • Follows the commit message standard
  • Meets the Tekton contributor standards (including functionality, content, code)
  • Has a kind label. You can add one by adding a comment on this PR that contains /kind <type>. Valid types are bug, cleanup, design, documentation, feature, flake, misc, question, tep
  • Release notes block below has been updated with any user facing changes (API changes, bug fixes, changes requiring upgrade notices or deprecation warnings). See some examples of good release notes.
  • Release notes contains the string "action required" if the change requires additional action from users switching to the new release

Release Notes

Fix a bug where TriggerBinding param values containing literal control characters (e.g. newlines) could produce invalid JSON when substituted into a TriggerTemplate, causing resource creation to fail.

AI assistance: this change was drafted with Claude Code.

Fixes #1782

Motivation:
TriggerBinding param values containing literal newlines (e.g. from a
YAML `|` block scalar) broke resource creation with "couldn't
unmarshal json from the TriggerTemplate: invalid character '\n' in
string literal". applyParamToResourceTemplate substituted
$(tt.params.NAME) tokens with the raw param value via a plain
bytes.ReplaceAll, with no escaping of control characters. A literal
newline (or tab, carriage return, etc.) landing inside a JSON string
literal is invalid per RFC 8259, regardless of where the value came
from.

Approach:
Replace the per-param bytes.ReplaceAll with a single linear scan over
the resource template that tracks whether the current position is
inside a JSON string literal (a minimal quote/backslash-escape
tracker). When a $(tt.params.NAME) token is found inside a string, its
value is substituted with control characters (\n, \r, \t, and other
bytes < 0x20) escaped to valid JSON escape sequences via a new
escapeJSONControlChars helper. Outside of a string (e.g. a raw JSON
object substituted unquoted), the value is left untouched, preserving
the behavior intentionally introduced by a prior PR that dropped
automatic quote-escaping so raw JSON blobs could be embedded directly.
Quote and backslash pass-through (both by default and under the
triggers.tekton.dev/old-escape-quotes annotation) is unchanged.

All params are now substituted together in one pass over the original
template bytes (substituteParamsInResourceTemplate), rather than one
pass per param feeding into the next. An earlier iteration of this fix
ran one bytes.ReplaceAll-style pass per param; re-scanning the
already-substituted output for each subsequent param let an earlier
param's raw, unescaped quote (still pass-through by design) desync the
string-boundary tracking used to decide whether a later param's
control characters needed escaping, which could silently reproduce the
original bug depending on param ordering. Scanning the original
template exactly once for all params avoids this, since no
substituted value is ever fed back through the tracker.

This does not change the pre-existing behavior where a raw, unescaped
quote in a param value can still produce structurally invalid JSON
(default behavior since the quote-escaping removal); that is a
separate, previously-accepted design tradeoff, not something this fix
alters.

Validation:
- go build ./...
- go test ./... (full repo; all packages pass, no failures)
- gofmt -l pkg/template/resource.go pkg/template/resource_test.go (clean)
- make golangci-lint (3 pre-existing issues remain in pkg/sink/sink.go
  and pkg/bootstrap/file_helper.go, confirmed present without this
  change via git stash; zero issues in the changed files)
- Added unit tests in pkg/template/resource_test.go reproducing the
  exact multiline-value-in-a-script-field scenario from the report,
  an unquoted raw-JSON-substitution case confirming that behavior is
  unchanged, and a multi-param case confirming an earlier param's
  unescaped quote no longer affects a later param's control-character
  escaping.

Report: tektoncd#1782
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@tekton-robot tekton-robot added the release-note Denotes a PR that will be considered when it comes time to generate release notes. label Aug 14, 2026
@tekton-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
To complete the pull request process, please assign khrm after the PR has been reviewed.
You can assign the PR to them by writing /assign @khrm in a comment when ready.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tekton-robot tekton-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 14, 2026
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 32.40%. Comparing base (ee4bfd3) to head (b4b4a7d).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2103      +/-   ##
==========================================
+ Coverage   32.17%   32.40%   +0.22%     
==========================================
  Files         260      260              
  Lines       11979    12030      +51     
==========================================
+ Hits         3854     3898      +44     
- Misses       7800     7806       +6     
- Partials      325      326       +1     
Flag Coverage Δ
unit-tests 32.40% <ø> (+0.22%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pujitha24

Copy link
Copy Markdown
Contributor Author

/assign @khrm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

triggerbinding stopped supporting multiline arguments

3 participants