Skip to content
Open
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
38 changes: 38 additions & 0 deletions .github/workflows/pr-title-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: PR Title Validation

on:
pull_request:
types: [opened, edited, synchronize, reopened]
pull_request_target:
types: [opened, edited, synchronize, reopened]
Comment on lines +3 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- workflow ---'
cat -n .github/workflows/pr-title-validation.yml

printf '%s\n' '--- workflow files and trigger references ---'
git ls-files '.github/workflows/*'
rg -n --glob '.github/workflows/*' 'pull_request_target|pull_request:|concurrency:|workflow_run|pr-title|title' .github/workflows

Repository: LLM-CR-EVAL/nx-242-cr1

Length of output: 3489


🌐 Web query:

GitHub Actions workflow both pull_request and pull_request_target same event two workflow runs documentation

💡 Result:

Yes, you can configure a single GitHub Actions workflow to be triggered by both pull_request and pull_request_target events [1][2]. You can include both in the on block of your workflow file [1]. When you define a workflow to listen for both events, a single pull request activity (such as opening or synchronizing a PR) can trigger two separate workflow runs: one for pull_request and one for pull_request_target [3][2]. Key differences to consider when using them together: 1. Context and Security: - pull_request: Runs the workflow code from the pull request's merge commit [4][3]. It is restricted to a read-only GITHUB_TOKEN and does not have access to repository secrets to prevent unauthorized code execution from forks [4][5]. - pull_request_target: Runs the workflow code from the target repository's base branch (the version of the code that is trusted) [4][3]. This allows the workflow to access repository secrets and a read/write GITHUB_TOKEN, making it suitable for tasks like labeling or commenting on PRs [3][2]. 2. Triggering: - Both events support the same activity types (such as opened, synchronize, and reopened) [6][2]. - If a pull request has a merge conflict, pull_request workflows will not run, but pull_request_target workflows will still trigger [6][2]. Because these two events run in different contexts, you should be careful if you use them in the same workflow. It is common practice to use conditional logic (e.g., if: github.event_name == 'pull_request') within your jobs or steps if you need to perform different actions depending on which event triggered the specific run [7]. Additionally, exercise extreme caution when using pull_request_target, as it executes code from your base branch while handling potentially untrusted data from a pull request [4][2]. Top results: [6][4][1][2]

Citations:


Keep only one pull request trigger.

The matching pull request event starts one run for pull_request and another for pull_request_target. Remove one trigger to prevent duplicate validation runs and check results.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 3-7: 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/pr-title-validation.yml around lines 3 - 7, Remove either
the pull_request or pull_request_target trigger from the workflow, keeping only
one event configuration with its existing activity types to ensure pull request
title validation runs once.


jobs:
validate-pr-title:
if: ${{ github.repository_owner == 'nrwl' }}
name: Validate PR Title
runs-on: ubuntu-latest
Comment on lines +9 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
file=".github/workflows/pr-title-validation.yml"
printf '%s\n' '--- workflow ---'
cat -n "$file"
printf '%s\n' '--- permission and checkout references ---'
rg -n '^\s*permissions:|^\s+(contents|actions|pull-requests|statuses|checks):|actions/checkout|github\.token|gh |curl |git push|git commit' "$file" || true
printf '%s\n' '--- bounded structural check ---'
python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/pr-title-validation.yml")
text = p.read_text()
lines = text.splitlines()
permissions = [i + 1 for i, line in enumerate(lines) if line.strip() == "permissions:"]
checkout = [i + 1 for i, line in enumerate(lines) if "actions/checkout" in line]
print({"permissions_lines": permissions, "checkout_lines": checkout})
PY

Repository: LLM-CR-EVAL/nx-242-cr1

Length of output: 1691


Security Misconfiguration (CWE-732): Incorrect Permission Assignment for Critical Resource

Reachability: External

Declare least-privilege permissions.

Add workflow-level permissions: contents: read. The workflow currently inherits repository or organization defaults, although it only needs read access to repository contents.

🤖 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/pr-title-validation.yml around lines 9 - 13, Add
workflow-level least-privilege permissions in the PR title validation workflow
by declaring contents read access near the workflow configuration, before the
jobs section. Keep the existing validate-pr-title job unchanged.

