Skip to content

Run the tests, database checks and secret scan on every pull request (app review batch 6a) - #27

Merged
EmotiveImpact merged 10 commits into
mainfrom
claude/app-review-batch-6a
Oct 12, 2026
Merged

EmotiveImpact merged 10 commits into
mainfrom
claude/app-review-batch-6a

Conversation

@EmotiveImpact

@EmotiveImpact EmotiveImpact commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Before: nothing ran the tests on a pull request, so a change could reach main with a failing test, a broken build or a committed secret; that is how the lost security headers and the re-committed key got there. The database tests could only be run one file at a time by hand. On a development machine, failing to sign in as 1,001 other addresses lifted every running sign-in lockout, setting the Resend keys for the worker was enough for the local API to email real invitations, any installed pnpm was used (pnpm 9 fails on the lockfile), and vite 7.3.2 carried two development server advisories.

After: every pull request and every push to main runs three checks, "Web app", "Database, local API and worker" and "Secret scan", and infra/neon/tests/run-local.sh runs every database test locally the same way. A lockout runs its full 15 minutes, the local API emails invitations only when told to, pnpm is pinned to 10.34.6 and vite is 7.3.7.

This is batch 6a of the app review fixes. It is built on #26 (batch 3), so the checks also run on the batches stacked above it; the diff here shows only this batch.

What changed, by finding

Finding Change
20 .github/workflows/ci.yml. Web app installs the web app, local API and reminder worker from the frozen lockfile, typechecks all three, runs the web tests and builds. Database, local API and worker runs every test in infra/neon/tests on fresh PostgreSQL 16 databases, then the local API's HTTP tests against its own database, then the reminder worker's tests. Secret scan builds gitleaks 8.30.1 from source and scans the files and every commit behind them, so a secret added and removed again is still caught, with anything found redacted. .gitleaksignore leaves out, by fingerprint only, the three old commits holding the key from finding 1 and the one holding the earlier Supabase app's web push key. Every action is pinned to a commit, the token is read-only, and nothing deploys or uses a secret. ESLint and type-checking the test files come in batch 6b.
65, in part packageManager pins pnpm 10.34.6 with its hash. It reads the current lockfile unchanged, and pnpm 11 switches to it when run in this repository.
67 run-local.sh builds a local-mode and a stub-mode database from the migrations and runs each test on a copy, refusing any host but loopback (including through PGHOSTADDR or a service file) and dropping only the databases it made. The local API's lockout list now drops only expired entries, so a running lockout survives any number of other failures. Real invitation email from the local API needs LOCAL_API_SEND_EMAIL=yes as well as the Resend settings, and the API says at start-up which way it is set. vite moves to 7.3.7; the lockfile changes vite, the references to it, and vite's own picomatch and tinyglobby, which move to versions the lockfile already held.

Needs the owner

  • Revoke the earlier Supabase app's web push (VAPID) private key wherever it is still set up. It sits in an old commit of apps/web/.env.example; the current app sends no web push. The Google key from finding 1 is in a third old commit as well, and revoking it covers all three.
  • If the organisation limits which actions may run, allow actions/checkout, actions/setup-node, actions/setup-go and pnpm/action-setup.
  • Optionally require the three checks on main. A run uses about 5 to 6 runner minutes.
  • The pnpm pin also reaches Vercel: the first Vercel build after this lands should show pnpm switching to 10.34.6 in its log.
  • Suggested, not changed here: setting Vercel's install command to pnpm install --frozen-lockfile --filter @commune/web (in apps/web/vercel.json or the project settings) would stop production builds installing every workspace app, including the mobile app's GitHub-hosted package.
  • Reinstall .deps-web locally to pick up vite 7.3.7.

How it was checked

Each job's steps were run as written, in order, on a fresh clone of this branch, with a local PostgreSQL 16.15 server standing in for the service container and pnpm 10.34.6 taken from the pin:

  • Web app passed in 91 seconds: the install took vite 7.3.7 from the lockfile, all three typechecks passed, 414 web tests passed in 39 files, and the build succeeded.
  • Database, local API and worker passed in 102 seconds: 22 database tests passed and 2 were skipped because they need a Neon branch, all 17 local API suites passed, and the reminder worker's 6 unit tests and 4 database suites passed. No database was left behind.
  • Secret scan: the first run on GitHub failed. This container's clone was shallow, so the local runs had shown the scan only 318 of the 658 commits, and the older ones hold the finding 1 key a third time and the earlier app's web push key. Both are now listed by fingerprint, and the scan passes on all 658 commits, here with the full history fetched and on GitHub. Building gitleaks from a cold cache took 48 seconds; the scans take under 15.

Separately: actionlint and ShellCheck report nothing; a made-up token planted in the files, in a new commit, or added and then removed in the same change fails the secret scan without the token appearing in any output; and a failing local API or worker test fails its job, the local API's log is printed, and every database is still dropped. The new lockout test failed on the old code (the right password was accepted after 1,001 other failures) and three of the four opt-in tests fail if the opt-in check is removed.

On GitHub, the first run's Web app and Database jobs passed, setup actions, service container and health check included, in about two and a half minutes each.

How

The database job reaches PostgreSQL only on loopback with throwaway credentials, and run-local.sh names every database it makes with a prefix and a random run id, so an interrupted run cleans up after itself and never touches anything else. The service sorts text as C.UTF-8, like the local servers the tests were written against. gitleaks is built with go install, which checks the source against Go's checksum database, rather than run through its GitHub Action, which needs a licence for an organisation's repository.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS

