chore(ci): add fork-side SSO audit script + workflow#20
Conversation
Adds a per-fork deterministic audit for the cross-app SSO contract. Mirrors Pressingly/plane#32 and Pressingly/twenty#9. Rows covered: 14 — SPA logout (app/stores/AuthStore.ts) MUST NOT call /oauth2/sign_out 20 — server/middlewares/authentication.ts MUST define normalizeProxyEmail OR throw AuthenticationError on proxy-identity mismatch in the JWT cookie branch. SECURITY-CRITICAL. 21 — No polynomial-backtracking email-shape regex in authentication.ts Local dry-run on foss-main: row 14 ✅, row 20 ❌, row 21 ❌. Both correctly waiting for Pressingly/ outline#19 (introduces normalizeProxyEmail + replaces the regex with indexOf). When #19 merges, this audit will go green on foss-main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Outline SSO Fork AuditCross-app contract: https://github.com/awais786/sso-rules-moneta/blob/main/openspec/specs/proxy-auth-middleware/spec.md
2 violations. Security-critical (row 20): 1. |
There was a problem hiding this comment.
Pull request overview
This PR adds a fork-specific CI audit to verify that this Outline fork continues to satisfy key invariants from the cross-app SSO “proxy-auth-middleware” contract, and publishes the results to the GitHub job summary (and as a sticky PR comment on same-repo PRs).
Changes:
- Adds a
scripts/sso-audit.shbash script that checks contract rows 14/20/21 against the repository’s source tree. - Adds a GitHub Actions workflow to run the audit on relevant PR changes, on
foss-mainpushes, and on a weekly schedule. - Posts audit output to the job summary and (when safe) as a sticky PR comment.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
scripts/sso-audit.sh |
Implements the contract checks and outputs a markdown report with pass/fail status per row. |
.github/workflows/sso-audit.yml |
Runs the audit in CI, publishes output, optionally comments on PRs, and fails the job on security-critical violations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| has_normalize_helper=$(grep -cE "\bnormalizeProxyEmail\b" "$AUTH_MIDDLEWARE" || true) | ||
|
|
||
| # Fallback marker: "proxy identity" literal in a throw — used in the | ||
| # AuthenticationError message when the mismatch fires. | ||
| local has_throw_proxy | ||
| has_throw_proxy=$(grep -cE "throw\s+AuthenticationError\(.*[Pp]roxy" "$AUTH_MIDDLEWARE" || true) |
There was a problem hiding this comment.
Addressed in commit c7ce838 — switched to grep -cF (fixed-string match) for portable detection. Avoids the \b word-boundary which is a GNU extension.
| local hits | ||
| hits=$(grep -nE '\[\^[\\\\]s@\]\+@\[\^[\\\\]s@\]\+\\\.\[\^[\\\\]s@\]\+' "$AUTH_MIDDLEWARE" 2>/dev/null || true) | ||
|
|
||
| if [[ -n "$hits" ]]; then | ||
| record 2 "❌" "Polynomial-backtracking email-shape regex detected in $AUTH_MIDDLEWARE: $hits. Rewrite to indexOf-based check per openspec proxy-auth-middleware §'email-shape detection SHALL avoid polynomial-backtracking regex'. Reference: \`normalizeProxyEmail\` in this file." | ||
| return | ||
| fi | ||
|
|
||
| record 2 "✅" "No polynomial-backtracking email-shape regex in $AUTH_MIDDLEWARE; using indexOf-based detection" |
There was a problem hiding this comment.
Verified locally against the current branch state — the audit regex does match line 316 of server/middlewares/authentication.ts:
$ grep -nE '\[\^[\\\\]s@\]\+@\[\^[\\\\]s@\]\+\\\.\[\^[\\\\]s@\]\+' server/middlewares/authentication.ts
316: const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailClaim);And bash scripts/sso-audit.sh correctly fires Row 21 ❌ on this finding (output truncated):
| 21 | email-shape detection uses indexOf, not polynomial regex | ❌ | Polynomial-backtracking email-shape regex detected in server/middlewares/authentication.ts: 316: const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailClaim); ... |
So:
- The
[\\\\]sescaping in the audit's bash-regex correctly matches the literal\sinside the source's JS regex character class[^\s@]. - The audit is doing its job — flagging an actual ReDoS surface that remains on the
fwd:token code path (lines 311–319). The PR that introducednormalizeProxyEmailreplaced the regex on the proxy-header code path but not on this one.
This finding is real and should not be removed from the audit. Closing this thread; the underlying ReDoS gap on the fwd: path is a separate follow-up issue worth opening.
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
|
|
There was a problem hiding this comment.
Implemented in 6b26a1c. I scoped workflow permissions to least privilege: pull-requests: write was removed from workflow-level permissions and moved to a separate PR-only sso-fork-audit-comment job, while the main audit job now runs with read-only permissions and passes output via artifact.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/f90ab832-2130-4fdd-b46b-9f568cb14996 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/f90ab832-2130-4fdd-b46b-9f568cb14996 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/c10e5d62-8adf-4de2-9854-c2e176770a90 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/c10e5d62-8adf-4de2-9854-c2e176770a90 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/c10e5d62-8adf-4de2-9854-c2e176770a90 Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
Fixed in ff9aeff. Row 20 now uses
Fixed in ff9aeff. Row 20 now uses |
|
Closing: fork-side audit tooling not needed on this fork. The audit is being maintained in sso-rules-moneta instead, which is the single source of truth across all forks. |
Summary
Per-fork SSO contract audit in CI. Mirrors Pressingly/plane#32 and Pressingly/twenty#9.
Checks
app/stores/AuthStore.ts/oauth2/sign_outserver/middlewares/authentication.tsnormalizeProxyEmailOR throwAuthenticationError(...proxy...)for stale-session detection in the JWT cookie branchSpec source:
sso-rules-moneta/openspec/specs/proxy-auth-middleware/spec.md.Current state
Local dry-run on
foss-main: rows 20 + 21 both ❌. The fix for both lives in PR #19 — introducesnormalizeProxyEmailAND replaces the polynomial regex with indexOf-based detection. When #19 merges, the audit goes green.To unblock this PR, rebase onto
fix/proxy-auth-stale-session-on-user-switch(or wait for #19).🤖 Generated with Claude Code