feat(namespace): egress guard + validation speak the law — whereMatcher's resolver reads system.* from the record and bare names from the metadata bag only (the bare-system switch is dead); validateFindParams refuses cursor/includeRelations/writeOnly typed (accepted-and-ignored dies as a class), validates order, and parses every orderBy address
Some checks are pending
CI / Node 22 (push) Waiting to run
CI / Node 24 (push) Waiting to run
CI / Bun (latest) (push) Waiting to run

This commit is contained in:
David Snelling 2026-08-03 15:51:14 -07:00
parent 4679c89458
commit c2fb28a2f7
3 changed files with 101 additions and 36 deletions

View file

@ -17,6 +17,7 @@ import { findCallerLocation } from './callerLocation.js'
// fallback branches that no supported runtime can reach.
import * as os from 'node:os'
import * as fs from 'node:fs'
import { parseFieldAddress, UnsupportedFindOptionError } from '../db/fieldAddressing.js'
const getSystemMemory = (): number => {
if (os) {
@ -466,9 +467,31 @@ export function validateFindParams(params: FindParams): void {
throw new Error('cannot specify both query and vector - they are mutually exclusive')
}
// Universal truth: can't use both cursor and offset pagination
if (params.cursor !== undefined && params.offset !== undefined) {
throw new Error('cannot use both cursor and offset pagination simultaneously')
// ACCEPTED-AND-IGNORED DIED AS A CLASS (sealed 2026-08-03): options the
// engine does not implement REFUSE with a typed error instead of silently
// doing nothing — a production consumer discovered a no-op by measurement
// once; never again.
if (params.cursor !== undefined) {
throw new UnsupportedFindOptionError('cursor')
}
if ((params as Record<string, unknown>).includeRelations !== undefined) {
throw new UnsupportedFindOptionError('includeRelations')
}
if ((params as Record<string, unknown>).writeOnly !== undefined) {
throw new UnsupportedFindOptionError('writeOnly')
}
// THE ONE ADDRESSING LAW: the orderBy address must PARSE (bare/metadata. =
// user field, system.<field> = the ruled map, anything else refuses typed
// with the valid map in the message) and order must be a real direction.
if (params.orderBy !== undefined) {
if (typeof params.orderBy !== 'string') {
throw new Error('orderBy must be a string field address')
}
parseFieldAddress(params.orderBy, 'entity') // throws InvalidFieldAddressError on a bad address
}
if (params.order !== undefined && params.order !== 'asc' && params.order !== 'desc') {
throw new Error(`order must be 'asc' or 'desc', got '${String(params.order)}'`)
}
// Auto-limit query length based on memory