Skip to content

Commit b2c8af9

Browse files
JamesMGreenemikesurowiecrsese
authored
Secure early access staging deployment (github#21450)
* Explicitly ensure the early access parent directories are created when cloning * Use explicit --file flag with tar * Remove security hole for Staging deployment by concatenating archives * Fail the staging builds if *.js, .npmrc, or Procfile is changed in the open source repo * docker build: extract user-code in separate directory * Checkout PR base branch and install dependencies * Remove one-off package installs * Remove selective file checkout * Don't persist git cloning credentials It usually makes the clone of early access content fail in the later steps * Update .github/workflows/staging-deploy-pr-docker.yml Co-authored-by: James M. Greene <[email protected]> * Update .github/workflows/staging-deploy-pr-docker.yml Co-authored-by: James M. Greene <[email protected]> * Remove debugging step * Best practice: Use environment variables to avoid potential injection attacks if the data was user-controlled Co-authored-by: Mike Surowiec <[email protected]> Co-authored-by: Robert Sese <[email protected]> Co-authored-by: Robert Sese <[email protected]>
1 parent fce5fe2 commit b2c8af9

File tree

5 files changed

+119
-84
lines changed

5 files changed

+119
-84
lines changed

.github/workflows/staging-build-pr-docker.yml

+6-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333

3434
# Make sure only approved files are changed if it's in github/docs
3535
- name: Check changed files
36-
if: github.repository == 'github/docs' && github.event.pull_request.user.login != 'Octomerger'
36+
if: ${{ github.repository == 'github/docs' && github.event.pull_request.user.login != 'Octomerger' }}
3737
uses: dorny/paths-filter@eb75a1edc117d3756a18ef89958ee59f9500ba58
3838
id: filter
3939
with:
@@ -47,20 +47,23 @@ jobs:
4747
# Returns list of changed files matching each filter
4848
filters: |
4949
notAllowed:
50+
- '*.js'
5051
- '*.mjs'
5152
- '*.ts'
5253
- '*.tsx'
5354
- '*.json'
55+
- '.npmrc'
56+
- 'script/**'
5457
- 'Dockerfile*'
5558
5659
# When there are changes to files we can't accept
57-
- name: 'Fail when not allowed files are changed'
60+
- name: Fail when disallowed files are changed
5861
if: ${{ steps.filter.outputs.notAllowed == 'true' }}
5962
run: exit 1
6063

6164
- name: Create an archive
6265
run: |
63-
tar -cf app.tar \
66+
tar -c --file=app.tar \
6467
assets/ \
6568
content/ \
6669
stylesheets/ \

.github/workflows/staging-build-pr.yml

+31-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,36 @@ jobs:
2626
- name: Check out repo
2727
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
2828

29+
# Make sure only approved files are changed if it's in github/docs
30+
- name: Check changed files
31+
if: ${{ github.repository == 'github/docs' && github.event.pull_request.user.login != 'Octomerger' }}
32+
uses: dorny/paths-filter@eb75a1edc117d3756a18ef89958ee59f9500ba58
33+
id: filter
34+
with:
35+
# Base branch used to get changed files
36+
base: 'main'
37+
38+
# Enables setting an output in the format in `${FILTER_NAME}_files
39+
# with the names of the matching files formatted as JSON array
40+
list-files: json
41+
42+
# Returns list of changed files matching each filter
43+
filters: |
44+
notAllowed:
45+
- '*.js'
46+
- '*.mjs'
47+
- '*.ts'
48+
- '*.tsx'
49+
- '*.json'
50+
- '.npmrc'
51+
- 'script/**'
52+
- 'Procfile'
53+
54+
# When there are changes to files we can't accept
55+
- name: Fail when disallowed files are changed
56+
if: ${{ steps.filter.outputs.notAllowed == 'true' }}
57+
run: exit 1
58+
2959
- name: Setup node
3060
uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f
3161
with:
@@ -53,7 +83,7 @@ jobs:
5383

5484
- name: Create an archive
5585
run: |
56-
tar -cf app.tar \
86+
tar -c --file=app.tar \
5787
node_modules/ \
5888
.next/ \
5989
assets/ \

.github/workflows/staging-deploy-pr-docker.yml

+33-37
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ permissions:
1919
statuses: write
2020

2121
env:
22+
EARLY_ACCESS_SCRIPT_PATH: script/early-access/clone-for-build.js
23+
EARLY_ACCESS_SUPPORT_FILES: script/package.json
2224
# In this specific workflow relationship, the `github.event.workflow_run.pull_requests`
2325
# array will always contain only 1 item! Specifically, it will contain the PR associated
2426
# with the `github.event.workflow_run.head_branch` that triggered the preceding
@@ -90,56 +92,49 @@ jobs:
9092
app_name: ${{ steps.create-app.outputs.app_name}}
9193
docker_image_id: ${{ steps.image-id.outputs.image_id}}
9294
steps:
93-
- name: Dump event context
94-
env:
95-
GITHUB_EVENT_CONTEXT: ${{ toJSON(github.event) }}
96-
run: echo "$GITHUB_EVENT_CONTEXT"
97-
9895
- name: Check out repo's default branch
9996
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
10097
with:
98+
# For enhanced security: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
10199
persist-credentials: 'false'
102100

103-
- name: Download build artifact
104-
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74
105-
with:
106-
workflow: ${{ github.event.workflow_run.workflow_id }}
107-
run_id: ${{ github.event.workflow_run.id }}
108-
name: pr_build_docker
109-
path: ./
110-
111-
- name: Show contents
112-
run: ls -l
113-
114-
- name: Extract the archive
115-
run: |
116-
tar -xf app.tar -C ./
117-
rm app.tar
118-
119-
- name: Show contents again
120-
run: ls -l
121-
122-
- if: ${{ github.repository == 'github/docs-internal' }}
123-
name: Setup node to clone early access
101+
- name: Setup node
124102
uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f
125103
with:
126104
node-version: 16.8.x
127105
cache: npm
128106

129-
# Add any dependencies that are needed for this workflow below
130-
- if: ${{ github.repository == 'github/docs-internal' }}
131-
name: Install temporary development-only dependencies
132-
run: npm install --no-save rimraf dotenv
107+
- name: Install dependencies
108+
run: npm ci
133109

134110
- if: ${{ github.repository == 'github/docs-internal' }}
135111
name: Clone early access
136-
run: node script/early-access/clone-for-build.js
112+
run: node ${{ env.EARLY_ACCESS_SCRIPT_PATH }}
137113
env:
138114
DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }}
139115
GIT_BRANCH: ${{ github.event.workflow_run.head_branch }}
140116

141-
- name: Install one-off development-only dependencies
142-
run: npm install --no-save --include=optional esm
117+
- if: ${{ github.repository == 'github/docs-internal' }}
118+
name: Create an archive for early access
119+
run: |
120+
tar -c --file=early-access.tar \
121+
assets/ \
122+
content/ \
123+
data/
124+
125+
# Download the previously built "app.tar"
126+
- name: Download build artifact
127+
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74
128+
with:
129+
workflow: ${{ github.event.workflow_run.workflow_id }}
130+
run_id: ${{ github.event.workflow_run.id }}
131+
name: pr_build
132+
path: ./
133+
134+
# Append "early-access.tar" into "app.tar"
135+
- if: ${{ github.repository == 'github/docs-internal' }}
136+
name: Concatenate the archives
137+
run: tar -A --file=app.tar early-access.tar
143138

144139
- name: Create app
145140
id: create-app
@@ -182,9 +177,13 @@ jobs:
182177
throw(err)
183178
}
184179
180+
- name: Extract user-changes to tmp directory
181+
run: |
182+
tar -x --file=app.tar -C ${{ runner.temp }}/
183+
185184
- name: Build, tag, push, and release the Docker image
186185
run: |
187-
docker image build --target production_early_access -t registry.heroku.com/${{ steps.create-app.outputs.app_name}}/web .
186+
docker image build -f ${{ runner.temp }}/Dockerfile --target production_early_access -t registry.heroku.com/${{ steps.create-app.outputs.app_name}}/web ${{ runner.temp }}
188187
heroku container:login
189188
docker push registry.heroku.com/${{ steps.create-app.outputs.app_name }}/web
190189
heroku container:release web --app=${{ steps.create-app.outputs.app_name }}
@@ -202,9 +201,6 @@ jobs:
202201
echo "::set-output name=image_id::$(docker image inspect registry.heroku.com/${{ steps.create-app.outputs.app_name }}/web --format={{.Id}})"
203202
exit 1 # Stop at this point, don't move on to prepare job
204203
205-
# TODO - heroku stuff
206-
# - create a release based on the image
207-
208204
- name: Send Slack notification if workflow fails
209205
uses: someimportantcompany/github-actions-slack-message@0b470c14b39da4260ed9e3f9a4f1298a74ccdefd
210206
if: ${{ failure() }}

0 commit comments

Comments
 (0)