Add use-pr-linker workflow to auto-link PRs to issues#279
Conversation
WalkthroughThis PR adds a GitHub Actions workflow that automatically links pull requests to related issues. The workflow triggers on ChangesPR-to-Issue Linking Automation
Estimated Code Review Effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
1c00bbc to
37dcf13
Compare
Signed-off-by: Ivanmeneges <ivan.anil016@gmail.com>
37dcf13 to
11e0cbf
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/use-pr-linker.yml:
- Around line 19-21: Replace the mutable ref in the reusable workflow call so
the "uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop" line pins
to an immutable commit SHA (e.g., replace "`@develop`" with the specific commit
SHA like "`@6bb3c38b8f5643b906a0d7f4baaede996766b71e`") to prevent remote changes
from getting executed with the injected secret ACTION_PAT; ensure the "secrets:
ACTION_PAT" mapping is left intact after updating the ref.
- Around line 3-10: The workflow exposes secrets to untrusted forks because it
uses pull_request_target and forwards secrets.ACTION_PAT to the external mutable
workflow mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop (used as
GH_TOKEN) while deriving REPO/NUMBER from PR title/body; to fix, change the
trigger from pull_request_target to pull_request if you don't need elevated
permissions for fork PRs, otherwise stop passing ACTION_PAT to mutable external
workflows and instead pin the called workflow to a commit SHA (or vendor its
steps), add a guard that verifies github.event.pull_request.base.repo.full_name
=== github.repository (i.e., only same-repo PRs) before exposing secrets and
restrict permissions to the minimum necessary (avoid issues: write or remove
destructive scopes), and validate/whitelist the derived REPO/NUMBER so comments
can only target the base repo.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2c78847b-e420-45d3-bc09-f4609fb69f64
📒 Files selected for processing (1)
.github/workflows/use-pr-linker.yml
| on: | ||
| pull_request_target: | ||
| types: | ||
| - opened | ||
| - edited | ||
| - synchronize | ||
| - reopened | ||
| - closed |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Fetch and examine the external workflow to understand its permissions requirements
curl -s https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml | head -50Repository: mosip/audit-manager
Length of output: 1283
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect local workflow
echo "== Local: .github/workflows/use-pr-linker.yml =="
nl -ba .github/workflows/use-pr-linker.yml | sed -n '1,200p'
echo
echo "== Local: search for secret/ref passing =="
rg -n "mosip/kattu|ACTION_PAT|workflow_call|uses:" .github/workflows/use-pr-linker.yml || true
# 2) Inspect external workflow more thoroughly
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
echo
echo "== External: head (first 220 lines) =="
curl -s "$URL" | nl -ba | sed -n '1,220p'
echo
echo "== External: grep for ACTION_PAT usage and fork-related checks =="
curl -s "$URL" | rg -n "ACTION_PAT|secrets\.ACTION_PAT|fork|fromFork|pull_request\.head|head_repo|repository_owner|is_fork|base_repo|permissions" || true
echo
echo "== External: tail (last 220 lines) =="
curl -s "$URL" | nl -ba | tail -n 220Repository: mosip/audit-manager
Length of output: 173
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Local: .github/workflows/use-pr-linker.yml =="
cat -n .github/workflows/use-pr-linker.yml | sed -n '1,220p'
echo
echo "== Local: search for uses/ref + secret passing =="
rg -n "mosip/kattu|ACTION_PAT|workflow_call|secrets:|secrets\.ACTION_PAT|uses:" .github/workflows/use-pr-linker.yml || true
echo
echo "== External: full text is unknown size; search by patterns =="
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
curl -s "$URL" > /tmp/link-pr-to-issue.yml
echo
echo "== External: occurrences =="
rg -n "ACTION_PAT|secrets\.ACTION_PAT|fork|fromFork|is_fork|head_repo|head.*repo|pull_request\.head|repository_owner|base_repo|permissions" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: show lines around ACTION_PAT usage =="
rg -n "ACTION_PAT|secrets\.ACTION_PAT" -n /tmp/link-pr-to-issue.yml | head -n 50 || true
# Print a context window around first 3 matches (if any)
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
with open(path,'r',encoding='utf-8') as f:
lines=f.readlines()
idx=[]
for i,l in enumerate(lines, start=1):
if "ACTION_PAT" in l:
idx.append(i)
for i in idx[:3]:
start=max(1,i-25); end=min(len(lines), i+40)
print(f"\n--- {path}: lines {start}-{end} (match at {i}) ---")
for j in range(start, end+1):
print(f"{j}\t{lines[j-1].rstrip()}")
PYRepository: mosip/audit-manager
Length of output: 7471
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Local: file length =="
wc -l .github/workflows/use-pr-linker.yml
echo
echo "== Local: full file =="
cat -n .github/workflows/use-pr-linker.yml
echo
echo "== External: look for any fork/untrusted checks =="
curl -s "https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml" > /tmp/link-pr-to-issue.yml
rg -n "fork|fromFork|is_fork|head_repo|head\.repo|pull_request\.head|repository_owner|base_repo" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: where GH_TOKEN is used (context) =="
rg -n "GH_TOKEN: \$\{\{ secrets\.ACTION_PAT \}\}|Authorization: Bearer \$GH_TOKEN|issues/comments|api.github.com/search/issues|curl -s -X (POST|PUT|PATCH)|curl -s -X DELETE" /tmp/link-pr-to-issue.yml
echo
echo "== External: show around first POST/PUT/PATCH (if any) =="
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
with open(path,'r',encoding='utf-8') as f: lines=f.readlines()
targets=[]
for i,l in enumerate(lines, start=1):
if re.search(r'curl -s -X (POST|PUT|PATCH)', l):
targets.append(i)
for i in targets[:5]:
s=max(1,i-20); e=min(len(lines), i+25)
print(f"\n--- {path}: lines {s}-{e} (match at {i}) ---")
for j in range(s,e+1):
print(f"{j}\t{lines[j-1].rstrip()}")
PYRepository: mosip/audit-manager
Length of output: 1124
🏁 Script executed:
#!/bin/bash
set -euo pipefail
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
curl -s "$URL" > /tmp/link-pr-to-issue.yml
echo "== External: check for any fork/untrusted gating patterns =="
rg -n "fork|fromFork|is_fork|head\.repo|base\.repo|repository_owner|event\.pull_request\.head" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: locate comment creation / POST usage =="
rg -n "curl -s -X POST|issues/comments" /tmp/link-pr-to-issue.yml || true
echo
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
lines=open(path,'r',encoding='utf-8').read().splitlines()
# find likely comment/PR-link marker logic
patterns=["MARKER=","pr-linker:","pr-linker:","<!-- pr-linker:","issues/comments","COMMENT_ID","SOURCE_REPO","ISSUE_REPO","REPO="]
for p in patterns:
hits=[i+1 for i,l in enumerate(lines) if p in l]
if hits:
print(f"\n-- first hits for {p} at lines {hits[:5]} --")
i=hits[0]
s=max(1,i-35); e=min(len(lines), i+70)
for j in range(s,e+1):
print(f"{j}\t{lines[j-1]}")
PYRepository: mosip/audit-manager
Length of output: 36974
🏁 Script executed:
#!/bin/bash
set -euo pipefail
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
curl -s "$URL" > /tmp/link-pr-to-issue.yml
echo "== External: search for any POST (including multiline -X POST) and comment creation =="
rg -n "curl -s.*-X\s*POST|X POST|issues/.*/comments\?per_page|issues/\$NUMBER/comments" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: show section around where it likely posts comment (first match on issues/$NUMBER/comments without -X DELETE) =="
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
lines=open(path,'r',encoding='utf-8').read().splitlines()
# find first occurrence of "issues/$NUMBER/comments" and show nearby lines
for i,l in enumerate(lines):
if "issues/$NUMBER/comments" in l and "-X DELETE" not in l:
start=max(0,i-40); end=min(len(lines)-1,i+80)
print(f"--- lines {start+1}-{end+1} around first non-delete use at {i+1} ---")
for j in range(start,end+1):
print(f"{j+1}\t{lines[j]}")
break
PY
echo
echo "== External: check for any explicit fork/untrusted checks around PR events =="
rg -n "fork|fromFork|is_fork|head_repo|base_repo|repository_owner|pull_request\.head|security|untrusted|trusted" /tmp/link-pr-to-issue.yml || trueRepository: mosip/audit-manager
Length of output: 394
🏁 Script executed:
#!/bin/bash
set -euo pipefail
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
curl -s "$URL" > /tmp/link-pr-to-issue.yml
echo "== External: grep for POST usage (comment creation) =="
rg -n "curl\s+-s\s+-w.*-X\s+POST|curl.*-X\s+POST|issues/comments\?" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: show around CREATE_RESPONSE (-X POST) =="
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
lines=open(path,'r',encoding='utf-8').read().splitlines()
create=[]
for i,l in enumerate(lines, start=1):
if "CREATE_RESPONSE" in l or "-X POST" in l:
create.append(i)
print("matches:", create[:10])
start_line = create[0] if create else None
if start_line:
s=max(1,start_line-80); e=min(len(lines), start_line+120)
for j in range(s,e+1):
print(f"{j}\t{lines[j-1]}")
PY
echo
echo "== External: search for any fork/untrusted gating =="
rg -n "fork|fromFork|is_fork|head_repo|base_repo|repository_owner|pull_request\.head|security|untrusted|trusted" /tmp/link-pr-to-issue.yml || true
echo
echo "== External: search for any validation of issue targets from PR body =="
rg -n "grep -Eo '([a-zA-Z0-9_.-]+/|cut -d'#'|ISSUES=|ISSUE_COUNT|SOURCE_REPO|REPO=\" /tmp/link-pr-to-issue.yml || trueRepository: mosip/audit-manager
Length of output: 4638
🏁 Script executed:
#!/bin/bash
set -euo pipefail
URL="https://raw.githubusercontent.com/mosip/kattu/develop/.github/workflows/link-pr-to-issue.yml"
curl -s "$URL" > /tmp/link-pr-to-issue.yml
python3 - <<'PY'
import re
path="/tmp/link-pr-to-issue.yml"
lines=open(path,'r',encoding='utf-8').read().splitlines()
def show_around(needle, radius=60, max_print=220):
for i,l in enumerate(lines):
if needle in l:
s=max(0,i-radius); e=min(len(lines)-1,i+radius)
print(f"\n--- around '{needle}' (lines {s+1}-{e+1}) ---")
for j in range(s,e+1):
print(f"{j+1}\t{lines[j]}")
return True
return False
needles=[
"ISSUES=$(echo \"$BODY\"",
"grep -Eo '([a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+)?#[0-9]+'",
"if [[ \"$ISSUE\" == *\"/\"* ]]; then",
"REPO=\"$SOURCE_REPO\"",
"Processing $REPO#$NUMBER",
]
for n in needles:
ok=show_around(n, radius=55)
if not ok:
print(f"\n--- needle not found: {n} ---")
PY
echo "== External: grep for any comparisons between REPO and SOURCE_REPO =="
rg -n "REPO.*SOURCE_REPO|SOURCE_REPO.*REPO|if.*REPO|allow|whitelist" /tmp/link-pr-to-issue.yml || trueRepository: mosip/audit-manager
Length of output: 19811
Major security risk: pull_request_target passing ACTION_PAT to mutable external workflow enables fork-driven issue/comment writes.
.github/workflows/use-pr-linker.ymlruns onpull_request_targetand passessecrets.ACTION_PATintomosip/kattu/.github/workflows/link-pr-to-issue.yml@develop(mutable ref), withissues: writepermissions.- The called workflow requires
secrets.ACTION_PATand uses it asGH_TOKENto search, delete, PATCH, and POST issue comments via the GitHub API. - It derives the target
REPO/NUMBERfrom PR title/body: it acceptsowner/repo#number(or defaults to the base repo) and then useshttps://api.github.com/repos/$REPO/issues/$NUMBER/comments...—there’s no fork/untrusted-source gating shown before using the PAT.
Verify whether pull_request_target is necessary for fork PRs; if not, switch to pull_request. If it must remain, gate secret usage to same-repo PRs only, pin the external workflow to a commit SHA (or vendor it), and restrict comment targets to the base repo.
🧰 Tools
🪛 zizmor (1.25.2)
[error] 3-10: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🤖 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/use-pr-linker.yml around lines 3 - 10, The workflow
exposes secrets to untrusted forks because it uses pull_request_target and
forwards secrets.ACTION_PAT to the external mutable workflow
mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop (used as GH_TOKEN)
while deriving REPO/NUMBER from PR title/body; to fix, change the trigger from
pull_request_target to pull_request if you don't need elevated permissions for
fork PRs, otherwise stop passing ACTION_PAT to mutable external workflows and
instead pin the called workflow to a commit SHA (or vendor its steps), add a
guard that verifies github.event.pull_request.base.repo.full_name ===
github.repository (i.e., only same-repo PRs) before exposing secrets and
restrict permissions to the minimum necessary (avoid issues: write or remove
destructive scopes), and validate/whitelist the derived REPO/NUMBER so comments
can only target the base repo.
| uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop | ||
| secrets: | ||
| ACTION_PAT: ${{ secrets.ACTION_PAT }} No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Fetch the latest commit SHA for the develop branch of mosip/kattu
curl -s https://api.github.com/repos/mosip/kattu/commits/develop | jq -r '.sha'Repository: mosip/audit-manager
Length of output: 104
Critical: Pin reusable workflow to an immutable commit SHA instead of @develop.
This workflow runs on pull_request_target and passes secrets.ACTION_PAT to mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop; using a mutable ref allows the called workflow to change and potentially exfiltrate/abuse the secret. Pin it to a commit SHA (e.g., 6bb3c38b8f5643b906a0d7f4baaede996766b71e).
🔒 Proposed fix
jobs:
call-linker:
- uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop
+ uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@6bb3c38b8f5643b906a0d7f4baaede996766b71e
secrets:
ACTION_PAT: ${{ secrets.ACTION_PAT }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop | |
| secrets: | |
| ACTION_PAT: ${{ secrets.ACTION_PAT }} | |
| uses: mosip/kattu/.github/workflows/link-pr-to-issue.yml@6bb3c38b8f5643b906a0d7f4baaede996766b71e | |
| secrets: | |
| ACTION_PAT: ${{ secrets.ACTION_PAT }} |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 19-19: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 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/use-pr-linker.yml around lines 19 - 21, Replace the
mutable ref in the reusable workflow call so the "uses:
mosip/kattu/.github/workflows/link-pr-to-issue.yml@develop" line pins to an
immutable commit SHA (e.g., replace "`@develop`" with the specific commit SHA like
"`@6bb3c38b8f5643b906a0d7f4baaede996766b71e`") to prevent remote changes from
getting executed with the injected secret ACTION_PAT; ensure the "secrets:
ACTION_PAT" mapping is left intact after updating the ref.
Summary
Adds
use-pr-linker.ymlworkflow that calls the reusable PR-to-issue linker frommosip/kattu(@develop).Notes
ACTION_PATrepository secret to be configured.develop-java21.Summary by CodeRabbit