claude added 9 commits October 9, 2026 15:16
infra/neon/tests/run-local.sh builds a local-mode database
(bootstrap.sql, every migration, after-core.sql) and a stub-mode one
(local_auth_stub.sql and every migration), then runs each SQL test on its
own copy of the one its header names, and the lock order test the same
way. Tests written for a Neon branch are skipped and said to be.

It reads the connection from the PG* variables and accepts only a
loopback server, since the auth stand-ins create cluster roles that must
never reach Neon. Every database it makes carries a random run id that no
database had when the run began, creating one fails rather than reuse a
taken name, and cleanup (also on an interrupt) drops only those.
--create builds one database for the local API and worker tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The local API's sign-in throttle emptied its whole list once it held more
than 1,000 entries, so failing to sign in as 1,001 other addresses lifted
every running lockout and gave a guesser ten more tries. Entries now stay
in order of their latest failure and each failure drops only the expired
ones from the front, so a lockout runs its full 15 minutes.

apps/local-api/tests/sign-in-lockout.mjs locks an account out, fails
1,001 other sign-ins and checks the account is still locked (it got 200
for the right password before this change).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Setting RESEND_API_KEY, RESEND_FROM and APP_URL, perhaps for the worker,
was enough for the local API to email real invitations from a development
machine. It now also needs LOCAL_API_SEND_EMAIL=yes, and logs at startup
whether invitation email is on or off and why. Without the opt-in the
sender gets the existing "not configured here" answer (503) and can copy
the link as before.

The decision lives in apps/local-api/src/invitation-email.ts so
tests/invitation-email-opt-in.mjs can check it without a server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The root package.json now names pnpm 10.34.6 in packageManager, so CI,
corepack and pnpm itself all use one version instead of whatever is
installed. 10.34.6 reads the current lockfile unchanged. The hash lets
corepack check the download.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
vite 7.3.2 is in range of two advisories fixed in 7.3.5: a server.fs.deny
bypass through Windows alternate paths, and the bundled launch-editor
disclosing an NTLM hash through UNC paths. The catalog now asks for
^7.3.5, which resolves to 7.3.7, the current 7.3 release.

The lockfile changes only vite and the references to it: the peer
suffixes of the packages that depend on it, and vite's own fdir,
picomatch and tinyglobby, now the versions vitest already uses. Nothing
new is downloaded beyond vite itself. minimumReleaseAge is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
ShellCheck prefers a glob to parsing ls. The count is only for the
progress line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Finding 20 from the app review: nothing ran the tests automatically.
.github/workflows/ci.yml has three jobs, each with a time limit and
read-only access to the repository:

- web: installs the web app, local API and reminder worker with the
  frozen lockfile, typechecks all three, runs the web tests and builds.
- database: on a PostgreSQL 16.15 service, runs every test in
  infra/neon/tests through run-local.sh, starts the local API on its own
  database and runs its HTTP tests, then runs the reminder worker's
  tests, each on a fresh database. The service sorts text as C.UTF-8,
  like the local servers the tests were written against.
- secrets: gitleaks, built from source with go install, scans the files
  and the commits the pull request or push adds, with findings
  redacted. Older history is left out: it still holds the key from
  finding 1.

Actions are pinned to full commit hashes. Nothing deploys or uses a
secret.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The database README points at run-local.sh, the local README says invitation
email needs LOCAL_API_SEND_EMAIL=yes as well as the Resend settings, the local
API README covers the two new tests, and the handoff records CI and the pnpm
and vite pins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
…hole

The secret scan read only the commits a change added, so after a force
push, or on the first push of a branch, it read the newest commit alone
and a secret added and removed within the push went unseen. It now
reads every commit behind the checkout, which takes about eight seconds.
.gitleaksignore leaves out the two old commits that hold the Google API
key from app review finding 1, by fingerprint only. That key still has
to be revoked.

run-local.sh checked PGHOST, but libpq connects to PGHOSTADDR or a
PGSERVICE entry instead when they are set. It now refuses to start
while PGHOSTADDR, PGSERVICE or PGSERVICEFILE is set.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
@vercel

vercel Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
commune-web Error Error Oct 9, 2026 4:50pm UTC

The first run on GitHub failed the history scan. This container's clone was
shallow, so the scan here had seen 318 commits; GitHub's checkout holds all
658, and two more findings sit in the older ones. The Google API key from
app review finding 1 is also in docs/SESSION-REFERENCE.md at c5fae88 (the
same key, compared by hash), and the earlier Supabase app's web push
(VAPID) private key is in apps/web/.env.example at fb1d3c8. Neither is in
the files today.

Both join .gitleaksignore by fingerprint (commit, file, rule and line; no
secret), so any other secret, old or new, still fails the scan. With the
full history fetched, the scan here now sees the same 658 commits and
passes. The Google key is the one already to be revoked; the web push key
belongs to the old app's send-notification function and should be revoked
there too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS

Copy link
Copy Markdown
Owner Author

The Vercel preview for commune-web shows Error here, as it has on every branch since 6 October. It is the project or account issue explained on #24 (app review finding 45), not this diff: the Web app check, which builds the app, passes on this head (d44e76b). It needs someone with the Vercel login to read a failed deployment's log.


Generated by Claude Code

@EmotiveImpact
EmotiveImpact changed the base branch from claude/app-review-batch-3 to main October 12, 2026 02:00
@EmotiveImpact
EmotiveImpact marked this pull request as ready for review October 12, 2026 02:00
@EmotiveImpact
EmotiveImpact merged commit 0a9e36b into main Oct 12, 2026
6 of 7 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T02:01:22.379843Z d44e76b Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch had an error being deployed

1 failed deployment
Preview — d44e76bd Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants