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
113 changes: 113 additions & 0 deletions .github/workflows/poutine.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Pipeline supply-chain analysis

on:
push:
branches: ['main']
paths:
- '.github/workflows/**'
- '.github/actions/**'
pull_request:
branches: ['**']
paths:
- '.github/workflows/**'
- '.github/actions/**'
schedule:
# Weekly drift check: new poutine rules / advisories can flag workflows
# that were clean when they were last touched.
- cron: '17 6 * * 1'
workflow_dispatch:

concurrency:
group: poutine-${{ github.ref }}
cancel-in-progress: true

permissions: {}

jobs:
poutine:
name: Run poutine
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # to upload SARIF results to code scanning
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

# Pinned to main@badd750a rather than the v1.1.4 tag: that tag still
# builds FROM poutine 1.1.4, while this commit builds FROM
# poutine:1.1.6@sha256:722a8e09..., so both the action and the scanner
# binary are pinned. Bump deliberately when upstream tags the 1.1.6 base.
- name: poutine - GitHub Actions SAST (human-readable)
uses: boostsecurityio/poutine-action@badd750a259150be3b8b00823ad60849d539b96d # main @ poutine 1.1.6
env:
POUTINE_DISABLE_VERSION_CHECK: '1'
with:
format: pretty
output: poutine.txt

- name: poutine - GitHub Actions SAST (SARIF)
uses: boostsecurityio/poutine-action@badd750a259150be3b8b00823ad60849d539b96d # main @ poutine 1.1.6
env:
POUTINE_DISABLE_VERSION_CHECK: '1'
with:
format: sarif
output: poutine.sarif

- name: Annotate pull request with findings
# PRs from forks cannot upload SARIF to code scanning, so surface
# findings as inline annotations instead.
if: ${{ github.event_name == 'pull_request' }}
run: |
jq -r '
.runs[0].results[]?
| (.level // "warning") as $level
| (if $level == "error" then "error"
elif $level == "note" then "notice"
else "warning" end) as $kind
| .locations[0].physicalLocation as $loc
| "::\($kind) file=\($loc.artifactLocation.uri),line=\($loc.region.startLine // 1),title=poutine: \(.ruleId)::"
+ (.message.text | gsub("\\s+"; " ") | .[0:400])
' poutine.sarif

# The action always exits 0, so gate here. Notes (e.g. actions from
# unverified creators) are reported but do not fail CI; warnings and
# errors do.
- name: Report and gate on findings
run: |
{
echo '## poutine'
echo '```'
cat poutine.txt
echo '```'
} >>"$GITHUB_STEP_SUMMARY"

blocking="$(jq '
[.runs[0].results[]? | select((.level // "warning") | test("^(warning|error)$"))]
| length
' poutine.sarif)"

if [ "$blocking" -gt 0 ]; then
echo "::error title=poutine::${blocking} finding(s) at warning level or above"
exit 1
fi
echo "poutine: no findings at warning level or above"

- name: Upload SARIF to code scanning
if: ${{ always() && github.event_name != 'pull_request' }}
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
sarif_file: poutine.sarif
category: poutine

- name: Upload poutine reports
if: ${{ always() }}
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: poutine-reports
path: |
poutine.sarif
poutine.txt
if-no-files-found: warn
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ build/
# NCMEC report XML files (contain sensitive data)
ncmec-reports/
.devops/backups/

# poutine supply-chain scan reports (.github/workflows/poutine.yml)
poutine.sarif
poutine.txt
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ For more information about each release including git tags and artifacts, see [R
- Passwords are hashed with Argon2id instead of bcrypt at cost factor 5 ([#901](https://github.com/roostorg/coop/pull/901) by [@serendipty01](https://github.com/serendipty01), closes [#900](https://github.com/roostorg/coop/issues/900))
- Minimum password length raised to 15 and enforced server-side ([#1065](https://github.com/roostorg/coop/pull/1065), [#1094](https://github.com/roostorg/coop/pull/1094) by [@serendipty01](https://github.com/serendipty01))

### CI & infrastructure

- `poutine` added to scan CI workflows for supply-chain vulnerabilities, gated on warning-level findings and reporting to code scanning ([#1156](https://github.com/roostorg/coop/pull/1156) by [@reitblatt](https://github.com/reitblatt))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to add this to our changelog if it's not user-facing? cc @cassidyjames


## [1.0.2] - 2026-06-30

This release addresses reported security advisories, improves NCMEC CyberTipline reporting, and includes front-end quality-of-life improvements.
Expand Down
Loading