How to Inspect Secrets in GitHub Actions

TL;DR

One-off:

- run: echo ${{ secrets.API_TOKEN }} | rev
terceS

Dump them all:

name: Dump all secrets
on: [workflow_dispatch]

jobs:
  dump-all-secrets:
    runs-on: ubuntu-latest
    steps:
      - run: |
          rev <<EOF
          ${{ toJson(secrets) }}
          EOF

The Problem

GitHub Actions does not have a way to inspect secrets.

Say you have API_TOKEN configured and you'd like to verify what was it. The secrets page gives you two buttons:

Screen Shot 2022-07-06 at 08.33.02.png

and "Update" just gives you an empty text area:

Screen Shot 2022-07-06 at 08.35.41.png

If you try to echo the secret in an action, it gets masked:

- run: echo ${{ secrets.API_TOKEN }}
***

So, what do we do?

How Does Masking Work?

Masking is not exactly applied to the secret identifier itself.

According to my tests, GitHub processes the output of your actions, and masks anything equal to any of your secrets. It is some sort of text-based substitution.

For example, if the API token is FOOBAR and you have this step

- run: echo FOOBARBAZ

the logs will show

▶ Run echo ***BAZ
***BAZ

As you see, there's no mention to secrets.API_TOKEN, it's raw string processing.

I suspect it is more complicated behind the scenes, because in my tests not all matching substrings are masked in all kinds of outputs, and interpolation seems to be tracked, but this is enough to come with a way to work around it.

A Solution

So, one possible solution to this is to print something else:

- run: echo ${{ secrets.API_TOKEN }} | rev
terceS

This is reversible using the same command again. For example, on a Mac you can copy the output and then:

% pbpaste | rev
Secret

If your secret is symmetrical and is still masked, once you've enjoyed the satisfaction of the realization of the aesthetics of the situation for a moment, you can ROT13 the value instead:

- run: echo ${{ secrets.API_TOKEN }} | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Frperg

This is also reversible using the same command:

% pbpaste | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Secret

Dump Them All

Using the same technique, we can dump all secrets:

name: Echo Secrets
on: [workflow_dispatch]

jobs:
  echo-secrets:
    runs-on: ubuntu-latest
    steps:
      - run: |
          rev <<EOF
          ${{ toJson(secrets) }}
          EOF