28 lines
1.7 KiB
TypeScript
28 lines
1.7 KiB
TypeScript
/** Shared pg_dump argv boundary for REST and private MCP exports. No store,
|
|
* configured pool, application bootstrap, or DDL is imported here. */
|
|
export const DATABASE_CONTROL_DUMP_EXCLUDE_SCHEMA_ARG = "--exclude-schema=mmapp_control"
|
|
|
|
export class DatabaseControlArchiveBoundaryError extends Error {
|
|
readonly code = "DATABASE_CONTROL_ARCHIVE_REFUSED"
|
|
}
|
|
const refuse = (): never => { throw new DatabaseControlArchiveBoundaryError("Архив содержит служебное пространство управления базой или неподдерживаемый список объектов") }
|
|
|
|
/** Inspect the actual pg_restore --list output BEFORE archive decoding or
|
|
* target replacement. This is a conservative TOC namespace barrier, not an
|
|
* SQL parser: functions, dependencies and archive SQL are not certified safe.
|
|
* A public object whose description contains this standalone reserved token
|
|
* also refuses. No TOC entry or archive SQL is removed or rewritten. */
|
|
export function assertDatabaseControlArchiveBoundary(toc: Buffer | string): void {
|
|
if (!(typeof toc === "string" || Buffer.isBuffer(toc)) || Buffer.byteLength(toc) > 16 * 1024 * 1024) return refuse()
|
|
let text: string
|
|
try { text = typeof toc === "string" ? toc : new TextDecoder("utf-8", { fatal: true }).decode(toc) }
|
|
catch { return refuse() }
|
|
if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(text)) return refuse()
|
|
const lines = text.split(/\r?\n/)
|
|
if (lines.length > 200000) return refuse()
|
|
for (const line of lines) {
|
|
if (!line.trim() || /^\s*;/.test(line)) continue
|
|
if (!/^\d+;\s+\d+\s+\d+\s+\S/.test(line) || /(?:^|[^A-Za-z0-9_$])mmapp_control(?=$|[^A-Za-z0-9_$])/i.test(line)) return refuse()
|
|
}
|
|
}
|