Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions github-actions/previews/upload-artifacts-to-firebase/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,49 @@ runs:
# RISK: The downloaded `unsafe-artifact` is of input category `RISK`.
- name: Extracting workflow artifact into Firebase public directory.
shell: bash
# Pass the input via the environment rather than interpolating it into the
# script body, so its value can never be parsed as part of the shell script.
env:
FIREBASE_PUBLIC_DIR: ${{inputs.firebase-public-dir}}
run: |
set -euo pipefail

extractDir="$RUNNER_TEMP/artifact-unpack"
publicDir="$FIREBASE_PUBLIC_DIR"

mkdir -p '${{inputs.firebase-public-dir}}'
mkdir -p "$publicDir"
mkdir -p "$extractDir"

unzip unsafe-artifact.zip -d "$extractDir"
tar -xvzf "$extractDir/deploy-artifact.tar.gz" -C '${{inputs.firebase-public-dir}}'
# No `-v`: the archive is attacker-influenced (PR-supplied) and a crafted
# entry name containing a newline + `::workflow-command::` could otherwise
# be echoed to stdout during extraction and interpreted by the runner.
tar -xzf "$extractDir/deploy-artifact.tar.gz" -C "$publicDir"

# Defense-in-depth: fail the deploy if the extracted artifact contains
# symlinks. The artifact is attacker-influenced (PR-supplied build
# output); a symlink such as `public/leak -> /proc/self/environ` or
# `public/leak -> ~/.config/gcloud/application_default_credentials.json`
# would otherwise be followed by downstream tooling that walks
# `firebase-public-dir` and reads each entry, ending up on the
# public Firebase Hosting CDN. There is no legitimate reason for a
# `public` artifact destined for static hosting to contain
# symlinks, so refusing them outright is the safest default.
# See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
#
# `find` is not suffixed with `|| true` / `2>/dev/null`: under `set -e`
# any failure to complete the scan must fail the step (fail closed)
# rather than silently treating the artifact as symlink-free.
symlinks=$(find "$publicDir" -type l)
if [ -n "$symlinks" ]; then
echo "::error title=Symlinks rejected in artifact::The deploy artifact contains symlinks, which are not permitted in a Hosting public directory for security reasons (a symlink can leak the contents of files outside the source tree onto the public Firebase Hosting CDN)."
echo "Symlinks found:"
# Indent so a malicious filename containing a newline cannot start a
# line with `::` and be interpreted as a workflow command; `printf`
# is used instead of `echo` for safe handling of arbitrary strings.
printf '%s\n' "$symlinks" | sed 's/^/ /'
exit 1
fi

- name: Extracting artifact metadata
id: artifact-info
Expand Down
Loading