actions / runner

The Runner for GitHub Actions :rocket:
https://github.com/features/actions
MIT License
4.74k stars 922 forks source link

add-mask doesn't work with workflow_dispatch inputs #643

Open rkuhlercadent opened 4 years ago

rkuhlercadent commented 4 years ago

Describe the bug Github actions workflow with inputs cannot be masked using add-mask.

To Reproduce

  1. Create workflow
name: add-mask-test
on: 
  workflow_dispatch:
    inputs:
      secret:
        description: 'secret value'
        required: true
jobs:
  my-job:
    runs-on: ubuntu-latest
    steps:
      - name: add-mask test
        run: |
          echo "::add-mask::${{ github.event.inputs.secret }}"
  1. Run workflow entering secret value "password" as input
  2. Look at workflow log and see value "password" appears twice without masking

Expected behavior The value in add-mask does not appear at all in the workflow log output

Runner Version and Platform

Current runner version: '2.272.0' Operating System Ubuntu 18.04.4 LTS

What's not working?

The value in add-mask appears twice without masking

Job Log Output

add-mask test shell: /bin/bash -e {0} Run echo "::add-mask::password" echo "::add-mask::password" shell: /bin/bash -e {0}

radeva commented 3 years ago

I also confirm the bug.

A possible solution to this could be the workflowdispatch inputs to be enhanced with INPUT* environment variables as it is described in the documentation. This is also discussed in the Github Community.

Provided the env variables are automatically set, we can then use echo "::add-mask::$INPUT_MYVAR" and the actual value will not be exposed in the logs.

scordio commented 3 years ago

Seems to be related to #475.

TomaszKlosinski commented 3 years ago

Any updates on this? This is a major blocker for my company. We planned to use the input parameter as passphrase to decrypt files needed in the workflow that are user-specific.

marble-sh commented 3 years ago

My use case is slightly different but I think my workaround will work for others as well.

curl would produce output that contained secret data; but it was data i needed to parse with jq or fromJson. add-mask wouldn't work just the same as everyone else here.

my workaround was to write to a file and just read from that for future actions:

      - name: auth
        run: curl -sL "https://.../licenseKey?licenseKey=${KEY}" -o /tmp/key.json
        env:
          KEY: ${{ secrets.KEY }}

and then later steps i can just get a value from the response like so. This'll be different depending on what you're parsing and how you need to pass the data between tasks.

        run: |
          idToken=$(jq -r '.idToken' /tmp/key.json)
          do_something_with $idToken

Doing it this way kept the output clean.

netzwirt commented 3 years ago

my workarround create a shell variable from your input:


        run: |
           MY_SECRET=$(cat $GITHUB_EVENT_PATH | jq '.inputs.secret' | sed 's/"//g' )
           echo "::add-mask::$MY_SECRET"
marble-sh commented 3 years ago

my workarround

Thank you! (MY_SECRET can be shortened to jq -r '.inputs.secret' $GITHUB_EVENT_PATH)

sshymko commented 3 years ago

For the record, inputs / environment variables following certain naming conventions are being masked automatically. Please reference the detailed description and code examples in the related issue https://github.com/actions/runner/issues/475#issuecomment-742271143.

mykhailo-inv-disco commented 3 years ago

Full example with inputs and outputs. Leaving for reference.

Inputs

Workflow file:

name: Test masking inputs
on:
  workflow_dispatch:
    inputs:
      secret:
        description: "secret value"
        required: true
      token:
        description: "token value"
        required: true
      secret_token:
        description: "secret_token value"
        required: true
jobs:
  test_masking_inputs:
    runs-on: ubuntu-20.04
    steps:
      - name: Test masking inputs
        id: add_mask
        run: |
          INP_SECRET=$(jq -r '.inputs.secret' $GITHUB_EVENT_PATH)
          INP_TOKEN=$(jq -r '.inputs.token' $GITHUB_EVENT_PATH)
          INP_SECRET_TOKEN=$(jq -r '.inputs.secret_token' $GITHUB_EVENT_PATH)
          echo Before mask
          echo $INP_SECRET
          echo $INP_TOKEN
          echo $INP_SECRET_TOKEN
          echo ::add-mask::$INP_SECRET
          echo ::add-mask::$INP_TOKEN
          echo ::add-mask::$INP_SECRET_TOKEN
          echo After mask
          echo $INP_SECRET
          echo $INP_TOKEN
          echo $INP_SECRET_TOKEN
          echo Setting output
          echo ::set-output name=secret::$INP_SECRET
          echo ::set-output name=token::$INP_TOKEN
          echo ::set-output name=secret_token::$INP_SECRET_TOKEN
          echo Setting environment variables
          echo SECRET="$INP_SECRET" >> $GITHUB_ENV
          echo TOKEN="$INP_TOKEN" >> $GITHUB_ENV
          echo SECRET_TOKEN="$INP_SECRET_TOKEN" >> $GITHUB_ENV

      - name: Check output from another step
        run: |
          echo "${{ steps.add_mask.outputs.secret }}"
          echo "${{ steps.add_mask.outputs.token }}"
          echo "${{ steps.add_mask.outputs.secret_token }}"

      - name: Check environment variables 1
        run: |
          echo "${{ env.SECRET }}"
          echo "${{ env.TOKEN }}"
          echo "${{ env.SECRET_TOKEN }}"

      - name: Check environment variables 2
        run: |
          echo $SECRET
          echo $TOKEN
          echo $SECRET_TOKEN

