GitHub Actions¶
Gate release artifacts (Linux runner + Docker)¶
.github/workflows/pe-gate.yml
name: pe-gate
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- run: msbuild MyApp.sln /p:Configuration=Release # your build
- uses: actions/upload-artifact@v4
with: { name: dist, path: dist/ }
inspect:
needs: build
runs-on: ubuntu-latest
env:
PPEE_IMAGE: ghcr.io/${{ github.repository_owner }}/ppee-cli:2.0.0
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with: { name: dist, path: dist }
- name: Pull ppee-cli
run: docker pull "$PPEE_IMAGE"
- name: Policy gate
run: |
export PPEE_CLI="docker run --rm -v $PWD:$PWD:ro -w $PWD $PPEE_IMAGE"
curl -fsSLo ppee-policy.sh https://mzrst.com/puppy/docs/assets/ppee-policy.sh # or commit it to your repo
chmod +x ppee-policy.sh
./ppee-policy.sh --require-hardening --forbid-wx --forbid-admin \
--report ppee-report.jsonl dist/*.exe dist/*.dll | tee gate.txt
- name: Job summary
if: always()
run: |
{
echo "## PE inspection"
echo '| File | Signed | ASLR | DEP | CFG | W+X |'
echo '|---|---|---|---|---|---|'
jq -r '"| \(.path|split("/")|last) | \(.signed) | \(.aslr) | \(.dep) | \(.cfg) | \(.wxSections) |"' ppee-report.jsonl
} >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@v4
if: always()
with: { name: ppee-report, path: ppee-report.jsonl }
The gate step fails the job (exit 1) on any violation. The summary and report are uploaded either way.
Verify signatures on a Windows runner¶
Only the Windows build reports WinVerifyTrust trust (chain and revocation):
.github/workflows/verify-signature.yml
jobs:
verify:
runs-on: windows-latest
steps:
- uses: actions/download-artifact@v4
with: { name: dist, path: dist }
- name: Get ppee-cli
shell: pwsh
run: |
# Download or restore ppee-cli.exe into .\tools (e.g. from your release assets or a cache)
echo "$PWD\tools" | Out-File -Append $env:GITHUB_PATH
- name: Verify
shell: pwsh
run: |
$bad = 0
Get-ChildItem dist -Include *.exe,*.dll,*.sys -Recurse | ForEach-Object {
$j = ppee-cli.exe --no-update-check --no-similarity --json --security $_.FullName | ConvertFrom-Json
$r = $j.security.validity.result
if ($r -ne 'SIGNED & VERIFIED') { Write-Output "::error file=$($_.Name)::$r"; $bad++ }
else { Write-Output "OK $($_.Name) signer: $($j.security.signatures[0].signerCertificate.subjectName)" }
}
if ($bad) { exit 1 }
Build and publish the image¶
.github/workflows/ppee-image.yml
name: ppee-image
on:
push:
tags: ["v*"]
permissions:
contents: read
packages: write
jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # a repo holding the Dockerfile and the release files
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64
tags: ghcr.io/${{ github.repository_owner }}/ppee-cli:${{ github.ref_name }}
- name: Smoke test
run: |
docker run --rm ghcr.io/${{ github.repository_owner }}/ppee-cli:${{ github.ref_name }} --version
Annotations
Print ::error file=<path>::<message> lines (as in the Windows example) to put findings inline on the PR's Files changed tab.
References¶
- Workflow syntax for GitHub Actions: every key used in the workflows on this page.