Skip to content

Commit c898d0d

Browse files
imSzukalaclaude
andauthored
fix(ci): harden update-dependencies workflow against supply chain attacks (#435)
SHA-pin all action references to immutable commit hashes, move all expression interpolations from run: blocks to env: blocks to prevent shell injection, and add persist-credentials: false for public repo credential safety. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 306ea6b commit c898d0d

1 file changed

Lines changed: 97 additions & 71 deletions

File tree

.github/workflows/update-dependencies.yml

Lines changed: 97 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ jobs:
1717

1818
steps:
1919
- name: 🔍 Checkout repository
20-
uses: actions/checkout@v4
20+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
2121
with:
2222
fetch-depth: 0
23+
persist-credentials: false
2324

2425
- name: ⚙️ Setup Node.js
25-
uses: actions/setup-node@v4
26+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
2627
with:
2728
node-version: '22.14.0'
2829

@@ -75,12 +76,12 @@ jobs:
7576
7677
- name: 🧮 Analyze update requirements
7778
id: update-analysis
79+
env:
80+
CURRENT_IOS: ${{ steps.current-versions.outputs.ios_version }}
81+
LATEST_IOS: ${{ steps.latest-versions.outputs.ios_version }}
82+
CURRENT_ANDROID: ${{ steps.current-versions.outputs.android_version }}
83+
LATEST_ANDROID: ${{ steps.latest-versions.outputs.android_version }}
7884
run: |
79-
CURRENT_IOS="${{ steps.current-versions.outputs.ios_version }}"
80-
LATEST_IOS="${{ steps.latest-versions.outputs.ios_version }}"
81-
CURRENT_ANDROID="${{ steps.current-versions.outputs.android_version }}"
82-
LATEST_ANDROID="${{ steps.latest-versions.outputs.android_version }}"
83-
8485
echo "📊 Version Analysis:"
8586
echo " iOS: $CURRENT_IOS → $LATEST_IOS"
8687
echo " Android: $CURRENT_ANDROID → $LATEST_ANDROID"
@@ -140,12 +141,11 @@ jobs:
140141
id: pr-analysis
141142
env:
142143
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
144+
TARGET_IOS: ${{ steps.update-analysis.outputs.ios_new_version }}
145+
TARGET_ANDROID: ${{ steps.update-analysis.outputs.android_new_version }}
146+
IOS_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.ios_needs_update }}
147+
ANDROID_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.android_needs_update }}
143148
run: |
144-
TARGET_IOS="${{ steps.update-analysis.outputs.ios_new_version }}"
145-
TARGET_ANDROID="${{ steps.update-analysis.outputs.android_new_version }}"
146-
IOS_NEEDS_UPDATE="${{ steps.update-analysis.outputs.ios_needs_update }}"
147-
ANDROID_NEEDS_UPDATE="${{ steps.update-analysis.outputs.android_needs_update }}"
148-
149149
echo "🔍 Checking for existing dependency PRs..."
150150
echo " Target: iOS=$TARGET_IOS, Android=$TARGET_ANDROID"
151151
@@ -226,7 +226,7 @@ jobs:
226226
227227
- name: 🔧 Setup Ruby for CocoaPods
228228
if: steps.update-analysis.outputs.overall_update_needed == 'true' && steps.pr-analysis.outputs.should_create_pr == 'true'
229-
uses: ruby/setup-ruby@v1
229+
uses: ruby/setup-ruby@97ecb7b512899eb71ab1bf2310a624c6f1589ac6 # v1.308.0
230230
with:
231231
bundler-cache: true
232232