Output

Test masking inputs:

Before mask
(boo3)()
)wo%o()ho$o(
not_really..)(*^%%%%%%%^&*$
After mask
***
***
***
Setting output
Setting environment variables

Check output from another step (WRONG):

***
)wo%o()ho(
***

Check environment variables 1 (WRONG):

***
)wo%o()ho(
***

Check environment variables 2: (CORRECT?)

***
***
***

As I understand last case is the correct usage of masked input (use it as environment variable after placing it into GITHUB_ENV during add_mask step), as opposed to two previous steps where stars appear only because variable contains a SECRET substring in its name.

Forrin commented 2 years ago

The solutions that I'm seeing here result in the secret being written to event.json. I'm not entirely sure on the lifecycle of this file, but at least for a period of time your secret is written in plain text and possibly ends up in server logs. The event.json file is the POST payload that triggered the workflow.

An alternative solution would be to use a vault, such as HashiCorp Vault. Your input would be the secret id and the vault credentials would be stored as a repository secret (these are not sent as plain text, unlike using the input). Then, just make an API call to vault to retrieve the secret. Note that Github recommends registering retrieved secret values as a secret.

For more on hardening see the following: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-secrets and examples are here: https://github.com/actions/toolkit/tree/main/packages/core#setting-a-secret

nikola-jokic commented 2 years ago

Hi @mykhailo-inv-disco,

This is actually masked well, but the output you are seeing and labeled wrong is actually due to the command being executed. If you look at it, what it does is actually executing echo ")wo%o()ho$o(". The output of that would be of course )wo%o()ho( since it is inside the double quotes, and the $o would be used as a reference to a variable that is not set.

nikola-jokic commented 2 years ago

Hey everyone,

I labelled this issue as a feature request. Currently, we log everything that is executed, so using variables like in your workarounds is a good way to bypass this. We have added this issue to the enhancement backlog and it will be considered in the future 😊

sammcj commented 1 year ago

This is still an issue - I've recently found a large number of workflows that should have had their secrets masked as they were correctly using add-mask within the workflow - but because they were called with workflow-dispatch the values were being echoed to the log 🤦.

I'm aware that people probably shouldn't be entering things that might contain secrets as inputs - but we should be designing systems to be safe by default.

levibostian commented 1 year ago

This issue was the most helpful resource I could find on how to solve this problem. Thanks everyone!

Thanks to the comments in here, I was able to get this working in a repo of mine recently. But, the solution was boilerplate heavy and I felt it was fragile/error-prone. I was hoping for a better solution.

This is not meant to be a self-promotion, but I did want to share I just created a GitHub Action to help make this easy. I thought other frustrated folks in here would find an Action useful.

prateekg1703 commented 7 months ago

Hello everyone,

Going through all the solutions here, and I'm wondering if anyone has faced this problem: The masked inputs are still printed in the reusable workflow?

Masking works fine with the approaches mentioned here for workflow dispatch, however, if you are using a reusable workflow from the caller workflow, it prints all the inputs by default:

Screenshot 2024-01-04 at 18 09 23

It would have been ideal if there was a way to reset the inputs before they are passed to the reusable workflow or if we could limit the ones that are not required by the reusable workflow. I have tried several approaches but none seem to work. Any help is appreciated.

Macmee commented 6 months ago

@prateekg1703 this started happening to us too!

tuxillo commented 1 month ago

It's really concerning that really basic stuff like this isn't handled properly in Actions. For example, if the secret comes from another step's output, it will print the value every single time. It just replaces the ${{ steps.blah.outputs.out... }} with its value in the action. The only thing that comes to mind is that the previous action creates a file in RUNNER_TEMP or GITHUB_WORKSPACE to share with the next step. It's all just hackery and brittle as thin ice.

larmitage-bjss commented 1 month ago

Bumping this feature as it's a bit ridiculous that it's not currently possible Ideally we would have a secret input type that would do this automatically You can't even put the workaround into a reusable action for deduplication as the secret value will be visible in the logs when it's passed as input