Skip to content

fix(ci): PR 제목의 '따옴표'가 셸에 삽입되던 Discord 알림 워크플로 #189

fix(ci): PR 제목의 '따옴표'가 셸에 삽입되던 Discord 알림 워크플로

fix(ci): PR 제목의 '따옴표'가 셸에 삽입되던 Discord 알림 워크플로 #189

name: Discord PR Notification
on:
pull_request:
types: [opened, reopened]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Send Discord Notification
# PR 제목·브랜치명은 사용자가 정하는 값이다. ${{ }} 로 run 스크립트에 직접 끼워 넣으면
# 셸이 그 문자열을 코드로 해석한다 — 따옴표 하나에 명령이 깨지고(실제로 깨졌다),
# 작정하면 러너에서 임의 명령을 실행할 수 있다(GitHub Actions script injection).
# 값은 env 로만 넘기고(러너가 직접 주입, 셸 파싱 없음) JSON 은 jq 가 이스케이프한다.
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_AVATAR: ${{ github.event.pull_request.user.avatar_url }}
PR_ACTOR: ${{ github.actor }}
PR_HEAD: ${{ github.event.pull_request.head.ref }}
PR_BASE: ${{ github.event.pull_request.base.ref }}
run: |
set -euo pipefail
payload=$(jq -n \
--arg title "$PR_TITLE" \
--arg url "$PR_URL" \
--arg avatar "$PR_AVATAR" \
--arg actor "$PR_ACTOR" \
--arg branches "$PR_HEAD ➡️ $PR_BASE" \
'{
content: "🚀 **새로운 PR이 도착했습니다!**",
embeds: [{
title: ($title | .[0:256]),
url: $url,
color: 3447003,
thumbnail: { url: $avatar },
fields: [
{ name: "작성자", value: $actor, inline: true },
{ name: "브랜치", value: $branches, inline: true }
]
}]
}')
# -f 가 없으면 Discord 가 400 을 줘도 성공으로 끝난다(지금까지 그랬다).
curl -sS -f -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK"