Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Linkiving frontend CD

on:
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened

permissions:
contents: read
pull-requests: write

concurrency:
group: frontend-cd-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
deploy-preview:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.19.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Pull Vercel preview environment
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

- name: Build Vercel artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy preview to Vercel
id: deploy
shell: bash
run: |
deployment_url="$(vercel deploy --prebuilt --archive=tgz --token=${{ secrets.VERCEL_TOKEN }})"
echo "deployment_url=${deployment_url}" >> "$GITHUB_OUTPUT"
Comment on lines +53 to +64
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 포크 PR 허용 여부 및 다른 워크플로의 트리거 패턴 확인
fd -e yml -e yaml . .github/workflows --exec rg -n 'pull_request|fork|secrets\.' {} \;

Repository: Team-SoFa/linkiving

Length of output: 1346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/cd.yml"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE" >&2
  exit 1
fi

echo "=== on: section (first 120 lines) ==="
nl -ba "$FILE" | sed -n '1,120p'

echo "=== workflow/jobs/steps around Vercel deploy (40-120 lines) ==="
nl -ba "$FILE" | sed -n '35,120p'

Repository: Team-SoFa/linkiving

Length of output: 163


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/cd.yml"
wc -l "$FILE"

echo "=== cd.yml: 1-120 ==="
sed -n '1,120p' "$FILE"

echo "=== cd.yml: 120-end ==="
sed -n '121,$p' "$FILE"

Repository: Team-SoFa/linkiving

Length of output: 2996


🌐 Web query:

GitHub Actions pull_request events from fork secrets.VERCEL_TOKEN not available GITHUB_TOKEN read-only even if permissions specified pull-requests: write

💡 Result:

In GitHub Actions, when a workflow is triggered by the pull_request event for a pull request coming from a fork, secrets (including your secrets.VERCEL_TOKEN) are not passed to the workflow, and GitHub also applies a security model that can make the GITHUB_TOKEN effectively read-only for that run even if you set permissions in the workflow YAML. 1) Why secrets.VERCEL_TOKEN is “not available” - GitHub documents that secrets are not passed to workflows that are triggered by a pull request from a fork. This means ${{ secrets.VERCEL_TOKEN }} will be unset/empty in that fork PR run (by design/security restriction). [1] 2) Why GITHUB_TOKEN becomes read-only and ignores “pull-requests: write” - GitHub applies a read-only permissions adjustment for pull-request runs triggered from forked repositories (the docs clarification indicates write permissions are changed to read-only except for pull_request_target). [2][3] - Therefore, on pull_request from forks, you should not expect permissions: pull-requests: write to grant write access in the way it does for same-repo pull requests. [3] 3) The supported workaround (use pull_request_target with caution) - If you need a workflow to run with access to secrets (and generally with broader token permissions) for PRs originating from forks, GitHub’s guidance is to use the pull_request_target event so the workflow runs in the context of the base repository and can access secrets/tokens. [4] - Note: pull_request_target runs against the base context rather than the PR head context; GitHub explicitly warns to avoid running unsafe code from the pull request because that could alter the repository or steal secrets. [5][4] 4) Practical guidance for your case (Vercel deploy on fork PRs) - For fork-origin PRs on pull_request, plan for VERCEL_TOKEN to be unavailable and therefore disable/skip any steps that require it. - If you truly must deploy preview from fork PRs, switch to pull_request_target and only check out the PR code after you have appropriate review/guardrails (the security risk is real, which is why many actions default to not deploying fork PRs). [6][5] If you paste your workflow YAML (especially the on: trigger, permissions:, and where you use secrets.VERCEL_TOKEN), I can suggest an exact conditional structure to prevent the token from being required on fork PRs while still allowing the behavior you want on internal PRs.

Citations:


🌐 Web query:

GitHub Actions pull_request events from fork secrets not passed GITHUB_TOKEN read-only even if permissions pull-requests: write

💡 Result:

For pull requests from forks using the pull_request event, GitHub intentionally prevents your workflow from getting write-capable GITHUB_TOKEN or any repository secrets beyond the default token’s limited access: repository secrets are not passed to workflows triggered by a pull request from a fork, and any “write” token permissions are adjusted down to read-only unless you use pull_request_target [1][2][3]. As a result, setting pull-requests: write (or broader write scopes) won’t be honored for the forked-PR workflow context; GitHub changes those permissions to read-only for security [1][4][3]. What you can do instead 1) If you need secrets and write access for forked PRs, the supported mechanism is pull_request_target (it runs in the base repository context and can access secrets; by default it grants read/write token permissions unless you restrict them) [5][3]. But you must not check out or execute untrusted code from the fork in that workflow; only do trusted actions like labeling/commenting/triage [5]. 2) Keep pull_request for untrusted code execution and move any secret-dependent or write-permission steps behind a trusted workflow (commonly by splitting workflows, or using pull_request_target only for metadata/reporting) [5]. 3) Use least-privilege permissions anyway: the GITHUB_TOKEN’s granted scopes are controlled via the permissions key, but for forked pull_request triggers GitHub still applies the fork-related read-only/secret-withholding restrictions [6][1][3]. Bottom line answer to your query - Yes: for fork PRs triggered with pull_request, GitHub will withhold secrets and downgrade any requested token write permissions (including pull-requests: write) to read-only for that workflow run [1][3]. - There is no way to “force” pull_request fork runs to get pull-requests: write with repository secrets; the security restriction is intentional [1][3]. - The practical option is to use pull_request_target for the privileged portion, with the critical rule that you do not execute fork code in that context [5].

