prole/pg-knoe-auth/test/unit/test_response_bound.c
chrisfu 57886f9268 feat(pg-knoe-auth): import upstream PostgreSQL JWT auth extension; compile in knoe-db image
- Copy pg-knoe-auth/ wholesale from upstream/knoe-db/20260523 (Task 1 of
  docs/plans/junie/upstream-knoe-db-20260523-integration.md).
- Extension: PG18 OAUTHBEARER JWT validator using libcurl + OpenSSL RS256.
- knoe-db/Dockerfile: add libcurl4-openssl-dev to dev deps; COPY src/ and
  build with make USE_PGXS=1 install after tds_fdw.
- NOT enabled in the default database build (absent from 20_create_extensions.sh).
  To enable: CREATE EXTENSION pg_knoe_auth; (requires pg_hba.conf oauth_issuer).

Closes Task 1 of upstream-knoe-db-20260523-integration.md.
2026-05-23 21:52:06 -07:00

104 lines
3.1 KiB
C

/*
* test_response_bound.c — unit tests for H3: HTTP response size bound.
*
* Tests that the curl write callback aborts at MAX_JWKS_BYTES+1 and
* passes for smaller payloads.
* See docs/plans/junie/pg_knoe_auth-rename-harden-modularize.md §2.2.3
*/
#include "test_helpers.h"
#define MAX_JWKS_BYTES (10 * 1024 * 1024)
typedef struct {
char *data;
size_t len;
size_t alloc;
} CurlBuf;
/* curl_write_cb — same logic as pg_knoe_auth.c */
static size_t
curl_write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
CurlBuf *buf = (CurlBuf *) userdata;
size_t new_bytes = size * nmemb;
if (buf->len + new_bytes > MAX_JWKS_BYTES) {
elog(WARNING, "pg_knoe_auth: JWKS response > %d bytes; aborting",
MAX_JWKS_BYTES);
return 0;
}
if (buf->len + new_bytes + 1 > buf->alloc) {
buf->alloc = (buf->len + new_bytes + 1) * 2;
buf->data = repalloc(buf->data, buf->alloc);
}
memcpy(buf->data + buf->len, ptr, new_bytes);
buf->len += new_bytes;
buf->data[buf->len] = '\0';
return new_bytes;
}
int main(void)
{
CurlBuf buf;
size_t ret;
char *chunk;
/* ── Test 1: small payload passes ──────────────────────────────────── */
buf.alloc = 8192;
buf.len = 0;
buf.data = palloc(buf.alloc);
buf.data[0] = '\0';
chunk = palloc(1024);
memset(chunk, 'A', 1024);
ret = curl_write_cb(chunk, 1, 1024, &buf);
ASSERT(ret == 1024, "1 KB payload: write callback returns 1024");
ASSERT(buf.len == 1024, "1 KB payload: buf.len is 1024");
pfree(chunk);
pfree(buf.data);
/* ── Test 2: exactly MAX_JWKS_BYTES passes ──────────────────────────── */
buf.alloc = MAX_JWKS_BYTES + 16;
buf.len = 0;
buf.data = palloc(buf.alloc);
buf.data[0] = '\0';
/* Simulate buf already at MAX_JWKS_BYTES - 1, then write 1 byte */
buf.len = MAX_JWKS_BYTES - 1;
chunk = palloc(1);
chunk[0] = 'X';
ret = curl_write_cb(chunk, 1, 1, &buf);
ASSERT(ret == 1, "write of 1 byte when buf.len == MAX-1 passes");
pfree(chunk);
pfree(buf.data);
/* ── Test 3: MAX_JWKS_BYTES + 1 aborts ─────────────────────────────── */
buf.alloc = MAX_JWKS_BYTES + 16;
buf.len = MAX_JWKS_BYTES;
buf.data = palloc(buf.alloc);
buf.data[0] = '\0';
chunk = palloc(1);
chunk[0] = 'Y';
ret = curl_write_cb(chunk, 1, 1, &buf);
ASSERT(ret == 0, "write of 1 byte when buf.len == MAX aborts (returns 0)");
pfree(chunk);
pfree(buf.data);
/* ── Test 4: large chunk that would overflow aborts ─────────────────── */
buf.alloc = 8192;
buf.len = MAX_JWKS_BYTES - 100;
buf.data = palloc(buf.alloc);
buf.data[0] = '\0';
chunk = palloc(200);
memset(chunk, 'Z', 200);
ret = curl_write_cb(chunk, 1, 200, &buf);
ASSERT(ret == 0, "200-byte chunk that would exceed MAX aborts");
pfree(chunk);
pfree(buf.data);
TEST_SUMMARY();
}