ci(landing): deploy landing via Actions so fork PRs stop failing Vercel - #1248
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
📝 WalkthroughWalkthroughAdds a GitHub Actions workflow that builds and deploys the landing app to Vercel for release pushes, pull requests, and manual runs, then records the deployment URL and updates pull request preview comments. ChangesLanding Vercel deployment
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant VercelCLI
participant Vercel
participant PullRequest
GitHubActions->>VercelCLI: Resolve deployment target
GitHubActions->>VercelCLI: Pull settings and build landing site
VercelCLI->>Vercel: Deploy prebuilt artifact
Vercel-->>GitHubActions: Return deployment URL
GitHubActions->>PullRequest: Create or update preview URL comment
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
Pull request overview
This PR replaces Vercel’s Git integration for the landing/** site with a GitHub Actions-driven deploy workflow, so fork-based pull requests no longer show a guaranteed-red “requires authorization” Vercel status check.
Changes:
- Added a new
landing-deployworkflow that deploys production onreleasepushes affectinglanding/**. - Added preview deploys for same-repo PRs affecting
landing/**, and skips fork PRs to yield a neutral result instead of a failing check. - Posts/updates a sticky PR comment containing the preview URL.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const existing = ( | ||
| await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }) | ||
| ).data.find((c) => c.body && c.body.includes(marker)); |
993e1ff to
7d14f83
Compare
|
Landing preview: https://neurolink-gqt5vptlw-sachin-sharmas-projects-7dbbe7a8.vercel.app Deployed from |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with 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.
Inline comments:
In @.github/workflows/landing-deploy.yml:
- Around line 58-59: Update the “Install Vercel CLI” workflow step to install an
exact validated Vercel CLI version instead of the mutable latest tag, and retain
that explicit version in the command so future upgrades require a deliberate
workflow change.
- Around line 34-36: Update the deploy job in the workflow to retain only
contents: read permission, move any PR commentary or commit-status steps into a
separate job scoped with pull-requests: write, and configure its checkout step
with persist-credentials: false.
- Around line 103-105: Update the sticky-comment lookup around
github.rest.issues.listComments to paginate through all comment pages rather
than limiting the search to one page, and only select a comment whose
c.user?.login equals github-actions[bot] in addition to containing marker.
Preserve the existing update-or-create flow using the authoritative matching
comment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 82fbd066-614b-4cb8-96ec-2adae74c21ba
📒 Files selected for processing (1)
.github/workflows/landing-deploy.yml
| permissions: | ||
| contents: read | ||
| pull-requests: write |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
sed -n '1,90p' .github/workflows/landing-deploy.yml 2>/dev/null || true
echo
echo "== checkout usages in workflows =="
rg -n "actions/checkout|permissions:|pull-requests:|persist-credentials" .github/workflows -C 2 || true
echo
echo "== git diff stat and relevant diff =="
git diff --stat || true
git diff -- .github/workflows/landing-deploy.yml | sed -n '1,160p' || trueRepository: juspay/neurolink
Length of output: 12935
🌐 Web query:
actions/checkout persist-credentials default true README
💡 Result:
In the actions/checkout GitHub Action, the persist-credentials input determines whether the action configures the provided token or SSH key within the local Git configuration [1][2]. As of July 2026, the default value for persist-credentials remains true [1][3][4]. When set to true, the action configures the authentication credentials in the local Git configuration, which allows your workflow scripts to perform authenticated Git commands (such as git push or git fetch) [1][2]. The action automatically removes these credentials during the post-job cleanup process [1][2]. While there have been community discussions and proposals to change this default to false to further enhance security—specifically to minimize the risk of accidental credential exposure—the default behavior has not been altered in the official releases [5]. If you prefer to disable this behavior for security reasons, you can explicitly set the input to false in your workflow file: - uses: actions/checkout@v4 with: persist-credentials: false
Citations:
- 1: https://github.com/actions/checkout/tree/v6.0.2?tab=readme-ov-file
- 2: https://github.com/marketplace/actions/checkout?version=v3.6.0
- 3: https://github.com/actions/checkout/tree/v6.0.2
- 4: https://spacelift.io/blog/github-actions-checkout
- 5: Change the default value of persist-credentials to false actions/checkout#1687
🌐 Web query:
GitHub security hardening of Actions permissions pull-requests write contents read actions checkout
💡 Result:
To harden GitHub Actions security, you must adhere to the principle of least privilege, especially when using sensitive event triggers [1][2]. 1. Set Default Permissions to Read-Only At the organization or repository level, configure the default GITHUB_TOKEN permissions to read-only [3][4][5]. This ensures that, by default, workflows cannot perform destructive actions or modify the repository [1][5]. You should then explicitly grant higher permissions only at the job level within individual workflow files [1][3][4]. 2. Securely Use pull_request_target The pull_request_target event is a high-risk trigger because it executes in the context of the base repository's default branch, granting access to secrets and potentially high-privilege tokens [6][7][8]. * Prefer the standard pull_request event, which is safer because it automatically restricts GITHUB_TOKEN permissions to read-only and withholds secrets when triggered from a fork [1][7][8]. * Never combine pull_request_target with a checkout of untrusted pull request code [7][8]. Doing so creates a pwn request vulnerability, where malicious code from a fork executes with the base repository's secrets [9][3][6]. * As of June 18, 2026, actions/checkout v7 automatically blocks the checkout of fork pull request code in pull_request_target and certain workflow_run workflows [9][3]. If you must check out untrusted code for specific, verified use cases, you must explicitly set allow-unsafe-pr-checkout: true [10][9][3]. Treat this as a significant security decision [9]. 3. Implement Fine-Grained Permissions Use the permissions key to restrict the GITHUB_TOKEN for every job [1][4]. * Start with an empty permissions block (permissions: {}) or read-all, then add only the specific scopes required [4]. * For checkout, typically only contents: read is needed [4]. * If you do not need the GITHUB_TOKEN at all, set permissions: {} to disable it entirely [4]. 4. General Hardening Best Practices * Use OIDC for cloud authentication instead of storing long-lived secrets [3][4]. * Pin actions to full commit SHAs to prevent supply-chain attacks via compromised action versions [4]. * Avoid using self-hosted runners on public repositories, as they are susceptible to compromise by any user who can open a pull request [2].
Citations:
- 1: https://github.com/github/docs/blob/962a1c8dccb8c0f66548b324e5b921b5e4fbc3d6/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md
- 2: https://github.com/github/docs/blob/main/content/actions/reference/security/secure-use.md
- 3: https://www.aikido.dev/blog/checklist-github-actions
- 4: https://secure-pipelines.com/ci-cd-security/github-actions-security-cheat-sheet/
- 5: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/ci-cd-pipeline-policies/github-cicd-pipeline-policies/ghactions-default-workflow-perm-org-rw
- 6: https://securitylab.github.com/resources/github-actions-new-patterns-and-mitigations/
- 7: https://github.com/github/awesome-copilot/blob/main/skills/github-actions-hardening/references/triggers-and-privilege.md
- 8: https://www.systemshardening.com/articles/cicd/github-actions-pull-request-target-injection/
- 9: https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/
- 10: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
Keep the deployment job read-only and disable checkout credential persistence.
pull-requests: write is job-scoped, so every step in deploy inherits PR write capability even though later steps only build/deploy. Move any PR commentary/commit-status actions to a separate job with pull-requests: write, keep the deploy job scoped to contents: read, and set persist-credentials: false for checkout to avoid later git commands reusing the token locally.
Proposed checkout hardening
- name: Checkout
uses: actions/checkout@v4
+ 与
+ persist-credentials: false🤖 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/landing-deploy.yml around lines 34 - 36, Update the deploy
job in the workflow to retain only contents: read permission, move any PR
commentary or commit-status steps into a separate job scoped with pull-requests:
write, and configure its checkout step with persist-credentials: false.
Source: Linters/SAST tools
| - name: Install Vercel CLI | ||
| run: npm install --global vercel@latest |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- workflow snippet ---\n'
sed -n '45,70p' .github/workflows/landing-deploy.yml 2>/dev/null || true
printf '\n--- package manager/lock files (common names) ---\n'
git ls-files | grep -Ei '(^|/)(package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml)$' || true
printf '\n--- Vercel CLI npm metadata for latest/dist-tags ---\n'
python3 - <<'PY'
import json, urllib.request
url='https://registry.npmjs.org/vercel'
with urllib.request.urlopen(url, timeout=20) as r:
data=json.load(r)
print('current version:', data.get('dist-tags',{}).get('latest'))
print('dist-tags:', data.get('dist-tags'))
PY
printf '\n--- npm config relevant to global installs ---\n'
npm config get prefix 2>/dev/null || true
npm config get cache 2>/dev/null || trueRepository: juspay/neurolink
Length of output: 245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- workflow snippet ---'
sed -n '45,70p' .github/workflows/landing-deploy.yml 2>/dev/null || true
echo
echo '--- package manager/lock files (common names) ---'
git ls-files | grep -Ei '(^|/)(package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml)$' || true
echo
echo '--- Vercel CLI npm metadata for latest/dist-tags ---'
python3 - <<'PY'
import json, urllib.request
url='https://registry.npmjs.org/vercel'
with urllib.request.urlopen(url, timeout=20) as r:
data=json.load(r)
print('current latest version:', data.get('dist-tags',{}).get('latest'))
print('dist-tags:', data.get('dist-tags'))
PY
echo
echo '--- npm config relevant to global installs ---'
npm config get prefix 2>/dev/null || true
npm config get cache 2>/dev/null || trueRepository: juspay/neurolink
Length of output: 3495
Pin the Vercel CLI used for deployments.
npm install --global vercel@latest installs the mutable latest registry tag on each fresh runner, separate from the repo’s checked-in pnpm-lock.yaml, so a newly published release can change deployment behavior without reviewing the workflow. Pin an exact validated version and update it explicitly.
Proposed fix
- run: npm install --global vercel@latest
+ run: npm install --global vercel@<validated-version>🧰 Tools
🪛 zizmor (1.28.0)
[warning] 59-59: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile
(adhoc-packages)
🤖 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/landing-deploy.yml around lines 58 - 59, Update the
“Install Vercel CLI” workflow step to install an exact validated Vercel CLI
version instead of the mutable latest tag, and retain that explicit version in
the command so future upgrades require a deliberate workflow change.
Source: Linters/SAST tools
| const existing = ( | ||
| await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }) | ||
| ).data.find((c) => c.body && c.body.includes(marker)); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow file excerpt =="
sed -n '1,160p' .github/workflows/landing-deploy.yml 2>/dev/null || true
echo
echo "== relevant GitHub Actions REST API usage =="
rg -n "issues.listComments|paginate|github-actions|preview|marker" .github/workflows/landing-deploy.yml .github/workflows 2>/dev/null || true
echo
echo "== package/workflow context =="
ls -la .github/workflows/landing-deploy.yml 2>/dev/null || true
git diff --stat HEAD~1..HEAD 2>/dev/null | sed -n '1,80p' || trueRepository: juspay/neurolink
Length of output: 7410
🌐 Web query:
GitHub REST API list issue comments per_page maximum 100
💡 Result:
The GitHub REST API enforces a maximum limit of 100 results per page for listing issue comments [1][2]. The default value for the per_page parameter is 30 [2][3]. It is important to note that if you provide a value for per_page that exceeds 100, the API will silently clamp the value to the maximum of 100 [4]. The request will still return a 200 OK status, meaning that if your application logic expects a higher number of results per page, it may under-fetch data without explicitly indicating that the limit was capped [4].
Citations:
- 1: https://docs.github.com/rest/issues/comments
- 2: https://docs.github.com/en/rest/issues/comments?apiVersion=--
- 3: https://docs.github.com/en/enterprise-server@3.17/rest/issues/comments
- 4: Document per_page max and silent clamping of oversized values github/docs#45189
🌐 Web query:
docs github actions github-script github.rest.issues.updateComment available actions rest API
💡 Result:
The github.rest.issues.updateComment method is available and is the standard way to update issue and pull request comments when using the actions/github-script action [1][2][3]. In the actions/github-script action, the github object provides a pre-authenticated Octokit client that includes the @octokit/plugin-rest-endpoint-methods plugin [1][4]. Starting with version 5 of actions/github-script, all REST API methods were moved under the github.rest namespace [1][5]. To use github.rest.issues.updateComment, you must provide the following required parameters [6][2]: owner: The owner of the repository. repo: The name of the repository. comment_id: The unique integer ID of the comment you wish to update. body: The new content of the comment (string). Below is a typical example of how this is used within an actions/github-script workflow step: await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: comment_id_variable, body: 'New comment body content' }); For comprehensive details on the parameters and behavior of this endpoint, you should refer to the official GitHub REST API documentation for "Update an issue comment" [6][7]. The github-script action itself relies on the underlying octokit/rest.js client, so you can also reference the octokit/plugin-rest-endpoint-methods documentation for further technical details [1][5].
Citations:
- 1: https://github.com/actions/github-script?tab=readme-ov-file
- 2: https://github.com/nmfs-ost/FIT-test-quality-checks/blob/main/.github/workflows/generate-post-acceptance-checklist.yml
- 3: https://github.com/llnl/Surfactant/blob/94e966e2e2d91e42422a0d82460005d097bed1bb/.github/workflows/regressions-comment.yml
- 4: https://github.com/marketplace/actions/github-script?version=v6.3.2
- 5: https://github.com/actions/github-script
- 6: https://docs.github.com/en/rest/issues/comments?apiVersion=--
- 7: https://docs.github.com/rest/issues/comments
🌐 Web query:
Octokit paginate list issue comments maxPerPage 100
💡 Result:
When using Octokit to list issue comments, you can set per_page to 100 to optimize the number of requests made during pagination [1][2]. The per_page parameter defaults to 30, but the GitHub REST API supports a maximum of 100 per page [1][2]. You can use the octokit.paginate method in combination with the relevant REST endpoint method. Below is an example of how to fetch all issue comments for a specific issue using this configuration: const comments = await octokit.paginate(octokit.rest.issues.listComments, { owner: "OWNER", repo: "REPO", issue_number: 123, per_page: 100, }); In this example, octokit.paginate will automatically handle fetching subsequent pages until all results are retrieved [3][4]. The per_page: 100 setting ensures that each individual request retrieves the maximum number of items allowed, which helps minimize API overhead and reduce the likelihood of hitting rate limits [1][2].
Citations:
- 1: https://github.com/octokit/plugin-paginate-rest.js/
- 2: https://www.npmjs.com/package/@octokit/plugin-paginate-rest
- 3: https://actions-cool.github.io/octokit-rest/guide/05_pagination/
- 4: https://michaelheap.com/octokit-pagination/
Make the sticky-comment lookup authoritative.
listComments is capped at 100 results per page, so PRs with more than 100 comments can get a new preview comment instead of updating the existing one. Also pin the lookup to the workflow bot author, since any comment containing the marker will currently be updated. Paginate the comments and require c.user?.login === 'github-actions[bot]' before updating.
🤖 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/landing-deploy.yml around lines 103 - 105, Update the
sticky-comment lookup around github.rest.issues.listComments to paginate through
all comment pages rather than limiting the search to one page, and only select a
comment whose c.user?.login equals github-actions[bot] in addition to containing
marker. Preserve the existing update-or-create flow using the authoritative
matching comment.
✅ APPROVEDThis PR successfully migrates the landing site deployment from Vercel's Git integration to GitHub Actions, solving the persistent problem where fork PRs show red "requires authorization" status that camouflages real build failures. Summary of ChangesFile Added: The new workflow:
Verification Status✅ Workflow tested end-to-end: The preview deployment succeeded (comment ID 5122784624 shows working preview URL) Why This Is Safe
Impact Assessment
RecommendationAPPROVED — This change cleanly solves a persistent blocker for external contributions without introducing any risks. The workflow has been validated end-to-end with a successful preview deployment. Reviewed by Yama on 2026-07-29 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with 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.
Inline comments:
In @.github/workflows/landing-deploy.yml:
- Around line 40-48: Update the PR preview job in the workflow around the
same-repository condition and Vercel deployment steps to use a preview-scoped,
read-only Vercel token for pull request builds. Keep production-capable Vercel
credentials available only to the release-only job or protected environment, and
replace any PR-path references to secrets.VERCEL_TOKEN while preserving the
existing fork exclusion behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5386a660-d9be-4351-972c-134ecf7ae829
📒 Files selected for processing (1)
.github/workflows/landing-deploy.yml
| # Same-repo refs only. Fork PRs skip → no check, rather than a red one. | ||
| if: >- | ||
| github.event_name != 'pull_request' || | ||
| github.event.pull_request.head.repo.full_name == github.repository | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| env: | ||
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | ||
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file exists and line count =="
if [ -f .github/workflows/landing-deploy.yml ]; then
wc -l .github/workflows/landing-deploy.yml
else
echo "missing .github/workflows/landing-deploy.yml"
fd -a 'landing-deploy\.yml$' .
fi
echo "== relevant workflow contents =="
if [ -f .github/workflows/landing-deploy.yml ]; then
nl -ba .github/workflows/landing-deploy.yml | sed -n '1,150p'
fi
echo "== workflow files containing VERCEL_TOKEN references =="
rg -n "VERCEL_(TOKEN|ORG_ID|PROJECT_ID)" .github/workflows || trueRepository: juspay/neurolink
Length of output: 302
🌐 Web query:
GitHub Actions pull requests from forks secrets security no-secrets documentation
💡 Result:
GitHub Actions security policy for pull requests originating from forks is designed to prevent unauthorized access to sensitive repository secrets [1][2][3]. By default, workflows triggered by the pull_request event for a pull request from a fork do not have access to secrets, and they are provided with a read-only GITHUB_TOKEN [4][1][2]. If a workflow requires access to secrets for a pull request from a fork, developers often use the pull_request_target event [5][4][6]. This event runs in the context of the base repository and does have access to secrets and a read/write GITHUB_TOKEN [5][4][2]. However, using this event introduces significant security risks because the workflow uses the repository's workflow file and is considered trusted, even though it is processing an untrusted pull request [5][7]. To securely handle these scenarios, follow these best practices: 1. Avoid checking out untrusted code: Never use actions/checkout to check out the head of an untrusted pull request in a workflow that has access to secrets [5][2][6]. Doing so allows the pull request's code (such as malicious build scripts or dependencies) to execute with the full privileges of the workflow, potentially leading to secret exfiltration [8][2][6]. 2. Use separate workflows: If you must test code from a fork and also perform actions requiring secrets, split the task into two workflows [6]. Use a pull_request event (without secrets) to test the untrusted code, and a separate pull_request_target event to perform the privileged operations, ensuring the latter never processes or executes the untrusted pull request code [2][6]. 3. Understand checkout protections: As of June 2026, actions/checkout (v7 and later) includes built-in protections that refuse to fetch code from a fork when used within pull_request_target workflows to prevent common "pwn request" vulnerabilities [8]. You should not override these protections (e.g., by setting allow-unsafe-pr-checkout: true) unless you have thoroughly audited the workflow to ensure that no untrusted code is executed [5][8]. 4. Configuration for private repositories: For private repositories, repository owners can adjust settings to enable or disable access to secrets and write tokens for pull requests from forks, but this should be done with extreme caution as it increases the risk of privilege escalation [9]. In summary, standard pull_request workflows are inherently safe from secret exfiltration because they lack access to them [2]. The use of pull_request_target is an intentional design choice for automation that requires elevated permissions, but it mandates strict separation between code execution and privileged secret handling to remain secure [5][2][6].
Citations:
- 1: https://github.com/github/docs/blob/962a1c8dccb8c0f66548b324e5b921b5e4fbc3d6/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md
- 2: https://runs-on.com/github-actions/pull-request-vs-pull-request-target/
- 3: https://docs.github.com/en/code-security/reference/secret-security/secret-types
- 4: https://github.blog/news-insights/product-news/github-actions-improvements-for-fork-and-pull-request-workflows/
- 5: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
- 6: https://www.valtikstudios.com/blog/github-actions-secret-exfiltration
- 7: https://stackoverflow.com/questions/76952023/how-to-make-github-actions-safely-access-secrets-for-prs-created-from-forks
- 8: https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/
- 9: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== relevant workflow contents =="
awk '{printf "%5d %s\n", NR, $0}' .github/workflows/landing-deploy.yml | sed -n '1,140p'
echo
echo "== workflow files using Vercel tokens/secrets =="
rg -n "VERCEL_(TOKEN|ORG_ID|PROJECT_ID)|pull_request|pull_request_target|environment:" .github/workflows || true
echo
echo "== git diff stat/name/status =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat 2>/dev/null || trueRepository: juspay/neurolink
Length of output: 10010
🌐 Web query:
GitHub Actions pull_request same repository secrets access default security
💡 Result:
GitHub Actions handles secret access for pull requests differently depending on the source of the pull request and the specific trigger used [1][2][3]. For pull requests originating from a fork, GitHub enforces strict isolation for the pull_request event by default [1][2][3]: - Workflows triggered by pull_request for pull requests from forks do not have access to repository or organization secrets [1][4][3]. - They are also issued a read-only GITHUB_TOKEN to prevent unauthorized modifications to the repository [1][5][3]. For pull requests originating from the same repository (i.e., branches created directly within the base repository), workflows triggered by pull_request have access to repository secrets [2]. This is permitted because the user creating the pull request already possesses write access to the repository [2]. To handle scenarios requiring secret access for pull requests from forks, the pull_request_target trigger is available [1][2][6]. This trigger operates with elevated trust, granting access to secrets and a read/write GITHUB_TOKEN [1][7][3]. However, it is critical to note that pull_request_target executes the workflow file from the base repository's default branch rather than the pull request's branch [1][7]. While this provides a security boundary, it remains highly dangerous if the workflow subsequently checks out and runs code from the untrusted pull request branch [1][6][8]. If a workflow triggered by pull_request_target executes untrusted code, that code can compromise the repository and access the secrets available to the workflow [9][8][3]. In summary, the security model is designed to prevent secrets from leaking to untrusted fork code by withholding them from the standard pull_request event [2][3]. The pull_request_target trigger is a powerful tool for automation, but it must be used with extreme caution to ensure no untrusted input or code influences the execution of privileged steps [1][8][3].
Citations:
- 1: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
- 2: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/
- 3: https://www.paulserban.eu/blog/post/pullrequest-vs-pullrequesttarget-the-github-actions-trigger-hiding-a-security-nightmare/
- 4: https://github.com/github/docs/blob/main/content/code-security/reference/secret-security/understanding-github-secret-types.md
- 5: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository
- 6: https://stackoverflow.com/questions/76952023/how-to-make-github-actions-safely-access-secrets-for-prs-created-from-forks
- 7: https://docs.github.com/en/enterprise-cloud@latest/actions/reference/security/securely-using-pull_request_target
- 8: https://github.blog/changelog/2025-11-07-actions-pull_request_target-and-environment-branch-protections-changes/
- 9: https://docs.github.com/en/actions/reference/security/secure-use
Use a preview-scoped Vercel token for PR builds.
This preview path now includes same-repository PRs and loads VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID before running PR-controlled checkout/build commands. Forks are still excluded from secret-bearing runs, but not all same-repo PRs are equal; use a read-only preview deployment token for PRs and keep production-capable credentials behind a release-only job or protected environment. Also applies to the Vercel steps that use secrets.VERCEL_TOKEN.
🤖 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/landing-deploy.yml around lines 40 - 48, Update the PR
preview job in the workflow around the same-repository condition and Vercel
deployment steps to use a preview-scoped, read-only Vercel token for pull
request builds. Keep production-capable Vercel credentials available only to the
release-only job or protected environment, and replace any PR-path references to
secrets.VERCEL_TOKEN while preserving the existing fork exclusion behavior.
|
🎉 This PR is included in version 10.8.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Problem
Every fork PR shows a red Vercel check. It is not a build failure — it is an authorization prompt. The status links to
vercel.com/git/authorize?..., not to a deployment.Vercel's docs: "when you receive a pull request from a fork of your repository, Vercel will require authorization from you or a team member to deploy the pull request… The authorization step will be skipped if the commit author is already a team member."
Confirmed 8/8 across the open PRs:
#1237,#1172)#1238,#1241,#1242,#1233,#1204,#1200)Two things make this unfixable in-repo:
vercel.json/ignoreCommandnever runs on a fork PR — no repo-side config can suppress it.gitForkProtection: true. Contributors can't be added as team members to skip the gate, and turning fork protection off would let fork code build against the project's environment — a security downgrade, not a fix.Net effect today: every external contribution is permanently non-
CLEAN, and real failures are camouflaged by a guaranteed-red check. (It doesn't block merging — Vercel isn't inrequired_status_checks— but it makes "is this PR green?" unreadable.)Fix
Drive the landing deploy from Actions instead, where the trigger is ours to gate:
releasetouchinglanding/**→ production deploylanding/**→ preview deploy + a sticky PR comment with the URLThe
if:states the intent; GitHub withholding secrets from forkpull_requestruns is what actually enforces it — a fork cannot deploy even if the condition were wrong.vercel pullfetches project settings, sorootDirectory: landing, the SvelteKit preset and thelanding/vercel.jsonredirects keep working without being duplicated here.Follow-up (not in this PR): once this is confirmed green, disconnect the Vercel Git integration so the two don't both deploy.
Verification
This PR modifies
.github/workflows/landing-deploy.yml, which is in its ownpaths:filter — so it deploys itself. A preview URL commented below is the proof it works end to end.Secrets
VERCEL_TOKEN/VERCEL_ORG_ID/VERCEL_PROJECT_IDare configured on the repo. The token is scoped to this use and independently revocable.Summary by CodeRabbit