Citations:


포크 PR에서 Vercel 토큰/PR 코멘트 권한이 막혀 워크플로가 실패할 수 있어요.

  • 현재 .github/workflows/cd.ymlpull_request 트리거에서는 secrets.VERCEL_TOKEN을 전제로 vercel pull/build/deploy를 항상 실행하는데, 포크 PR의 pull_request에서는 시크릿이 전달되지 않아 이 단계가 실패할 수 있습니다.
  • 또한 permissions: pull-requests: write를 설정해도 포크 PR pull_request 실행에서는 GITHUB_TOKEN이 write로 동작하지 않을 수 있어 코멘트 작성 단계(createComment/updateComment)도 실패할 수 있습니다.
  • 대응: 포크 PR이면 해당 단계들을 if: ...head.repo.fork == false로 스킵하거나, 권한/시크릿이 필요한 부분만 pull_request_target로 분리(비신뢰 코드 실행 주의)하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cd.yml around lines 53 - 64, The Vercel steps ("Pull
Vercel preview environment", "Build Vercel artifacts", and "Deploy preview to
Vercel") assume secrets and PR write permissions and will fail for forked PRs;
update the workflow so these steps run only for non-forked PRs by adding a
conditional (e.g., check head.repo.fork == false) to each step or move only the
secret/permission-requiring parts into a separate pull_request_target job,
keeping the deploy/comment logic guarded from untrusted fork runs and
referencing the step names above to locate where to add the condition or
extraction.

echo "Preview deployment: ${deployment_url}" >> "$GITHUB_STEP_SUMMARY"

- name: Add PR preview comment
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
env:
DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment_url }}
with:
script: |
const marker = '<!-- vercel-preview-deployment -->';
const body = `${marker}\nPreview deployment is ready.\n\n- URL: ${process.env.DEPLOYMENT_URL}\n- Commit: ${context.sha}`;
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner,
repo,
issue_number,
per_page: 100,
});
const existing = comments.find((comment) => comment.user?.type === 'Bot' && comment.body?.includes(marker));

if (existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
return;
}

await github.rest.issues.createComment({
owner,
repo,
issue_number,
body,
});
66 changes: 66 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Vercel Production Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

concurrency:
group: vercel-release-${{ github.ref }}
cancel-in-progress: false

env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
deploy-production:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.19.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Pull Vercel production environment
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

- name: Build production artifacts
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy production to Vercel
id: deploy
shell: bash
run: |
deployment_url="$(vercel deploy --prebuilt --archive=tgz --prod --token=${{ secrets.VERCEL_TOKEN }})"
echo "deployment_url=${deployment_url}" >> "$GITHUB_OUTPUT"
{
echo "Production release deployed."
echo
echo "- Tag: ${{ github.ref_name }}"
echo "- URL: ${deployment_url}"
} >> "$GITHUB_STEP_SUMMARY"
Comment on lines +53 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

스텝 요약에서 github.ref_name을 직접 셸로 확장하지 마세요 (코드 인젝션).

echo "- Tag: ${{ github.ref_name }}"는 표현식을 셸 스크립트로 직접 전개합니다. git 태그 이름에는 $, (, ), 백틱 등 셸 메타문자가 포함될 수 있어, 태그를 푸시할 수 있는 권한자가 임의 명령을 실행할 수 있는 인젝션 경로가 됩니다(이 잡은 VERCEL_TOKEN 시크릿에 접근). env로 전달해 안전하게 참조하세요.

🛡️ env 변수로 전달
       - name: Deploy production to Vercel
         id: deploy
         shell: bash
+        env:
+          REF_NAME: ${{ github.ref_name }}
         run: |
           deployment_url="$(vercel deploy --prebuilt --archive=tgz --prod --token=${{ secrets.VERCEL_TOKEN }})"
           echo "deployment_url=${deployment_url}" >> "$GITHUB_OUTPUT"
           {
             echo "Production release deployed."
             echo
-            echo "- Tag: ${{ github.ref_name }}"
+            echo "- Tag: ${REF_NAME}"
             echo "- URL: ${deployment_url}"
           } >> "$GITHUB_STEP_SUMMARY"
🧰 Tools
🪛 zizmor (1.25.2)

[error] 59-59: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 53 - 61, The step is vulnerable
because echo "- Tag: ${{ github.ref_name }}" expands the GitHub expression
directly into the shell (possible code injection); instead pass the tag through
the environment and reference that env var in the run block. Update the job to
set an env variable (e.g., TAG) to ${{ github.ref_name }} and then in the run
section write the summary using that env var (e.g., echo "- Tag: $TAG" >>
"$GITHUB_STEP_SUMMARY"), keeping deployment_url handling unchanged.


- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
Loading