@@ -248,39 +248,40 @@ jobs:
248248
- name: 📝 Update dependency files
249249
if: steps.update-analysis.outputs.overall_update_needed == 'true' && steps.pr-analysis.outputs.should_create_pr == 'true'
250250
id: update-files
251+
env:
252+
IOS_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.ios_needs_update }}
253+
IOS_NEW_VERSION: ${{ steps.update-analysis.outputs.ios_new_version }}
254+
IOS_OLD_VERSION: ${{ steps.current-versions.outputs.ios_version }}
255+
ANDROID_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.android_needs_update }}
256+
ANDROID_NEW_VERSION: ${{ steps.update-analysis.outputs.android_new_version }}
257+
ANDROID_OLD_VERSION: ${{ steps.current-versions.outputs.android_version }}
251258
run: |
252259
CHANGES=""
253260
PR_TITLE_PARTS=""
254261
255262
# Update iOS if needed
256-
if [ "${{ steps.update-analysis.outputs.ios_needs_update }}" = "true" ]; then
257-
NEW_IOS="${{ steps.update-analysis.outputs.ios_new_version }}"
258-
OLD_IOS="${{ steps.current-versions.outputs.ios_version }}"
259-
260-
echo "📱 Updating iOS SDK: $OLD_IOS → $NEW_IOS"
261-
sed -i '' "s/s\.dependency \"Intercom\", '~> [^']*'/s.dependency \"Intercom\", '~> $NEW_IOS'/" intercom-react-native.podspec
263+
if [ "$IOS_NEEDS_UPDATE" = "true" ]; then
264+
echo "📱 Updating iOS SDK: $IOS_OLD_VERSION → $IOS_NEW_VERSION"
265+
sed -i '' "s/s\.dependency \"Intercom\", '~> [^']*'/s.dependency \"Intercom\", '~> $IOS_NEW_VERSION'/" intercom-react-native.podspec
262266
263-
CHANGES="$CHANGES\\n- Updated iOS SDK from $OLD_IOS to $NEW_IOS"
264-
PR_TITLE_PARTS="iOS: $OLD_IOS → $NEW_IOS"
267+
CHANGES="$CHANGES\\n- Updated iOS SDK from $IOS_OLD_VERSION to $IOS_NEW_VERSION"
268+
PR_TITLE_PARTS="iOS: $IOS_OLD_VERSION → $IOS_NEW_VERSION"
265269
266270
git add intercom-react-native.podspec
267271
fi
268272
269273
# Update Android if needed
270-
if [ "${{ steps.update-analysis.outputs.android_needs_update }}" = "true" ]; then
271-
NEW_ANDROID="${{ steps.update-analysis.outputs.android_new_version }}"
272-
OLD_ANDROID="${{ steps.current-versions.outputs.android_version }}"
274+
if [ "$ANDROID_NEEDS_UPDATE" = "true" ]; then
275+
echo "🤖 Updating Android SDK: $ANDROID_OLD_VERSION → $ANDROID_NEW_VERSION"
276+
sed -i '' "s/io\.intercom\.android:intercom-sdk:[^']*/io.intercom.android:intercom-sdk:$ANDROID_NEW_VERSION/" android/build.gradle
277+
sed -i '' "s/io\.intercom\.android:intercom-sdk-ui:[^']*/io.intercom.android:intercom-sdk-ui:$ANDROID_NEW_VERSION/" android/build.gradle
273278
274-
echo "🤖 Updating Android SDK: $OLD_ANDROID → $NEW_ANDROID"
275-
sed -i '' "s/io\.intercom\.android:intercom-sdk:[^']*/io.intercom.android:intercom-sdk:$NEW_ANDROID/" android/build.gradle
276-
sed -i '' "s/io\.intercom\.android:intercom-sdk-ui:[^']*/io.intercom.android:intercom-sdk-ui:$NEW_ANDROID/" android/build.gradle
277-
278-
CHANGES="$CHANGES\\n- Updated Android SDK from $OLD_ANDROID to $NEW_ANDROID"
279+
CHANGES="$CHANGES\\n- Updated Android SDK from $ANDROID_OLD_VERSION to $ANDROID_NEW_VERSION"
279280
280281
if [ -n "$PR_TITLE_PARTS" ]; then
281-
PR_TITLE_PARTS="$PR_TITLE_PARTS, Android: $OLD_ANDROID → $NEW_ANDROID"
282+
PR_TITLE_PARTS="$PR_TITLE_PARTS, Android: $ANDROID_OLD_VERSION → $ANDROID_NEW_VERSION"
282283
else
283-
PR_TITLE_PARTS="Android: $OLD_ANDROID → $NEW_ANDROID"
284+
PR_TITLE_PARTS="Android: $ANDROID_OLD_VERSION → $ANDROID_NEW_VERSION"
284285
fi
285286
286287
git add android/build.gradle
@@ -289,10 +290,10 @@ jobs:
289290
# Determine version bump type using semver script
290291
echo "📈 Analyzing version bump requirements..."
291292
VERSION_BUMP_TYPE=$(node semver.js analyze \
292-
"${{ steps.current-versions.outputs.ios_version }}" \
293-
"${{ steps.update-analysis.outputs.ios_new_version }}" \
294-
"${{ steps.current-versions.outputs.android_version }}" \
295-
"${{ steps.update-analysis.outputs.android_new_version }}")
293+
"$IOS_OLD_VERSION" \
294+
"$IOS_NEW_VERSION" \
295+
"$ANDROID_OLD_VERSION" \
296+
"$ANDROID_NEW_VERSION")
296297
297298
# Get current version and increment using semver script
298299
echo "📈 Applying $VERSION_BUMP_TYPE version bump..."
@@ -352,6 +353,11 @@ jobs:
352353
353354
- name: 💾 Commit and push changes
354355
if: steps.update-analysis.outputs.overall_update_needed == 'true' && steps.pr-analysis.outputs.should_create_pr == 'true'
356+
env:
357+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
358+
PR_TITLE_PARTS: ${{ steps.update-files.outputs.pr_title_parts }}
359+
CHANGES: ${{ steps.update-files.outputs.changes }}
360+
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
355361
run: |
356362
# Check if we have changes to commit
357363
if git diff --cached --quiet; then
@@ -360,94 +366,114 @@ jobs:
360366
fi
361367
362368
echo "💾 Committing changes..."
363-
git commit -m "chore: update Intercom SDK dependencies (${{ steps.update-files.outputs.pr_title_parts }})
369+
git commit -m "chore: update Intercom SDK dependencies ($PR_TITLE_PARTS)
364370
365-
${{ steps.update-files.outputs.changes }}
371+
$CHANGES
366372
- Updated lockfiles and example projects"
367373
368-
echo "🚀 Pushing branch: ${{ steps.create-branch.outputs.branch_name }}"
369-
git push origin "${{ steps.create-branch.outputs.branch_name }}"
374+
echo "🚀 Pushing branch: $BRANCH_NAME"
375+
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
376+
git push origin "$BRANCH_NAME"
370377
371378
- name: 🎯 Create Pull Request
372379
if: steps.update-analysis.outputs.overall_update_needed == 'true' && steps.pr-analysis.outputs.should_create_pr == 'true'
373380
env:
374381
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
382+
CHANGES: ${{ steps.update-files.outputs.changes }}
383+
IOS_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.ios_needs_update }}
384+
IOS_NEW_VERSION: ${{ steps.update-analysis.outputs.ios_new_version }}
385+
IOS_TAG: ${{ steps.latest-versions.outputs.ios_version }}
386+
IOS_CHANGELOG: ${{ steps.latest-versions.outputs.ios_changelog }}
387+
ANDROID_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.android_needs_update }}
388+
ANDROID_NEW_VERSION: ${{ steps.update-analysis.outputs.android_new_version }}
389+
ANDROID_TAG: ${{ steps.latest-versions.outputs.android_version }}
390+
ANDROID_CHANGELOG: ${{ steps.latest-versions.outputs.android_changelog }}
391+
PR_TITLE_PARTS: ${{ steps.update-files.outputs.pr_title_parts }}
392+
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
375393
run: |
376394
echo "🎯 Creating pull request..."
377395
378396
# Build PR description
379-
cat > pr_body.md << 'EOF'
397+
cat > pr_body.md <<BODY_EOF
380398
## 🔄 Automated Intercom SDK Dependency Update
381399
382400
This PR updates the Intercom SDK dependencies to their latest versions.
383401
384402
### 📋 Changes Made:
385-
${{ steps.update-files.outputs.changes }}
403+
$CHANGES
386404
387405
### 📚 Release Notes:
388-
EOF
406+
BODY_EOF
389407
390408
# Add iOS release notes if updated
391-
if [ "${{ steps.update-analysis.outputs.ios_needs_update }}" = "true" ]; then
392-
cat >> pr_body.md << 'EOF'
409+
if [ "$IOS_NEEDS_UPDATE" = "true" ]; then
410+
cat >> pr_body.md <<IOS_EOF
393411
394-
#### 📱 iOS SDK ${{ steps.update-analysis.outputs.ios_new_version }}
395-
[View Release Notes](https://github.com/intercom/intercom-ios/releases/tag/${{ steps.latest-versions.outputs.ios_version }})
412+
#### 📱 iOS SDK $IOS_NEW_VERSION
413+
[View Release Notes](https://github.com/intercom/intercom-ios/releases/tag/$IOS_TAG)
396414
397-
```
398-
${{ steps.latest-versions.outputs.ios_changelog }}
399-
```
400-
EOF
415+
\`\`\`
416+
$IOS_CHANGELOG
417+
\`\`\`
418+
IOS_EOF
401419
fi
402420
403421
# Add Android release notes if updated
404-
if [ "${{ steps.update-analysis.outputs.android_needs_update }}" = "true" ]; then
405-
cat >> pr_body.md << 'EOF'
422+
if [ "$ANDROID_NEEDS_UPDATE" = "true" ]; then
423+
cat >> pr_body.md <<ANDROID_EOF
406424
407-
#### 🤖 Android SDK ${{ steps.update-analysis.outputs.android_new_version }}
408-
[View Release Notes](https://github.com/intercom/intercom-android/releases/tag/${{ steps.latest-versions.outputs.android_version }})
425+
#### 🤖 Android SDK $ANDROID_NEW_VERSION
426+
[View Release Notes](https://github.com/intercom/intercom-android/releases/tag/$ANDROID_TAG)
409427
410-
```
411-
${{ steps.latest-versions.outputs.android_changelog }}
412-
```
413-
EOF
428+
\`\`\`
429+
$ANDROID_CHANGELOG
430+
\`\`\`
431+
ANDROID_EOF
414432
fi
415433
416-
cat >> pr_body.md << 'EOF'
434+
cat >> pr_body.md <<FOOTER_EOF
417435
418436
---
419437
🤖 This PR was automatically created by the [Update Dependencies workflow](.github/workflows/update-dependencies.yml).
420-
EOF
438+
FOOTER_EOF
421439
422440
# Create PR
423441
if gh pr create \
424-
--title "chore: update Intercom SDK dependencies (${{ steps.update-files.outputs.pr_title_parts }})" \
442+
--title "chore: update Intercom SDK dependencies ($PR_TITLE_PARTS)" \
425443
--body-file pr_body.md \
426-
--head "${{ steps.create-branch.outputs.branch_name }}" \
444+
--head "$BRANCH_NAME" \
427445
--base "main"; then
428446
echo "✅ Pull request created successfully!"
429447
else
430448
echo "❌ Failed to create pull request"
431-
echo "📋 Branch pushed: ${{ steps.create-branch.outputs.branch_name }}"
432-
echo "📋 Title: chore: update Intercom SDK dependencies (${{ steps.update-files.outputs.pr_title_parts }})"
449+
echo "📋 Branch pushed: $BRANCH_NAME"
450+
echo "📋 Title: chore: update Intercom SDK dependencies ($PR_TITLE_PARTS)"
433451
exit 1
434452
fi
435453
436454
- name: 📊 Summary
437455
if: always()
456+
env:
457+
OVERALL_UPDATE_NEEDED: ${{ steps.update-analysis.outputs.overall_update_needed }}
458+
SHOULD_CREATE_PR: ${{ steps.pr-analysis.outputs.should_create_pr }}
459+
JOB_STATUS: ${{ job.status }}
460+
IOS_VERSION: ${{ steps.current-versions.outputs.ios_version }}
461+
ANDROID_VERSION: ${{ steps.current-versions.outputs.android_version }}
462+
PR_TITLE_PARTS: ${{ steps.update-files.outputs.pr_title_parts }}
463+
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
438464
run: |
439465
echo "## 📊 Workflow Summary"
440466
441-
if [ "${{ steps.update-analysis.outputs.overall_update_needed }}" != "true" ]; then
467+
if [ "$OVERALL_UPDATE_NEEDED" != "true" ]; then
442468
echo "ℹ️ No dependency updates needed - all SDKs are current"
443-
echo " 📱 iOS: ${{ steps.current-versions.outputs.ios_version }}"
444-
echo " 🤖 Android: ${{ steps.current-versions.outputs.android_version }}"
445-
elif [ "${{ steps.pr-analysis.outputs.should_create_pr }}" != "true" ]; then
469+
echo " 📱 iOS: $IOS_VERSION"
470+
echo " 🤖 Android: $ANDROID_VERSION"
471+
elif [ "$SHOULD_CREATE_PR" != "true" ]; then
446472
echo "⏭️ Updates available but existing PR already covers latest versions"
447-
elif [ "${{ job.status }}" = "success" ]; then
473+
elif [ "$JOB_STATUS" = "success" ]; then
448474
echo "✅ Successfully created dependency update PR!"
449-
echo " 📋 Title: chore: update Intercom SDK dependencies (${{ steps.update-files.outputs.pr_title_parts }})"
450-
echo " 🌿 Branch: ${{ steps.create-branch.outputs.branch_name }}"
475+
echo " 📋 Title: chore: update Intercom SDK dependencies ($PR_TITLE_PARTS)"
476+
echo " 🌿 Branch: $BRANCH_NAME"
451477
else
452478
echo "❌ Workflow failed - check logs above"
453479
fi

0 commit comments

Comments
 (0)