diff --git a/.github/workflows/opengrep.yml b/.github/workflows/opengrep.yml new file mode 100644 index 000000000..ec13254c8 --- /dev/null +++ b/.github/workflows/opengrep.yml @@ -0,0 +1,108 @@ +name: "OpenGrep" + +# OpenGrep is an LGPL 2.1 fork of Semgrep CE, backed by a consortium of +# AppSec vendors. It is rule-compatible with Semgrep, so existing rule packs +# (p/security-audit, p/javascript, p/typescript, p/github-actions) work +# unchanged. See https://opengrep.dev for project details. +# +# This workflow currently runs on workflow_dispatch only while we evaluate +# the tool's effectiveness for the Maester codebase. If results are valuable, +# add push/pull_request triggers in a follow-up PR. + +permissions: {} + +on: + workflow_dispatch: + inputs: + opengrep_version: + description: 'OpenGrep release tag to install. Defaults to the pinned, checksum-verified version below; override only if you also provide a matching opengrep_sha256.' + required: false + default: 'v1.21.0' + opengrep_sha256: + description: 'SHA256 of opengrep_manylinux_x86 for the chosen version. Leave blank to use the built-in checksum for the default version.' + required: false + default: '' + config: + description: 'Rule config(s) to run (space- or comma-separated).' + required: false + default: 'p/security-audit p/javascript p/typescript p/github-actions' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + scan: + name: Scan with OpenGrep + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # Install OpenGrep by downloading the release binary directly from + # GitHub Releases (pinned by tag) and verifying its SHA256 before + # executing it. Avoids piping a moving `install.sh` into bash and + # closes the integrity gap left by trusting a mutable release tag. + - name: Install OpenGrep + env: + OPENGREP_VERSION: ${{ inputs.opengrep_version }} + OPENGREP_SHA256_INPUT: ${{ inputs.opengrep_sha256 }} + # Pinned checksum for opengrep_manylinux_x86 at the default version + # (v1.21.0). Update both the version default above and this value + # in lockstep when bumping the pinned release. + DEFAULT_OPENGREP_VERSION: 'v1.21.0' + DEFAULT_OPENGREP_SHA256: '9ed0ceee4a3a406d27d40894bcce85ea151be21e6d4b180689689224faff2a3e' + run: | + set -euo pipefail + version="${OPENGREP_VERSION:-${DEFAULT_OPENGREP_VERSION}}" + expected_sha="${OPENGREP_SHA256_INPUT}" + if [[ -z "${expected_sha}" ]]; then + if [[ "${version}" != "${DEFAULT_OPENGREP_VERSION}" ]]; then + echo "::error::Custom opengrep_version '${version}' supplied without opengrep_sha256. Refusing to install an unverified binary." >&2 + exit 1 + fi + expected_sha="${DEFAULT_OPENGREP_SHA256}" + fi + asset="opengrep_manylinux_x86" + base="https://github.com/opengrep/opengrep/releases/download/${version}" + echo "Downloading OpenGrep ${version} (${asset})" + curl -fsSL --retry 3 -o /tmp/opengrep "${base}/${asset}" + echo "${expected_sha} /tmp/opengrep" | sha256sum --check --status + chmod +x /tmp/opengrep + sudo mv /tmp/opengrep /usr/local/bin/opengrep + opengrep --version + + - name: Run OpenGrep scan + id: scan + continue-on-error: true + env: + OPENGREP_CONFIG: ${{ inputs.config }} + run: | + set -euo pipefail + # Build a bash array of --config flags from the space/comma-separated + # input so values are passed as discrete argv entries (no + # word-splitting surprises). + args=() + normalized="${OPENGREP_CONFIG//,/ }" + for cfg in ${normalized}; do + args+=(--config "${cfg}") + done + opengrep scan \ + "${args[@]}" \ + --sarif \ + --sarif-output=results.sarif \ + --error \ + --no-suppress-errors \ + . + + - name: Upload SARIF results + if: always() && hashFiles('results.sarif') != '' + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + with: + sarif_file: results.sarif + category: opengrep diff --git a/.github/workflows/psscriptanalyzer.yml b/.github/workflows/psscriptanalyzer.yml new file mode 100644 index 000000000..3eb1d0316 --- /dev/null +++ b/.github/workflows/psscriptanalyzer.yml @@ -0,0 +1,59 @@ +name: "PSScriptAnalyzer" + +permissions: {} + +on: + push: + branches: [ "main" ] + paths: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.psd1' + - '**/*.ps1xml' + - '.github/workflows/psscriptanalyzer.yml' + pull_request: + branches: [ "main" ] + paths: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.psd1' + - '**/*.ps1xml' + - '.github/workflows/psscriptanalyzer.yml' + schedule: + - cron: '28 13 * * 2' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + analyze: + name: Analyze PowerShell + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # Scans the entire repository recursively. PSScriptAnalyzer only + # analyzes files with PowerShell extensions (.ps1, .psm1, .psd1, .ps1xml), + # so non-PowerShell content under powershell/, tools/, build/, etc. is + # automatically ignored. This avoids per-directory invocations and + # transparently picks up any future PowerShell files added to the repo. + - name: Run PSScriptAnalyzer + uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f # v1.1 + with: + path: . + recurse: true + output: results.sarif + + - name: Upload SARIF results + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + with: + sarif_file: results.sarif + category: psscriptanalyzer