# +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2026 The Cacti Group | # | | # | This program is free software; you can redistribute it and/or | # | modify it under the terms of the GNU General Public License | # | as published by the Free Software Foundation; either version 2 | # | of the License, or (at your option) any later version. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ name: Security SAST on: push: branches: [develop, 1.2.x] pull_request: branches: [develop, 1.2.x] merge_group: workflow_dispatch: permissions: contents: read concurrency: group: security-sast-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: semgrep: name: Semgrep taint runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 with: fetch-depth: 0 persist-credentials: false - name: Set up Docker with the public registry cache uses: ./.github/actions/setup-ci-docker # Every event gates on new findings only. The grv()/gnrv() sources also # match request vars that an earlier gfrv() call validated, which Semgrep # cannot see, so a whole-tree result is not a usable pass/fail gate. - name: Run Semgrep env: EVENT_NAME: ${{ github.event_name }} MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} PUSH_BASE_SHA: ${{ github.event.before }} run: | set -euo pipefail case "$EVENT_NAME" in pull_request) base_sha="$PR_BASE_SHA" ;; push) base_sha="$PUSH_BASE_SHA" ;; merge_group) base_sha="$MERGE_GROUP_BASE_SHA" ;; *) base_sha="" ;; esac if [[ -z "$base_sha" || "$base_sha" =~ ^0+$ ]]; then base_sha="$(git rev-parse --verify HEAD^ 2>/dev/null || git rev-parse --verify HEAD)" fi git cat-file -e "${base_sha}^{commit}" # Parallel jobs hit taint fixpoint timeouts in Cacti's large files, # so the same tree can report different findings run to run. One job # keeps the result stable and still scans the whole tree in seconds. semgrep_args=(--config .semgrep.yml --error -j 1 --baseline-commit "$base_sha") docker run --rm \ --user "$(id -u):$(id -g)" \ --env HOME=/tmp \ --volume "${PWD}:/src" \ --workdir /src \ semgrep/semgrep@sha256:e04d2cb132288d90035db8791d64f610cb255b21e727b94db046243b30c01ae9 \ semgrep "${semgrep_args[@]}" . psalm: name: Psalm taint runs-on: ubuntu-24.04 timeout-minutes: 20 env: COMPOSER_ALLOW_SUPERUSER: 1 PSALM_VERSION: '6.16.1' PSALM_PHAR_SHA256: '7ace71e4f698466e7e16810679a1895ea623060579aff46fcf9fd0f1c09abe00' steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 with: persist-credentials: false - name: Setup locked PHP 8.3 dependencies uses: ./.github/actions/setup-php-composer with: php-version: '8.3' extensions: intl, mysql, gd, ldap, gmp, xml, curl, json, mbstring coverage: none dependency-mode: locked-psalm install-args: --no-dev --prefer-dist --no-progress --no-interaction - name: Download and verify pinned Psalm phar run: | set -euo pipefail curl --proto '=https' --proto-redir '=https' -fsSL -o psalm.phar \ "https://github.com/vimeo/psalm/releases/download/${PSALM_VERSION}/psalm.phar" echo "${PSALM_PHAR_SHA256} psalm.phar" | sha256sum -c - - name: Run Psalm taint analysis run: php psalm.phar --taint-analysis --no-progress