GitHub Actionsのscriptブロックにおけるコードインジェクション

actions/github-scriptに外部の値をコードとして埋め込まず、JavaScriptのデータとして渡してください。

説明

actions/github-scriptのscriptはJavaScript関数の本体として実行されます。Issueのタイトルなど外部の値を${{ }}で直接埋め込むと、コードの生成時に文字列の範囲を抜け出し、別のコードを実行される可能性があります。ファイルパスに使う場合は、パスの範囲も別途制限する必要があります。

想定される影響

  • 注入されたコードが実行環境のファイルを読み取ったり、成果物を改ざんしたりする可能性があります。
  • ジョブのトークンと権限によっては、Issue・コメントなどの操作を悪用されたり、シークレットが漏えいしたりするおそれがあります。

対処方法

  • 必要な値はcontext.payloadからデータとして読むか、envで渡してprocess.envから読んでください。コード文字列に埋め込んだり、evalで評価したりしないでください。
  • ファイルパスやAPI引数の形式と範囲を検証してください。データとして渡しても、その後のすべての使い方が安全になるわけではありません。
  • トークンとシークレットへのアクセスを限定し、強い権限のジョブで信頼できないコードを実行しないでください。

例

コメントの作成に必要な権限を明示した例です。最初の例の/tmpファイル読み取りは危険を示すもので、そのファイルが用意されていなければ失敗します。アクションは確認済みのバージョンやコミットに固定してください。

変更前

yaml
name: test-script-run

on:
  issues:
    types: [opened]

permissions:
  contents: read
  issues: write

jobs:
  script-run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Run script
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const body = fs.readFileSync('/tmp/${{ github.event.issue.title }}.txt', {encoding: 'utf8'});

            await github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: 'Thanks for reporting!'
            })

            return true;

IssueのタイトルをJavaScript文字列とファイルパスへ直接埋め込みます。コード注入とパスの操作の両方に対処する必要があります。

変更後

yaml
name: test-script-run

on:
  issues:
    types: [opened]

permissions:
  contents: read
  issues: write

jobs:
  script-run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Run script
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: 'Thanks for reporting!'
            })

            return true;

不要なファイル読み取りを削除し、固定のコメントだけを投稿します。タイトルが必要なら、コードに埋め込まずデータとして読み、検証してください。

参考資料