UNPKG

noa-receipt

Version:

NOA Agent Action Receipt — open, offline-verifiable provenance for AI-agent actions. The governance/receipt organ only; the NOA brain is separate and proprietary.

98 lines (97 loc) • 4.35 kB
import { isProxy as _isProxy, hasOwn as _hasOwn } from "./intrinsics.js"; import { decodeDocument } from "./bytes.js"; const _ownKeys = Reflect.ownKeys; const _getOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor; const _getPrototypeOf = Reflect.getPrototypeOf; const _objectPrototype = Object.prototype; const _isArray = Array.isArray; const _isSafeInteger = Number.isSafeInteger; const _create = Object.create; const _freeze = Object.freeze; export function inertOptions(schema, input, what) { return admit(schema, input, what, 0); } const MAX_OPTION_DEPTH = 1; function admit(schema, input, what, depth) { if (input === undefined || input === null) { return { ok: true, value: _freeze(_create(null)) }; } if (_isProxy(input)) { return { ok: false, reason: `${what}: a Proxy is not accepted — its traps are attacker code running inside the validator` }; } const t = typeof input; if (t === "function") { return { ok: false, reason: `${what}: options must be a plain object, never a function` }; } if (t !== "object") { return { ok: false, reason: `${what}: options must be a plain object` }; } if (_isArray(input)) { return { ok: false, reason: `${what}: options must be a plain object, never an array` }; } const proto = _getPrototypeOf(input); if (proto !== _objectPrototype && proto !== null) { return { ok: false, reason: `${what}: options must be a plain object (Object.prototype or null prototype) — an exotic prototype carries machinery that is not the caller's configuration`, }; } const out = _create(null); const keys = _ownKeys(input); for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (typeof key === "symbol") { return { ok: false, reason: `${what}: symbol-keyed options are not accepted` }; } const field = _hasOwn(schema, key) ? schema[key] : undefined; if (field === undefined) { return { ok: false, reason: `${what}: unknown option "${key}" — an unrecognised member is a misconfiguration, not a default` }; } const desc = _getOwnPropertyDescriptor(input, key); if (desc === undefined) { return { ok: false, reason: `${what}: option "${key}" has no readable own descriptor` }; } if (desc.get !== undefined || desc.set !== undefined) { return { ok: false, reason: `${what}: option "${key}" is an accessor — reading it would run caller code inside the boundary` }; } const raw = desc.value; if (raw === undefined) continue; if (field.kind === "boolean") { if (typeof raw !== "boolean") { return { ok: false, reason: `${what}: option "${key}" must be a boolean` }; } out[key] = raw; continue; } if (field.kind === "count") { if (typeof raw !== "number" || !_isSafeInteger(raw) || raw < 0) { return { ok: false, reason: `${what}: option "${key}" must be a non-negative safe integer` }; } const max = field.max; if (max === undefined || raw > max) { return { ok: false, reason: `${what}: option "${key}" exceeds its permitted ceiling` }; } out[key] = raw; continue; } if (field.kind === "nested") { if (field.schema === undefined) { return { ok: false, reason: `${what}: option "${key}" is declared nested without a schema — an unvalidated sub-object is not configuration` }; } if (depth >= MAX_OPTION_DEPTH) { return { ok: false, reason: `${what}: option "${key}" nests deeper than the schema permits` }; } const inner = admit(field.schema, raw, `${what}.${key}`, depth + 1); if (!inner.ok) return { ok: false, reason: inner.reason }; out[key] = inner.value; continue; } const decoded = decodeDocument(raw, `${what}: option "${key}"`); if (!decoded.ok) return { ok: false, reason: decoded.reason }; out[key] = decoded.text; } return { ok: true, value: _freeze(out) }; }