Patch OnMission’s Postgres this week. Prisma will not do it for you

PostgreSQL 18.6 landed on August 13 with 28 security fixes. Here is the five-step check I would run on OnMission and LOMA tonight — binaries, not a schema migration.

On 13 August 2026 the PostgreSQL Global Development Group shipped 18.6, 17.11, 16.15, 15.19, and 14.24. Twenty-eight CVEs. Over a hundred bugs. Postgres 18 skipped 18.4 and 18.5 — 18.5 never shipped because of a regression — so if you are on 18.3 you jump straight to 18.6. This is not a major upgrade. You stop the engine, replace the binaries, start it again. Prisma migrate deploy does not patch the server.

The ones that matter on an HR product: logical decoding can dlopen an arbitrary file if a non-superuser holds REPLICATION (CVE-2026-6471). Several overflows in to_char, regex, pg_stat_statements, and pg_dump are in the 8.8 range. psql COPY FROM STDIN can treat leftover data lines as commands (CVE-2026-6464). EXTRACT can be turned into SQL injection through expression deparse (CVE-2026-15741). OnMission, LOMA, HelloFutsall, and Kaha Booking all sit on Postgres. I would run these steps on OnMission first, then copy the same checklist to LOMA.

Step 1: Read the version the app actually talks to

Do not trust the Docker tag on your laptop. Ask the instance behind DATABASE_URL. If staging and production differ, you have two patches, not one.

import { prisma } from "./prisma";

export const MIN_PATCH: Record<number, number> = {
  14: 24,
  15: 19,
  16: 15,
  17: 11,
  18: 6,
};

export async function requirePatchedPostgres() {
  const rows = await prisma.$queryRaw<
    { server_version: string }[]
  >`SELECT current_setting('server_version') AS server_version`;

  const version = rows[0]?.server_version ?? "";
  const [major, minor] = version.split(".").map(Number);
  const floor = MIN_PATCH[major];

  if (floor == null || !Number.isFinite(minor) || minor < floor) {
    throw new Error(`Postgres ${version} is below the 13 Aug 2026 patch`);
  }
}

Step 2: Patch binaries. Do not run a Prisma migration as a substitute

Minor releases are cumulative. No dump/restore, no pg_upgrade, no Prisma schema change. RDS, Cloud SQL, and a Docker image all count as “binaries.” Pin the image to 16.15 or 18.6 — never postgres:16. Leave PostgreSQL 19 Beta 3 for a throwaway database. Production stays on a patched 16, 17, or 18.

services:
  postgres:
    image: postgres:16.15-alpine
    environment:
      POSTGRES_DB: onmission
      POSTGRES_USER: onmission
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Step 3: Revoke REPLICATION from roles that only query

CVE-2026-6471 is a missing authorization check: REPLICATION was never supposed to mean “load native code.” Prisma’s app role on OnMission should select, insert, update, delete — not replicate. If you are not running logical decoding yourself, the privilege should not exist.

SELECT rolname, rolsuper, rolreplication
FROM pg_roles
WHERE rolreplication
ORDER BY rolname;

-- App roles used by Prisma should not replicate.
REVOKE REPLICATION FROM onmission_app;

Step 4: After the restart, check GIN reltuples before you blame Prisma

A parallel GIN build bug could leave reltuples as Infinity or NaN. Autovacuum then ignores the table forever, and the assignment list looks like the same slow Prisma query I already wrote about. ANALYZE resets the counter. Do this on any OnMission or LOMA table that uses GIN — search, tags, JSON.

SELECT DISTINCT t.oid::regclass AS table_name, t.reltuples
FROM pg_class t
JOIN pg_index i ON t.oid = i.indrelid
JOIN pg_class ic ON i.indexrelid = ic.oid
WHERE t.relhasindex AND ic.relam = 2742;

-- If reltuples is NaN, Infinity, or nonsense:
-- ANALYZE public.assignments;

Step 5: Put Postgres 14 on a calendar