Skip to content

Commit db4ef7f

Browse files
dconlanclaude
andauthored
ci: pilot Psalm security scan, release-please, and PR title lint (#6)
Pilot of the CI tooling discussed for the ontology-provider modules, on this repo first since it's under active review (see #5). ## What this adds - **`security-scan.yml`** — runs Psalm's taint analysis (`--taint-analysis`) on every PR, scoped to mirror the REDCap consortium's stated security-scan categories (SQL, XSS, cookies, headers, path traversal, shell, LDAP, curl/SSRF) rather than general code quality. Findings are also uploaded as SARIF to the Security tab via `github/codeql-action/upload-sarif`, making them eligible for Copilot Autofix assignment. - **`stubs/redcap-em-framework.phpstub`** — minimal method/function *signatures* (no implementation) for the EM framework surface this module calls, with `@psalm-taint-sink`/`@psalm-taint-escape` annotations, so Psalm can do useful taint tracking without needing REDCap core itself in a public CI runner. Sourced from REDCap's published EM Framework docs, not REDCap source. - **`release-please.yml`** — dormant until this branch reaches `main` (it only triggers on pushes there). Uses `release-type: simple` since `config.json` has no version field to bump; release-please owns its own manifest. - **`pr-title-lint.yml`** — enforces Conventional Commits on PR titles via `pull_request_target` (safe here — only reads the title, never checks out fork code), so release-please has something reliable to parse. - **`.github/dependabot.yml`** — weekly version-update PRs for `composer` (Psalm itself) and `github-actions` (the pinned action versions here). ## Verified before opening this PR - Ran Psalm against the actual module code (clean) and against a deliberately tainted throwaway file (correctly flagged `TaintedSql` via the stubbed `query()` sink and `TaintedHtml`/`TaintedTextWithQuotes` via `echo`), to confirm the scan detects real taint rather than silently passing everything. - Confirmed `--report=psalm-results.sarif` produces valid SARIF (schema 2.1.0) alongside the normal console output, not instead of it. ## Known limitation Psalm only analyzes PHP. The module's actual JS lives embedded in PHP heredoc strings, which no mainstream JS static analyzer (ESLint, CodeQL) can see — that's a separate piece of follow-up work (extracting embedded `<script>` blocks into real `.js` files), deliberately not part of this PR. ## Still open - Repo merge-strategy restriction to squash-only (needed for release-please to read a clean one-commit-per-PR history) — pending, not part of this PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 18e5e76 commit db4ef7f

9 files changed

Lines changed: 3657 additions & 0 deletions

File tree

.github/dependabot.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "composer"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
8+
- package-ecosystem: "github-actions"
9+
directory: "/"
10+
schedule:
11+
interval: "weekly"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: PR title lint
2+
3+
# pull_request_target (not pull_request) so this also runs for PRs from forks -
4+
# safe here since the action only reads the PR title, it never checks out or
5+
# executes the fork's code.
6+
on:
7+
pull_request_target:
8+
types: [opened, edited, synchronize, reopened]
9+
10+
permissions:
11+
pull-requests: read
12+
13+
jobs:
14+
main:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: amannn/action-semantic-pull-request@v5
18+
env:
19+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: release-please
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
11+
jobs:
12+
release-please:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: googleapis/release-please-action@v4
16+
with:
17+
release-type: simple
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: security-scan
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
permissions:
9+
contents: read
10+
security-events: write
11+
12+
jobs:
13+
psalm:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: shivammathur/setup-php@v2
19+
with:
20+
php-version: '8.4'
21+
tools: composer
22+
23+
- run: composer install --no-interaction --no-progress
24+
25+
# continue-on-error so a failing scan doesn't skip the SARIF upload below -
26+
# findings should reach the Security tab even when they fail the job.
27+
- name: Run Psalm taint analysis
28+
id: psalm
29+
run: vendor/bin/psalm --taint-analysis --no-progress --report=psalm-results.sarif
30+
continue-on-error: true
31+
32+
# continue-on-error too: GITHUB_TOKEN is forced read-only on pull_request
33+
# runs triggered from forks, regardless of the security-events: write
34+
# permission declared above, so this step fails on every fork PR
35+
# (this repo has external contributors - see PR #5) for a reason
36+
# unrelated to Psalm's actual findings. Without this, that failure
37+
# would be indistinguishable from a real security finding.
38+
- name: Upload results to code scanning
39+
uses: github/codeql-action/upload-sarif@v3
40+
with:
41+
sarif_file: psalm-results.sarif
42+
continue-on-error: true
43+
44+
- name: Fail the job if Psalm found issues
45+
if: steps.psalm.outcome == 'failure'
46+
run: exit 1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
.idea
33
.project
44
.settings
5+
vendor

composer.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "aehrc/redcap-fhir-ontology-provider",
3+
"description": "REDCap external module providing FHIR-based ontology autocomplete",
4+
"type": "project",
5+
"require-dev": {
6+
"vimeo/psalm": "^6.16"
7+
}
8+
}

0 commit comments

Comments
 (0)