Skip to content

Commit 91f6e80

Browse files
authored
Merge pull request #440 from posit-dev/security/injection-and-permissions
Fix script injection and add permissions to shared workflows
2 parents 685c624 + e0e838d commit 91f6e80

7 files changed

Lines changed: 238 additions & 104 deletions

File tree

.github/workflows/bakery-build-native.yml

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,18 @@ defaults:
7575
run:
7676
shell: bash
7777

78+
# Security policy: No ${{ }} expressions in `run:` blocks.
79+
# All expression values are assigned to `env:` and referenced as
80+
# shell variables. This prevents script injection from runtime values
81+
# (matrix outputs, secrets) and keeps the rule enforceable by zizmor
82+
# without per-expression exceptions.
83+
7884
jobs:
7985
matrix:
8086
name: Image Matrix
8187
runs-on: ubuntu-latest
88+
permissions:
89+
contents: read
8290
outputs:
8391
platform-matrix: ${{ steps.images-by-platform.outputs.platform_matrix }}
8492
versions-matrix: ${{ steps.images-by-version.outputs.versions_matrix }}
@@ -94,15 +102,26 @@ jobs:
94102

95103
- name: Images by Version/Platform
96104
id: images-by-platform
105+
env:
106+
DEV_VERSIONS: ${{ inputs.dev-versions }}
107+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
108+
CONTEXT: ${{ inputs.context }}
97109
run: |
98-
echo "platform_matrix=$(bakery ci matrix --quiet --dev-versions ${{ inputs.dev-versions }} --matrix-versions ${{ inputs.matrix-versions }} --context ${{ inputs.context }} | jq --compact-output .)" >> $GITHUB_OUTPUT
110+
echo "platform_matrix=$(bakery ci matrix --quiet --dev-versions "$DEV_VERSIONS" --matrix-versions "$MATRIX_VERSIONS" --context "$CONTEXT" | jq --compact-output .)" >> $GITHUB_OUTPUT
99111
- name: Images by Version
100112
id: images-by-version
113+
env:
114+
DEV_VERSIONS: ${{ inputs.dev-versions }}
115+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
116+
CONTEXT: ${{ inputs.context }}
101117
run: |
102-
echo "versions_matrix=$(bakery ci matrix --quiet --dev-versions ${{ inputs.dev-versions }} --matrix-versions ${{ inputs.matrix-versions }} --exclude platform --context ${{ inputs.context }} | jq --compact-output .)" >> $GITHUB_OUTPUT
118+
echo "versions_matrix=$(bakery ci matrix --quiet --dev-versions "$DEV_VERSIONS" --matrix-versions "$MATRIX_VERSIONS" --exclude platform --context "$CONTEXT" | jq --compact-output .)" >> $GITHUB_OUTPUT
103119
104120
build-test:
105121
name: "Build/Test ${{ matrix.img.image }}:${{ matrix.img.version }} (${{ matrix.img.platform }})"
122+
permissions:
123+
contents: read
124+
packages: write
106125
needs: matrix
107126
strategy:
108127
fail-fast: false
@@ -141,18 +160,12 @@ jobs:
141160
# this step sets an output that we can reference later.
142161
- name: Filter Steps
143162
id: filter-steps
163+
env:
164+
HAS_DOCKER_HUB: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN != '' }}
165+
HAS_AWS_ROLE: ${{ secrets.AWS_ROLE != '' }}
144166
run: |
145-
if [ -n "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" ] ; then
146-
echo "docker-hub=true" >> $GITHUB_OUTPUT
147-
else
148-
echo "docker-hub=false" >> $GITHUB_OUTPUT
149-
fi
150-
151-
if [ -n "${{ secrets.AWS_ROLE }}" ] ; then
152-
echo "ecr=true" >> $GITHUB_OUTPUT
153-
else
154-
echo "ecr=false" >> $GITHUB_OUTPUT
155-
fi
167+
echo "docker-hub=$HAS_DOCKER_HUB" >> $GITHUB_OUTPUT
168+
echo "ecr=$HAS_AWS_ROLE" >> $GITHUB_OUTPUT
156169
157170
- name: Login to GitHub Container Registry
158171
uses: docker/login-action@v4
@@ -190,35 +203,50 @@ jobs:
190203
- name: Build
191204
env:
192205
GIT_SHA: ${{ github.sha }}
206+
RETRY: ${{ inputs.retry }}
207+
IMAGE_NAME: ${{ matrix.img.image }}
208+
IMAGE_VERSION: ${{ matrix.img.version }}
209+
IMAGE_PLATFORM: ${{ matrix.img.platform }}
210+
DEV_VERSIONS: ${{ inputs.dev-versions }}
211+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
212+
REGISTRY: ghcr.io/${{ github.repository_owner }}
213+
NORMALIZED_PLATFORM: ${{ steps.normalize-platform.outputs.platform }}
214+
CONTEXT: ${{ inputs.context }}
193215
# Cache-to is conditional on --push (handled by bakery internally)
194216
run: |
195-
PLATFORM=${BUILD_PLATFORM#linux/} \
196217
bakery build \
197218
--strategy build --pull \
198-
--retry ${{ inputs.retry }} \
199-
--image-name '^${{ matrix.img.image }}$' \
200-
--image-version ${{ matrix.img.version }} \
201-
--image-platform ${{ matrix.img.platform }} \
202-
--dev-versions ${{ inputs.dev-versions }} \
203-
--matrix-versions ${{ inputs.matrix-versions }} \
204-
--cache-registry "ghcr.io/${{ github.repository_owner }}" \
205-
--temp-registry "ghcr.io/${{ github.repository_owner }}" \
206-
--metadata-file "./${{ matrix.img.image }}-${{ matrix.img.version }}-${{ steps.normalize-platform.outputs.platform }}-metadata.json" \
207-
--context ${{ inputs.context }} \
219+
--retry "$RETRY" \
220+
--image-name "^${IMAGE_NAME}$" \
221+
--image-version "$IMAGE_VERSION" \
222+
--image-platform "$IMAGE_PLATFORM" \
223+
--dev-versions "$DEV_VERSIONS" \
224+
--matrix-versions "$MATRIX_VERSIONS" \
225+
--cache-registry "$REGISTRY" \
226+
--temp-registry "$REGISTRY" \
227+
--metadata-file "./${IMAGE_NAME}-${IMAGE_VERSION}-${NORMALIZED_PLATFORM}-metadata.json" \
228+
--context "$CONTEXT" \
208229
--push
209230
- name: Test
231+
env:
232+
IMAGE_NAME: ${{ matrix.img.image }}
233+
IMAGE_VERSION: ${{ matrix.img.version }}
234+
IMAGE_PLATFORM: ${{ matrix.img.platform }}
235+
DEV_VERSIONS: ${{ inputs.dev-versions }}
236+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
237+
NORMALIZED_PLATFORM: ${{ steps.normalize-platform.outputs.platform }}
238+
CONTEXT: ${{ inputs.context }}
210239
run: |
211-
PLATFORM=${BUILD_PLATFORM#linux/} \
212240
GOSS_PATH=${GITHUB_WORKSPACE}/tools/goss \
213241
DGOSS_PATH=${GITHUB_WORKSPACE}/tools/dgoss \
214242
bakery run dgoss \
215-
--image-name '^${{ matrix.img.image }}$' \
216-
--image-version ${{ matrix.img.version }} \
217-
--image-platform ${{ matrix.img.platform }} \
218-
--dev-versions ${{ inputs.dev-versions }} \
219-
--matrix-versions ${{ inputs.matrix-versions }} \
220-
--metadata-file "./${{ matrix.img.image }}-${{ matrix.img.version }}-${{ steps.normalize-platform.outputs.platform }}-metadata.json" \
221-
--context ${{ inputs.context }}
243+
--image-name "^${IMAGE_NAME}$" \
244+
--image-version "$IMAGE_VERSION" \
245+
--image-platform "$IMAGE_PLATFORM" \
246+
--dev-versions "$DEV_VERSIONS" \
247+
--matrix-versions "$MATRIX_VERSIONS" \
248+
--metadata-file "./${IMAGE_NAME}-${IMAGE_VERSION}-${NORMALIZED_PLATFORM}-metadata.json" \
249+
--context "$CONTEXT"
222250
- name: Upload Metadata
223251
uses: actions/upload-artifact@v7
224252
with:
@@ -228,6 +256,9 @@ jobs:
228256

229257
merge:
230258
name: "Merge/Push ${{ matrix.img.image }}:${{ matrix.img.version }}"
259+
permissions:
260+
contents: read
261+
packages: write
231262
needs:
232263
- matrix
233264
- build-test
@@ -260,18 +291,12 @@ jobs:
260291
# this step sets an output that we can reference later.
261292
- name: Filter Steps
262293
id: filter-steps
294+
env:
295+
HAS_DOCKER_HUB: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN != '' }}
296+
HAS_AWS_ROLE: ${{ secrets.AWS_ROLE != '' }}
263297
run: |
264-
if [ -n "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" ] ; then
265-
echo "docker-hub=true" >> $GITHUB_OUTPUT
266-
else
267-
echo "docker-hub=false" >> $GITHUB_OUTPUT
268-
fi
269-
270-
if [ -n "${{ secrets.AWS_ROLE }}" ] ; then
271-
echo "ecr=true" >> $GITHUB_OUTPUT
272-
else
273-
echo "ecr=false" >> $GITHUB_OUTPUT
274-
fi
298+
echo "docker-hub=$HAS_DOCKER_HUB" >> $GITHUB_OUTPUT
299+
echo "ecr=$HAS_AWS_ROLE" >> $GITHUB_OUTPUT
275300
276301
- name: Login to GitHub Container Registry
277302
uses: docker/login-action@v4
@@ -318,15 +343,21 @@ jobs:
318343
- name: Merge/Push
319344
env:
320345
GIT_SHA: ${{ github.sha }}
346+
CONTEXT: ${{ inputs.context }}
347+
REGISTRY: ghcr.io/${{ github.repository_owner }}
348+
PUSH: ${{ inputs.push }}
321349
run: |
350+
if [ "$PUSH" = "true" ]; then PUSH_FLAG=""; else PUSH_FLAG="--dry-run"; fi
322351
bakery ci merge \
323-
--context ${{ inputs.context }} \
324-
--temp-registry "ghcr.io/${{ github.repository_owner }}" \
325-
${{ inputs.push && ' \' || '--dry-run \' }}
352+
--context "$CONTEXT" \
353+
--temp-registry "$REGISTRY" \
354+
$PUSH_FLAG \
326355
*-metadata.json
327356
328357
readme:
329358
name: Push READMEs
359+
permissions:
360+
contents: read
330361
if: ${{ inputs.push }}
331362
needs:
332363
- merge
@@ -345,8 +376,11 @@ jobs:
345376
env:
346377
DOCKER_HUB_README_USERNAME: ${{ secrets.DOCKER_HUB_README_USERNAME }}
347378
DOCKER_HUB_README_PASSWORD: ${{ secrets.DOCKER_HUB_README_PASSWORD }}
379+
CONTEXT: ${{ inputs.context }}
380+
DEV_VERSIONS: ${{ inputs.dev-versions }}
381+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
348382
run: |
349383
bakery ci readme \
350-
--context ${{ inputs.context }} \
351-
--dev-versions ${{ inputs.dev-versions }} \
352-
--matrix-versions ${{ inputs.matrix-versions }}
384+
--context "$CONTEXT" \
385+
--dev-versions "$DEV_VERSIONS" \
386+
--matrix-versions "$MATRIX_VERSIONS"

.github/workflows/bakery-build.yml

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,18 @@ defaults:
6666
run:
6767
shell: bash
6868

69+
# Security policy: No ${{ }} expressions in `run:` blocks.
70+
# All expression values are assigned to `env:` and referenced as
71+
# shell variables. This prevents script injection from runtime values
72+
# (matrix outputs, secrets) and keeps the rule enforceable by zizmor
73+
# without per-expression exceptions.
74+
6975
jobs:
7076
matrix:
7177
name: Image Matrix
7278
runs-on: ubuntu-latest
79+
permissions:
80+
contents: read
7381
outputs:
7482
matrix: ${{ steps.images.outputs.matrix }}
7583

@@ -84,11 +92,18 @@ jobs:
8492

8593
- name: Images
8694
id: images
95+
env:
96+
DEV_VERSIONS: ${{ inputs.dev-versions }}
97+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
98+
CONTEXT: ${{ inputs.context }}
8799
run: |
88-
echo "matrix=$(bakery ci matrix --quiet --dev-versions ${{ inputs.dev-versions }} --matrix-versions ${{ inputs.matrix-versions }} --context ${{ inputs.context }} | jq --compact-output .)" >> $GITHUB_OUTPUT
100+
echo "matrix=$(bakery ci matrix --quiet --dev-versions "$DEV_VERSIONS" --matrix-versions "$MATRIX_VERSIONS" --context "$CONTEXT" | jq --compact-output .)" >> $GITHUB_OUTPUT
89101
90102
build:
91103
name: "${{ matrix.img.image }}:${{ matrix.img.version }}"
104+
permissions:
105+
contents: read
106+
packages: write
92107
needs: matrix
93108
runs-on: ${{ inputs.runs-on }}
94109
strategy:
@@ -115,18 +130,12 @@ jobs:
115130
# this step sets an output that we can reference later.
116131
- name: Filter Steps
117132
id: filter-steps
133+
env:
134+
HAS_DOCKER_HUB: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN != '' }}
135+
HAS_AWS_ROLE: ${{ secrets.AWS_ROLE != '' }}
118136
run: |
119-
if [ -n "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" ] ; then
120-
echo "docker-hub=true" >> $GITHUB_OUTPUT
121-
else
122-
echo "docker-hub=false" >> $GITHUB_OUTPUT
123-
fi
124-
125-
if [ -n "${{ secrets.AWS_ROLE }}" ] ; then
126-
echo "ecr=true" >> $GITHUB_OUTPUT
127-
else
128-
echo "ecr=false" >> $GITHUB_OUTPUT
129-
fi
137+
echo "docker-hub=$HAS_DOCKER_HUB" >> $GITHUB_OUTPUT
138+
echo "ecr=$HAS_AWS_ROLE" >> $GITHUB_OUTPUT
130139
131140
- name: Login to GitHub Container Registry
132141
uses: docker/login-action@v4
@@ -160,26 +169,39 @@ jobs:
160169
- name: Build
161170
env:
162171
GIT_SHA: ${{ github.sha }}
172+
RETRY: ${{ inputs.retry }}
173+
IMAGE_NAME: ${{ matrix.img.image }}
174+
IMAGE_VERSION: ${{ matrix.img.version }}
175+
DEV_VERSIONS: ${{ inputs.dev-versions }}
176+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
177+
REGISTRY: ghcr.io/${{ github.repository_owner }}
178+
CONTEXT: ${{ inputs.context }}
163179
run: |
164180
bakery build --load --pull \
165-
--retry ${{ inputs.retry }} \
166-
--image-name '^${{ matrix.img.image }}$' \
167-
--image-version ${{ matrix.img.version }} \
168-
--dev-versions ${{ inputs.dev-versions }} \
169-
--matrix-versions ${{ inputs.matrix-versions }} \
170-
--cache-registry "ghcr.io/${{ github.repository_owner }}" \
171-
--context ${{ inputs.context }}
181+
--retry "$RETRY" \
182+
--image-name "^${IMAGE_NAME}$" \
183+
--image-version "$IMAGE_VERSION" \
184+
--dev-versions "$DEV_VERSIONS" \
185+
--matrix-versions "$MATRIX_VERSIONS" \
186+
--cache-registry "$REGISTRY" \
187+
--context "$CONTEXT"
172188
173189
- name: Test
190+
env:
191+
IMAGE_NAME: ${{ matrix.img.image }}
192+
IMAGE_VERSION: ${{ matrix.img.version }}
193+
DEV_VERSIONS: ${{ inputs.dev-versions }}
194+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
195+
CONTEXT: ${{ inputs.context }}
174196
run: |
175197
GOSS_PATH=${GITHUB_WORKSPACE}/tools/goss \
176198
DGOSS_PATH=${GITHUB_WORKSPACE}/tools/dgoss \
177199
bakery run dgoss \
178-
--image-name '^${{ matrix.img.image }}$' \
179-
--image-version ${{ matrix.img.version }} \
180-
--dev-versions ${{ inputs.dev-versions }} \
181-
--matrix-versions ${{ inputs.matrix-versions }} \
182-
--context ${{ inputs.context }}
200+
--image-name "^${IMAGE_NAME}$" \
201+
--image-version "$IMAGE_VERSION" \
202+
--dev-versions "$DEV_VERSIONS" \
203+
--matrix-versions "$MATRIX_VERSIONS" \
204+
--context "$CONTEXT"
183205
184206
- name: Push
185207
# Since this is a reusable workflow, we need to be very explicit about
@@ -188,17 +210,25 @@ jobs:
188210
if: ${{ inputs.push }}
189211
env:
190212
GIT_SHA: ${{ github.sha }}
213+
RETRY: ${{ inputs.retry }}
214+
IMAGE_NAME: ${{ matrix.img.image }}
215+
IMAGE_VERSION: ${{ matrix.img.version }}
216+
DEV_VERSIONS: ${{ inputs.dev-versions }}
217+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
218+
CONTEXT: ${{ inputs.context }}
191219
run: |
192220
bakery build --push --pull \
193-
--retry ${{ inputs.retry }} \
194-
--image-name '^${{ matrix.img.image }}$' \
195-
--image-version ${{ matrix.img.version }} \
196-
--dev-versions ${{ inputs.dev-versions }} \
197-
--matrix-versions ${{ inputs.matrix-versions }} \
198-
--context ${{ inputs.context }}
221+
--retry "$RETRY" \
222+
--image-name "^${IMAGE_NAME}$" \
223+
--image-version "$IMAGE_VERSION" \
224+
--dev-versions "$DEV_VERSIONS" \
225+
--matrix-versions "$MATRIX_VERSIONS" \
226+
--context "$CONTEXT"
199227
200228
readme:
201229
name: Push READMEs
230+
permissions:
231+
contents: read
202232
if: ${{ inputs.push }}
203233
needs:
204234
- build
@@ -217,8 +247,11 @@ jobs:
217247
env:
218248
DOCKER_HUB_README_USERNAME: ${{ secrets.DOCKER_HUB_README_USERNAME }}
219249
DOCKER_HUB_README_PASSWORD: ${{ secrets.DOCKER_HUB_README_PASSWORD }}
250+
CONTEXT: ${{ inputs.context }}
251+
DEV_VERSIONS: ${{ inputs.dev-versions }}
252+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
220253
run: |
221254
bakery ci readme \
222-
--context ${{ inputs.context }} \
223-
--dev-versions ${{ inputs.dev-versions }} \
224-
--matrix-versions ${{ inputs.matrix-versions }}
255+
--context "$CONTEXT" \
256+
--dev-versions "$DEV_VERSIONS" \
257+
--matrix-versions "$MATRIX_VERSIONS"

0 commit comments

Comments
 (0)