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
116 changes: 116 additions & 0 deletions .github/workflows/build-src-cmake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Build source (CMake)

on:
workflow_call:
inputs:
build-target:
description: "Target name as defined by inputs.sh"
required: true
type: string
container-path:
description: "Path to built container at registry"
required: true
type: string
depends-key:
description: "Key needed to access cached depends"
required: true
type: string
depends-host:
description: "Host triplet from depends build"
required: true
type: string
depends-dep-opts:
description: "DEP_OPTS used to build depends"
required: false
type: string
default: ""
runs-on:
description: "Runner label to use (e.g., ubuntu-24.04 or ubuntu-24.04-arm)"
required: true
type: string

# Builds the tree with CMake on top of the depends prefix produced for the
# Autotools jobs, which is what makes this a full-stack check: depends emits
# toolchain.cmake, CMake consumes it, and the unit tests run against the
# result, followed by a short functional smoke list. The full functional suite
# stays with the Autotools jobs, so this job does not bundle artifacts.
jobs:
build-src-cmake:
name: Build source (CMake)
runs-on: ${{ inputs.runs-on }}
container:
image: ${{ inputs.container-path }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
options: --user root
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 50
Comment on lines +45 to +49

@coderabbitai coderabbitai Bot Jul 25, 2026

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 -euo pipefail

echo "== workflow files =="
git ls-files .github/workflows || true

echo "== target workflow excerpt =="
if [ -f .github/workflows/build-src-cmake.yml ]; then
  cat -n .github/workflows/build-src-cmake.yml
fi

echo "== all pull_request_target occurrences =="
rg -n "pull_request_target|pull_request\b|permissions:|GITHUB_TOKEN|checkout|permissions =" .github || true

echo "== changed workflow status =="
git diff -- .github/workflows/build-src-cmake.yml || true

Repository: dashpay/dash

Length of output: 11666


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== build.yml relevant excerpt =="
cat -n .github/workflows/build.yml | sed -n '1,120p'

echo "== build-src-cmake workflow_call references =="
rg -n "build-src-cmake|build_src_cmake|matrix\.sh|workflow_call|event_name|head\.sha|permissions:" .github/workflows/build.yml .github/workflows/build-src-cmake.yml

Repository: dashpay/dash

Length of output: 7034


🌐 Web query:

actions/checkout persist-credentials default true .git/config local credential helper

💡 Result:

The persist-credentials input for the actions/checkout GitHub Action determines whether the authentication token or SSH key used to fetch the repository is configured within the local Git configuration of the checked-out repository [1][2][3]. When set to its default value of true, the action enables your workflow scripts to run authenticated Git commands (such as git push or git pull) without needing to manually configure additional credentials [1][2][4]. The token or key is removed during the action's post-job cleanup [1][2][3]. Important changes regarding how this data is persisted have been introduced in recent versions: 1. Evolution of Storage Method: Historically, actions/checkout directly injected credentials into the repository's .git/config file [1][2][5]. Starting with version v6.0.0, the action was updated to store these credentials in a separate file located within the $RUNNER_TEMP directory [5][6]. It then uses Git's includeIf mechanism to conditionally include these credentials only when accessing the relevant Git directory, improving security and isolation [7][5]. 2. Security Context: Because the default is true, subsequent steps in your workflow have access to these credentials [8]. To minimize risk—especially if your workflow includes untrusted code—you can opt out of this behavior by setting persist-credentials: false in your workflow definition [1][2][4][8]. There have been community discussions and proposals to change this default to false to reduce the risk of accidental token exposure, though as of July 2026, the default remains true [9][8]. If you use persist-credentials: true, ensure your workflow adheres to the principle of least privilege by configuring appropriate repository permissions for the GITHUB_TOKEN [10][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== build.yml build-src-cmatrix inputs =="
cat -n .github/workflows/build.yml | sed -n '210,245p'

echo "== build-container uses workflow_call and checkout refs =="
cat -n .github/workflows/build-container.yml | sed -n '1,80p'

echo "== build-depends workflow_call and checkout refs =="
cat -n .github/workflows/build-depends.yml | sed -n '1,130p'

echo "== cache-depends-sources workflow_call and checkout refs =="
cat -n .github/workflows/cache-depends-sources.yml | sed -n '1,80p'

Repository: dashpay/dash

Length of output: 12296


🌐 Web query:

GitHub Pull_request_target workflows run with contents permission default read pull_request head commit security

💡 Result:

The pull_request_target event is a powerful trigger in GitHub Actions that runs workflows in the context of the base repository's default branch, granting them elevated trust, including access to organization/repository secrets and a write-capable GITHUB_TOKEN [1][2][3]. This trigger was designed to allow automation (like labeling or triage) on pull requests from forks, where standard pull_request events would be restricted [1][4]. Security Risks and Mechanics The primary security risk, often called a "pwn request," occurs when a workflow uses pull_request_target and then checks out the untrusted head commit of a pull request from a fork [5][2]. If that workflow subsequently executes any code from that checkout (e.g., running tests, build scripts, or Makefiles), an attacker can execute arbitrary commands with the base repository's secrets and elevated permissions [2][6]. Mitigation and Recent Changes 1. Automatic Protection: As of June 2026, actions/checkout v7 automatically refuses to fetch fork pull request code in pull_request_target workflows to prevent these vulnerabilities [5]. 2. Opt-Out: If a workflow genuinely requires checking out fork code with elevated trust, authors must explicitly add allow-unsafe-pr-checkout: true to the actions/checkout step [1][5]. This is an intentional hurdle meant to highlight the security risk during code review [5]. 3. Permissions: Setting permissions: contents: read at the job level is a recommended best practice to apply the principle of least privilege [7][8]. However, security researchers warn that this is a partial mitigation [9]. Permissions flags restrict the GITHUB_TOKEN but do not isolate the runner environment [9]. Features such as actions/cache and in-memory OIDC tokens can potentially be exploited by malicious code running on the same runner, regardless of the job's defined permissions [9]. 4. Context Execution: Because pull_request_target workflows run from the default branch, the workflow file itself is trusted [1][6]. The vulnerability only arises if the workflow fetches and executes external, unreviewed code [6]. Recommendation To secure these workflows: - Avoid executing code from a pull request head commit [2]. - If you only need to inspect changes (e.g., labeling), use the GitHub API to read PR data rather than checking out the source code [2]. - If you must use pull_request_target, explicitly define least-privilege permissions [1][7]. - Treat the entire runner environment as a trust boundary, not just the permissions on the GITHUB_TOKEN [9].

Citations:


Do not execute the PR head from this build workflow.

When triggered by pull_request_target, this workflow will checkout github.event.pull_request.head.sha and then run ./ci/dash/build_src_cmake.sh and tests from that ref. Keep PR builds on pull_request, or otherwise isolate execution as read-only and set persist-credentials: false if checkout is needed.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 45-49: 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/build-src-cmake.yml around lines 45 - 49, Update the
workflow trigger and checkout configuration around the checkout step and build
execution so untrusted pull-request head code is not executed in the privileged
workflow. Prefer running this build on pull_request rather than
pull_request_target; if checkout remains necessary under pull_request_target,
isolate it as read-only and set persist-credentials to false before running
ci/dash/build_src_cmake.sh and tests.

Source: Linters/SAST tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The current checkout-v6.1 default prevents the fork-head checkout before PR code executes, so this is not a presently reachable pwn-request path. It is still a blocker because fork coverage is broken; match Dash commit ba143fe163a by adding both allow-unsafe-pr-checkout: true and persist-credentials: false.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Current CMake workflow checkout step =="
sed -n '40,58p' .github/workflows/build-src-cmake.yml

echo "== Referenced precedent commit, relevant workflow changes =="
git show --format=fuller --no-ext-diff --unified=12 ba143fe163a -- .github/workflows 2>/dev/null || \
  echo "Referenced commit is unavailable in this checkout."

Length of output: 1109


@thepastaclaw, agreed. The current workflow lacks both settings, and the original finding overstated the currently reachable pwn-request path: checkout blocks fork-head checkout before PR code runs. The real blocker is restoring fork coverage intentionally while ensuring checkout credentials are not exposed to the executed PR code.

  • Add the explicit unsafe fork-PR checkout opt-in required by actions/checkout v6.1.
  • Disable persisted checkout credentials as defense in depth for the subsequently executed PR-controlled build and test commands.
  • Keep the workflow trigger and existing ref unchanged.

✏️ Learnings added
Learnt from: thepastaclaw
Repo: dashpay/dash PR: 7481
File: .github/workflows/build-src-cmake.yml:45-49
Timestamp: 2026-07-25T22:39:07.618Z
Learning: For `.github/workflows/build-src-cmake.yml`, Dash intentionally executes fork pull-request head code under its `pull_request_target`-based CI flow to retain fork coverage. With `actions/checkout` v6.1 or later, the checkout step must explicitly set `allow-unsafe-pr-checkout: true`; it must also set `persist-credentials: false` so the subsequent PR-controlled build and test commands do not receive persisted checkout credentials.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


  • 📌 Commit the changes to this branch


- name: Initial setup
run: |
git config --global --add safe.directory "$PWD"
shell: bash

- name: Restore depends cache
uses: actions/cache/restore@v5
with:
path: depends/built/${{ inputs.depends-host }}
key: ${{ inputs.depends-key }}
fail-on-cache-miss: true

- name: Rebuild depends prefix
run: |
# Use the HOST and DEP_OPTS from the depends build, not this build-target
# This ensures the build_id matches the cached packages, and it is what
# writes depends/${HOST}/toolchain.cmake.
make -j$(nproc) -C depends HOST="${{ inputs.depends-host }}" ${{ inputs.depends-dep-opts }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
shell: bash

- name: Restore ccache cache
uses: actions/cache/restore@v5
with:
path: |
/cache/ccache
key: ccache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-${{ github.sha }}
restore-keys: |
ccache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-

- name: Build source
run: |
CCACHE_MAXSIZE="600M"
CACHE_DIR="/cache"
mkdir /output
BASE_OUTDIR="/output"
BUILD_TARGET="${{ inputs.build-target }}"
source ./ci/dash/matrix.sh
./ci/dash/build_src_cmake.sh
ccache -X 9
ccache -c
du -hd0 "${BASE_OUTDIR}"
shell: bash

- name: Save ccache cache
if: |
github.event_name == 'push' &&
github.ref_name == github.event.repository.default_branch
uses: actions/cache/save@v5
with:
path: |
/cache/ccache
key: ccache-${{ hashFiles('contrib/containers/ci/ci.Dockerfile', 'depends/packages/*') }}-${{ inputs.build-target }}-${{ github.sha }}

- name: Run unit tests
run: |
BUILD_TARGET="${{ inputs.build-target }}"
source ./ci/dash/matrix.sh
./ci/dash/test_unittests_cmake.sh
shell: bash

- name: Run functional smoke tests
run: |
BUILD_TARGET="${{ inputs.build-target }}"
source ./ci/dash/matrix.sh
./ci/dash/test_integrationtests_cmake.sh
shell: bash
14 changes: 14 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ jobs:
needs: [check-skip, container, cache-sources]
if: |
vars.SKIP_LINUX64 == '' ||
vars.SKIP_LINUX64_CMAKE == '' ||
vars.SKIP_LINUX64_FUZZ == '' ||
vars.SKIP_LINUX64_SQLITE == '' ||
vars.SKIP_LINUX64_UBSAN == ''
Expand Down Expand Up @@ -217,6 +218,19 @@ jobs:
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

src-linux64_cmake:
name: linux64_cmake-build
uses: ./.github/workflows/build-src-cmake.yml
needs: [check-skip, container, depends-linux64]
if: ${{ vars.SKIP_LINUX64_CMAKE == '' }}
with:
build-target: linux64_cmake
container-path: ${{ needs.container.outputs.path }}
depends-key: ${{ needs.depends-linux64.outputs.key }}
depends-host: ${{ needs.depends-linux64.outputs.host }}
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

src-linux64_fuzz:
name: linux64_fuzz-build
uses: ./.github/workflows/build-src.yml
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
todo.txt
reset-files.bash

# Build subdirectories.
/*build*
!/build-aux
!/build_msvc

*.tar.gz

*.exe
Expand Down
Loading
Loading