Source: Linters/SAST tools

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# For pull_request_target, we need to checkout the base branch
ref: ${{ github.event.pull_request.base.ref }}
Comment on lines +15 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/pr-title-validation.yml' '.github/workflows/*' | sed -n '1,80p'
printf '%s\n' '--- target workflow ---'
cat -n .github/workflows/pr-title-validation.yml
printf '%s\n' '--- checkout and shell-related references ---'
rg -n -C 4 'actions/checkout|pull_request_target|run:|github\.event|commit-lint|commit message|title' .github/workflows/pr-title-validation.yml

Repository: LLM-CR-EVAL/nx-242-cr1

Length of output: 3232


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path

workflow = Path(".github/workflows/pr-title-validation.yml").read_text()
start = workflow.index("      - name: Create PR message file")
end = workflow.index("\n      - name: Validate PR title", start)
block = workflow[start:end]
print("--- workflow block ---")
print(block)

# Model GitHub expression interpolation into the run block. The payload is
# data supplied by the pull request, not repository code.
title = 'fix(core): valid $(cat "$GITHUB_WORKSPACE/.git/config")'
body = 'safe\nEOF\nprintf "injected-command-ran\\n"'
generated = block.replace("${{ github.event.pull_request.title }}", title)
generated = generated.replace("${{ github.event.pull_request.body }}", body)
print("--- generated run content ---")
print(generated)

print("--- credential persistence indicators ---")
checkout = workflow[workflow.index("uses: actions/checkout@v4"):workflow.index("\n\n", workflow.index("uses: actions/checkout@v4"))]
print(checkout)
print("persist-credentials explicitly disabled:", "persist-credentials: false" in checkout)
PY

Repository: LLM-CR-EVAL/nx-242-cr1

Length of output: 889


🌐 Web query:

actions/checkout v4 persist-credentials default true local git config official documentation

💡 Result:

In actions/checkout v4, the persist-credentials input defaults to true [1][2][3]. When persist-credentials is set to true (the default), the action configures the authentication token or SSH key to enable authenticated Git commands for subsequent steps in the workflow [1][4]. Official documentation and technical implementation details confirm that this configuration is managed through the local Git configuration [1][2]. Regarding how these credentials are stored: - In actions/checkout v4, the authentication token is persisted directly into the repository's .git/config file [5]. - Note that in later versions (e.g., v6 and beyond), the implementation was improved to store credentials in a separate file within $RUNNER_TEMP, with the .git/config file only containing a pointer to that location [6][7][5]. This change prevents credentials from being included if the workspace directory is uploaded as a build artifact [5]. If you do not require authenticated Git commands in subsequent steps, you can set persist-credentials: false to opt out and prevent the credentials from being written to the configuration [1][5]. Sources: [1] https://github.com/actions/checkout/blob/v4/README.md [2] https://github.com/actions/checkout/blob/v4/action.yml [3] https://github.com/actions/checkout/blob/v4.1.1/action.yml [6] https://github.com/actions/checkout/tree/v6.0.2?tab=readme-ov-file [7] https://github.com/actions/checkout/tree/v6.0.0 [5] cli/go-gh#225

Citations:


Sensitive Data Exposure (CWE-522): Insufficiently Protected Credentials

Reachability: External · Exploitability: Trivial

Disable checkout credential persistence.

The workflow interpolates the pull request title and body into shell commands. An attacker can use this injection to read the token that actions/checkout@v4 stores in .git/config by default. Set persist-credentials: false.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 15-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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/pr-title-validation.yml around lines 15 - 19, Update the
actions/checkout@v4 step in the workflow to set persist-credentials to false
alongside the existing ref configuration, preventing the checkout action from
storing the repository token in Git configuration.

Source: Linters/SAST tools


- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Create PR message file
run: |
mkdir -p /tmp
cat > /tmp/pr-message.txt << 'EOF'
${{ github.event.pull_request.title }}

${{ github.event.pull_request.body }}
EOF
Comment on lines +29 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the title independently of the body.

The supplied scripts/commit-lint.js, Lines 1-83, tests an unanchored regular expression against the entire file. This workflow appends the body at Line 32. An invalid title can pass when the body contains a valid type(scope): subject line. A title with a valid prefix and invalid suffix can also pass.

Write only the title for a title check, or anchor the validator to the complete first line.

🧰 Tools
🪛 zizmor (1.29.0)

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

(template-injection)


[error] 32-32: 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/pr-title-validation.yml around lines 29 - 33, Update the
pull-request title validation flow around the heredoc writing
/tmp/pr-message.txt so it validates only github.event.pull_request.title,
excluding the pull-request body. Preserve the existing commit-lint.js invocation
while ensuring invalid title suffixes or valid-looking lines in the body cannot
satisfy the title check.


- name: Validate PR title
run: |
echo "Validating PR title: ${{ github.event.pull_request.title }}"
node ./scripts/commit-lint.js /tmp/pr-message.txt
Loading