@nifrajs/mcp-db 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/dist/engine.d.ts +71 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +494 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +8 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -552
- package/dist/index.js.map +1 -1
- package/dist/internal/pg-exec.d.ts +113 -0
- package/dist/internal/pg-exec.d.ts.map +1 -0
- package/dist/internal/pg-exec.js +286 -0
- package/dist/internal/pg-exec.js.map +1 -0
- package/dist/internal/pg-lexer.d.ts +52 -0
- package/dist/internal/pg-lexer.d.ts.map +1 -0
- package/dist/internal/pg-lexer.js +273 -0
- package/dist/internal/pg-lexer.js.map +1 -0
- package/dist/internal/shared.d.ts +161 -0
- package/dist/internal/shared.d.ts.map +1 -0
- package/dist/internal/shared.js +538 -0
- package/dist/internal/shared.js.map +1 -0
- package/dist/internal/sqlite-session.d.ts +21 -0
- package/dist/internal/sqlite-session.d.ts.map +1 -0
- package/dist/internal/sqlite-session.js +254 -0
- package/dist/internal/sqlite-session.js.map +1 -0
- package/dist/postgres.d.ts +148 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +580 -0
- package/dist/postgres.js.map +1 -0
- package/package.json +15 -2
- package/src/engine.ts +664 -0
- package/src/index.ts +43 -588
- package/src/internal/pg-exec.ts +386 -0
- package/src/internal/pg-lexer.ts +279 -0
- package/src/internal/shared.ts +705 -0
- package/src/internal/sqlite-session.ts +273 -0
- package/src/postgres.ts +777 -0
package/dist/postgres.js
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/mcp-db/postgres` - a read-only Postgres engine for a development database, on Bun's
|
|
3
|
+
* built-in `Bun.SQL` (no npm driver). Every layer below is enforced on each call, in this order:
|
|
4
|
+
*
|
|
5
|
+
* 1. Host gate ({@link connectPostgres}): loopback, `*.localhost` or a unix socket, else only a host
|
|
6
|
+
* named in `allowHosts`. Connection fields are passed explicitly, so `DATABASE_URL`/`PG*` in the
|
|
7
|
+
* environment can never redirect the connection.
|
|
8
|
+
* 2. Role gate: a superuser, a member of a superuser role or of `pg_execute_server_program` /
|
|
9
|
+
* `pg_read_server_files` / `pg_write_server_files`, or a role that may execute the server-file
|
|
10
|
+
* functions is refused - a read-only transaction does not stop `COPY ... TO PROGRAM` or
|
|
11
|
+
* `pg_read_file`. {@link postgresRoleSql} prints the role to use instead.
|
|
12
|
+
* 3. Extension gate: `dblink`, `postgres_fdw`, `file_fdw` or an untrusted language the role can use
|
|
13
|
+
* is refused unless `allowExtensions` names it - `dblink_exec` writes to another database from a
|
|
14
|
+
* read-only transaction.
|
|
15
|
+
* 4. Transaction: every connection starts with `default_transaction_read_only=on`; each call runs in
|
|
16
|
+
* `BEGIN READ ONLY` with `statement_timeout`, `lock_timeout` and
|
|
17
|
+
* `idle_in_transaction_session_timeout` set locally, and always rolls back.
|
|
18
|
+
* 5. Statement gate: the query goes over the extended protocol (the server refuses a second
|
|
19
|
+
* statement) as `DECLARE ... NO SCROLL CURSOR` (the server accepts only a query there), and at most
|
|
20
|
+
* `maxRows + 1` rows are fetched. A tokenizer in front refuses side-effect functions.
|
|
21
|
+
* 6. Table scope: the plan's relations (and every table they inherit from) must sit in the allowed
|
|
22
|
+
* schemas and outside `exclude`. A function can still read a table internally; hard isolation
|
|
23
|
+
* comes from the role's grants, which {@link postgresRoleSql} writes with a REVOKE per exclusion.
|
|
24
|
+
*
|
|
25
|
+
* const target = parsePostgresUrl("postgres://nifra_dev_reader@localhost:5432/app")
|
|
26
|
+
* const client = connectPostgres(target, { timeoutMs: 5000 })
|
|
27
|
+
* const rows = await queryPostgres(client, "SELECT status, count(*) FROM orders GROUP BY 1", {
|
|
28
|
+
* timeoutMs: 5000, maxRows: 100, maxResultBytes: 100 * 1024,
|
|
29
|
+
* })
|
|
30
|
+
*/
|
|
31
|
+
import { explainThroughServer, fetchThroughCursor, inReadOnlyTransaction, records, } from "./internal/pg-exec.js";
|
|
32
|
+
import { DENIED_FUNCTION_PREFIXES, lintPostgres, withoutTerminator } from "./internal/pg-lexer.js";
|
|
33
|
+
import { dbRefusal, fitToBytes, isDbRefusal, shapeRows, } from "./internal/shared.js";
|
|
34
|
+
const SSL_MODES = ["disable", "allow", "prefer", "require", "verify-ca", "verify-full"];
|
|
35
|
+
/**
|
|
36
|
+
* Parse a `postgres://` (or `postgresql://`) URL. Only the host, port, user, password, database and
|
|
37
|
+
* `sslmode` are read - any other parameter (`options=-c ...` included) is ignored, so a URL cannot
|
|
38
|
+
* switch off the read-only session settings.
|
|
39
|
+
*/
|
|
40
|
+
export function parsePostgresUrl(url) {
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = new URL(url);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url is not a valid URL");
|
|
47
|
+
}
|
|
48
|
+
if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:") {
|
|
49
|
+
return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url must start with postgres:// or postgresql://");
|
|
50
|
+
}
|
|
51
|
+
const params = parsed.searchParams;
|
|
52
|
+
const queryHost = params.get("host") ?? undefined;
|
|
53
|
+
const host = (queryHost ?? decodeURIComponent(parsed.hostname)).replace(/^\[(.*)\]$/, "$1");
|
|
54
|
+
if (host.includes(",")) {
|
|
55
|
+
return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url names several hosts; name one");
|
|
56
|
+
}
|
|
57
|
+
const port = Number(parsed.port || params.get("port") || 5432);
|
|
58
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
59
|
+
return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url has an invalid port");
|
|
60
|
+
}
|
|
61
|
+
const username = decodeURIComponent(parsed.username) || params.get("user") || "postgres";
|
|
62
|
+
const password = decodeURIComponent(parsed.password) || params.get("password") || undefined;
|
|
63
|
+
const database = decodeURIComponent(parsed.pathname.replace(/^\//, "")) || params.get("dbname") || username;
|
|
64
|
+
const sslmode = params.get("sslmode") ?? undefined;
|
|
65
|
+
const tls = SSL_MODES.find((mode) => mode === sslmode);
|
|
66
|
+
if (sslmode !== undefined && tls === undefined) {
|
|
67
|
+
return dbRefusal("NIFRA_DB_CONFIG", `devDatabase.url has an unknown sslmode ${sslmode}`);
|
|
68
|
+
}
|
|
69
|
+
const location = host === "" || host.startsWith("/")
|
|
70
|
+
? { socket: `${host === "" ? "/tmp" : host.replace(/\/$/, "")}/.s.PGSQL.${port}` }
|
|
71
|
+
: { host };
|
|
72
|
+
return {
|
|
73
|
+
...location,
|
|
74
|
+
port,
|
|
75
|
+
username,
|
|
76
|
+
database,
|
|
77
|
+
...(password === undefined ? {} : { password }),
|
|
78
|
+
...(tls === undefined ? {} : { tls }),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** True for `localhost`, `*.localhost`, 127.0.0.0/8 and `::1` (also as an IPv4-mapped address). */
|
|
82
|
+
export function isLocalPostgresHost(host) {
|
|
83
|
+
const name = host.toLowerCase().replace(/^\[(.*)\]$/, "$1");
|
|
84
|
+
if (name === "localhost" || name.endsWith(".localhost"))
|
|
85
|
+
return true;
|
|
86
|
+
const v4 = /^(?:::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(name);
|
|
87
|
+
if (v4 !== null) {
|
|
88
|
+
const octets = v4.slice(1).map(Number);
|
|
89
|
+
return octets.every((octet) => octet <= 255) && octets[0] === 127;
|
|
90
|
+
}
|
|
91
|
+
return name === "::1" || name === "0:0:0:0:0:0:0:1";
|
|
92
|
+
}
|
|
93
|
+
/** Refuse a TCP host that is neither local nor named in `allowHosts`. */
|
|
94
|
+
export function postgresHostRefusal(target, allowHosts = []) {
|
|
95
|
+
if (target.socket !== undefined || target.host === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
if (isLocalPostgresHost(target.host))
|
|
98
|
+
return undefined;
|
|
99
|
+
const host = target.host.toLowerCase();
|
|
100
|
+
if (allowHosts.some((allowed) => allowed.toLowerCase() === host))
|
|
101
|
+
return undefined;
|
|
102
|
+
return dbRefusal("NIFRA_DB_REMOTE_HOST", `${target.host} is not a local host, and devDatabase.allowHosts does not name it`);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Open a one-connection `Bun.SQL` client for `target` after the host gate. Every session starts
|
|
106
|
+
* read-only with `standard_conforming_strings` on (the statement tokenizer reads strings that way).
|
|
107
|
+
*/
|
|
108
|
+
export function connectPostgres(target, options) {
|
|
109
|
+
const refusal = postgresHostRefusal(target, options.allowHosts);
|
|
110
|
+
if (refusal !== undefined)
|
|
111
|
+
return refusal;
|
|
112
|
+
if (typeof Bun === "undefined") {
|
|
113
|
+
return dbRefusal("NIFRA_DB_DRIVER", "the Postgres engine needs Bun (it uses Bun.SQL)");
|
|
114
|
+
}
|
|
115
|
+
return new Bun.SQL({
|
|
116
|
+
adapter: "postgres",
|
|
117
|
+
...(target.socket === undefined ? { hostname: target.host } : { path: target.socket }),
|
|
118
|
+
port: target.port,
|
|
119
|
+
username: target.username,
|
|
120
|
+
...(target.password === undefined ? {} : { password: target.password }),
|
|
121
|
+
database: target.database,
|
|
122
|
+
...(target.tls === undefined ? {} : { tls: target.tls }),
|
|
123
|
+
max: 1,
|
|
124
|
+
bigint: true,
|
|
125
|
+
connectionTimeout: Math.max(1, Math.ceil(options.timeoutMs / 1000)),
|
|
126
|
+
connection: {
|
|
127
|
+
application_name: "nifra-db",
|
|
128
|
+
default_transaction_read_only: "on",
|
|
129
|
+
standard_conforming_strings: "on",
|
|
130
|
+
statement_timeout: options.timeoutMs,
|
|
131
|
+
idle_in_transaction_session_timeout: options.timeoutMs,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const asStrings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
136
|
+
async function readRole(connection) {
|
|
137
|
+
const [row] = records(await connection `
|
|
138
|
+
SELECT current_user::text AS role,
|
|
139
|
+
r.rolsuper AS superuser,
|
|
140
|
+
pg_catalog.current_setting('server_version_num')::int AS version,
|
|
141
|
+
to_json(ARRAY(SELECT s.rolname::text FROM pg_catalog.pg_roles s
|
|
142
|
+
WHERE s.rolsuper AND s.rolname <> current_user
|
|
143
|
+
AND pg_catalog.pg_has_role(current_user, s.oid, 'MEMBER') ORDER BY 1)) AS superuser_roles,
|
|
144
|
+
to_json(ARRAY(SELECT p.rolname::text FROM pg_catalog.pg_roles p
|
|
145
|
+
WHERE p.rolname IN ('pg_execute_server_program', 'pg_read_server_files', 'pg_write_server_files')
|
|
146
|
+
AND pg_catalog.pg_has_role(current_user, p.oid, 'MEMBER') ORDER BY 1)) AS server_roles,
|
|
147
|
+
to_json(ARRAY(SELECT DISTINCT f.proname::text FROM pg_catalog.pg_proc f
|
|
148
|
+
WHERE f.pronamespace = 'pg_catalog'::regnamespace
|
|
149
|
+
AND f.proname IN ('pg_read_file', 'pg_read_binary_file', 'pg_ls_dir', 'pg_stat_file', 'lo_import', 'lo_export')
|
|
150
|
+
AND pg_catalog.has_function_privilege(f.oid, 'EXECUTE') ORDER BY 1)) AS file_functions,
|
|
151
|
+
to_json(ARRAY(
|
|
152
|
+
SELECT e.extname::text FROM pg_catalog.pg_extension e
|
|
153
|
+
WHERE e.extname = 'dblink' AND EXISTS (
|
|
154
|
+
SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_proc p ON p.oid = d.objid
|
|
155
|
+
WHERE d.classid = 'pg_catalog.pg_proc'::regclass AND d.refclassid = 'pg_catalog.pg_extension'::regclass
|
|
156
|
+
AND d.refobjid = e.oid AND d.deptype = 'e' AND pg_catalog.has_function_privilege(p.oid, 'EXECUTE'))
|
|
157
|
+
UNION
|
|
158
|
+
SELECT w.fdwname::text FROM pg_catalog.pg_foreign_data_wrapper w
|
|
159
|
+
WHERE w.fdwname IN ('postgres_fdw', 'file_fdw') AND (
|
|
160
|
+
pg_catalog.has_foreign_data_wrapper_privilege(w.oid, 'USAGE') OR EXISTS (
|
|
161
|
+
SELECT 1 FROM pg_catalog.pg_foreign_table ft JOIN pg_catalog.pg_foreign_server fs ON fs.oid = ft.ftserver
|
|
162
|
+
WHERE fs.srvfdw = w.oid AND pg_catalog.has_table_privilege(ft.ftrelid, 'SELECT')))
|
|
163
|
+
ORDER BY 1)) AS extensions,
|
|
164
|
+
to_json(ARRAY(SELECT l.lanname::text FROM pg_catalog.pg_language l
|
|
165
|
+
WHERE NOT l.lanpltrusted AND l.lanname NOT IN ('c', 'internal') AND (
|
|
166
|
+
pg_catalog.has_language_privilege(l.oid, 'USAGE') OR EXISTS (
|
|
167
|
+
SELECT 1 FROM pg_catalog.pg_proc p WHERE p.prolang = l.oid
|
|
168
|
+
AND pg_catalog.has_function_privilege(p.oid, 'EXECUTE')))
|
|
169
|
+
ORDER BY 1)) AS languages
|
|
170
|
+
FROM pg_catalog.pg_roles r WHERE r.rolname = current_user`);
|
|
171
|
+
if (row === undefined)
|
|
172
|
+
throw new Error("the connected role is not in pg_roles");
|
|
173
|
+
return {
|
|
174
|
+
role: String(row.role),
|
|
175
|
+
superuser: row.superuser === true,
|
|
176
|
+
serverVersionNum: Number(row.version),
|
|
177
|
+
superuserRoles: asStrings(row.superuser_roles),
|
|
178
|
+
serverRoles: asStrings(row.server_roles),
|
|
179
|
+
fileFunctions: asStrings(row.file_functions),
|
|
180
|
+
extensions: asStrings(row.extensions),
|
|
181
|
+
languages: asStrings(row.languages),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** Read the connected role's privileges, in a read-only transaction. */
|
|
185
|
+
export function inspectPostgresRole(client, options) {
|
|
186
|
+
return inReadOnlyTransaction(client, options.timeoutMs, readRole);
|
|
187
|
+
}
|
|
188
|
+
/** The role gate and the extension gate, as one decision over a {@link PostgresRoleReport}. */
|
|
189
|
+
export function postgresRoleRefusal(report, options = {}) {
|
|
190
|
+
const role = JSON.stringify(report.role);
|
|
191
|
+
if (report.superuser)
|
|
192
|
+
return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a superuser`);
|
|
193
|
+
if (report.superuserRoles.length > 0) {
|
|
194
|
+
return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a member of the superuser role ${report.superuserRoles.join(", ")}`);
|
|
195
|
+
}
|
|
196
|
+
if (report.serverRoles.length > 0) {
|
|
197
|
+
return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a member of ${report.serverRoles.join(", ")}, which reach server programs or files`);
|
|
198
|
+
}
|
|
199
|
+
if (report.fileFunctions.length > 0) {
|
|
200
|
+
return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} may execute ${report.fileFunctions.join(", ")}, which read or write server files`);
|
|
201
|
+
}
|
|
202
|
+
const allowed = new Set((options.allowExtensions ?? []).map((name) => name.toLowerCase()));
|
|
203
|
+
const usable = [...report.extensions, ...report.languages].filter((name) => !allowed.has(name.toLowerCase()));
|
|
204
|
+
if (usable.length > 0) {
|
|
205
|
+
return dbRefusal("NIFRA_DB_EXTENSION", `role ${role} can use ${usable.join(", ")}, which can reach outside this database`);
|
|
206
|
+
}
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
/** Refuse what the tokenizer layer refuses: a non-query, several statements, a denied function. */
|
|
210
|
+
export function lintPostgresStatement(sql) {
|
|
211
|
+
const refusal = lintPostgres(sql);
|
|
212
|
+
if (refusal === undefined)
|
|
213
|
+
return undefined;
|
|
214
|
+
return dbRefusal(refusal.kind === "function" ? "NIFRA_DB_FUNCTION_REFUSED" : "NIFRA_DB_WRITE_REFUSED", refusal.message);
|
|
215
|
+
}
|
|
216
|
+
function scopeMatcher(scope) {
|
|
217
|
+
const schemas = new Set((scope.schemas ?? ["public"]).map((name) => name.toLowerCase()));
|
|
218
|
+
const excluded = new Set((scope.exclude ?? []).map((name) => name.toLowerCase()));
|
|
219
|
+
return ({ schema, name }) => {
|
|
220
|
+
const qualified = `${schema}.${name}`;
|
|
221
|
+
if (!schemas.has(schema.toLowerCase())) {
|
|
222
|
+
return `the query reads ${qualified}, outside the allowed schemas (${[...schemas].join(", ")})`;
|
|
223
|
+
}
|
|
224
|
+
if (excluded.has(name.toLowerCase()) || excluded.has(qualified.toLowerCase())) {
|
|
225
|
+
return `the query reads ${qualified}, which devDatabase.exclude leaves out`;
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The plan-level table check: each relation, and every table it inherits from (partitions too). A
|
|
232
|
+
* `pg_*` function in FROM reads server state the way a catalog does - a system view such as
|
|
233
|
+
* `pg_stat_activity` plans as one once the planner drops its catalog joins - so it is out of scope too.
|
|
234
|
+
* With `masked`, a plan expression that uses a masked column, or a whole row holding one, is refused.
|
|
235
|
+
*/
|
|
236
|
+
function checkScope(scope, masked) {
|
|
237
|
+
const outOfScope = scopeMatcher(scope);
|
|
238
|
+
return async (connection, reads) => {
|
|
239
|
+
for (const fn of reads.functions) {
|
|
240
|
+
const name = fn.name.toLowerCase();
|
|
241
|
+
if (fn.schema === "pg_catalog" && name.startsWith("pg_")) {
|
|
242
|
+
return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", `the query reads server state through ${fn.schema}.${fn.name}(), a system catalog function`);
|
|
243
|
+
}
|
|
244
|
+
if (DENIED_FUNCTION_PREFIXES.some((prefix) => name.startsWith(prefix))) {
|
|
245
|
+
return dbRefusal("NIFRA_DB_FUNCTION_REFUSED", `the query calls ${fn.schema}.${fn.name}(), which reaches outside a read-only query`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const { relations } = reads;
|
|
249
|
+
for (const relation of relations) {
|
|
250
|
+
const reason = outOfScope(relation);
|
|
251
|
+
if (reason !== undefined)
|
|
252
|
+
return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", reason);
|
|
253
|
+
}
|
|
254
|
+
if (relations.length === 0)
|
|
255
|
+
return undefined;
|
|
256
|
+
const ancestors = records(await connection `
|
|
257
|
+
WITH RECURSIVE planned AS (
|
|
258
|
+
SELECT pg_catalog.to_regclass(pg_catalog.quote_ident(r.schema) || '.' || pg_catalog.quote_ident(r.name))::oid AS oid
|
|
259
|
+
FROM pg_catalog.json_to_recordset(${[...relations]}::json) AS r(schema text, name text)
|
|
260
|
+
), up(oid) AS (
|
|
261
|
+
SELECT i.inhparent FROM pg_catalog.pg_inherits i JOIN planned p ON i.inhrelid = p.oid
|
|
262
|
+
UNION
|
|
263
|
+
SELECT i.inhparent FROM pg_catalog.pg_inherits i JOIN up ON i.inhrelid = up.oid
|
|
264
|
+
)
|
|
265
|
+
SELECT n.nspname::text AS schema, c.relname::text AS name
|
|
266
|
+
FROM up JOIN pg_catalog.pg_class c ON c.oid = up.oid
|
|
267
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace`);
|
|
268
|
+
for (const row of ancestors) {
|
|
269
|
+
const reason = outOfScope({ schema: String(row.schema), name: String(row.name) });
|
|
270
|
+
if (reason !== undefined)
|
|
271
|
+
return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", reason);
|
|
272
|
+
}
|
|
273
|
+
if (masked === undefined)
|
|
274
|
+
return undefined;
|
|
275
|
+
const hits = new Set();
|
|
276
|
+
const wholeRows = [];
|
|
277
|
+
for (const { relation, column } of reads.columns) {
|
|
278
|
+
if (column === "*")
|
|
279
|
+
wholeRows.push(relation);
|
|
280
|
+
else if (masked(column))
|
|
281
|
+
hits.add(`${relation.name}.${column}`);
|
|
282
|
+
}
|
|
283
|
+
const listed = [...wholeRows, ...reads.outputs.map((output) => output.relation)];
|
|
284
|
+
if (listed.length > 0) {
|
|
285
|
+
const live = new Map();
|
|
286
|
+
const rows = records(await connection `
|
|
287
|
+
SELECT r.schema, r.name, a.attname::text AS column
|
|
288
|
+
FROM pg_catalog.json_to_recordset(${listed}::json) AS r(schema text, name text)
|
|
289
|
+
JOIN pg_catalog.pg_attribute a
|
|
290
|
+
ON a.attrelid = pg_catalog.to_regclass(pg_catalog.quote_ident(r.schema) || '.' || pg_catalog.quote_ident(r.name))
|
|
291
|
+
WHERE a.attnum > 0 AND NOT a.attisdropped`);
|
|
292
|
+
for (const row of rows) {
|
|
293
|
+
const key = `${String(row.schema)}.${String(row.name)}`;
|
|
294
|
+
live.set(key, [...(live.get(key) ?? []), String(row.column)]);
|
|
295
|
+
}
|
|
296
|
+
const columnsOf = (relation) => live.get(`${relation.schema}.${relation.name}`) ?? [];
|
|
297
|
+
for (const relation of wholeRows) {
|
|
298
|
+
for (const column of columnsOf(relation))
|
|
299
|
+
if (masked(column))
|
|
300
|
+
hits.add(`${relation.name}.${column}`);
|
|
301
|
+
}
|
|
302
|
+
for (const output of reads.outputs) {
|
|
303
|
+
const all = columnsOf(output.relation);
|
|
304
|
+
// Every column is the physical target list: the parent's expressions say what it uses.
|
|
305
|
+
if (all.length > 0 && all.every((column) => output.columns.includes(column)))
|
|
306
|
+
continue;
|
|
307
|
+
for (const column of output.columns)
|
|
308
|
+
if (masked(column))
|
|
309
|
+
hits.add(`${output.relation.name}.${column}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (hits.size === 0)
|
|
313
|
+
return undefined;
|
|
314
|
+
return dbRefusal("NIFRA_DB_COLUMN_REFUSED", `the query reads ${[...hits].join(", ")}, masked as ${hits.size === 1 ? "a credential" : "credentials"}`);
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
async function gated(client, sql, options, work) {
|
|
318
|
+
const linted = lintPostgresStatement(sql);
|
|
319
|
+
if (linted !== undefined)
|
|
320
|
+
return linted;
|
|
321
|
+
return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
|
|
322
|
+
const refusal = postgresRoleRefusal(await readRole(connection), options);
|
|
323
|
+
if (refusal !== undefined)
|
|
324
|
+
return refusal;
|
|
325
|
+
return work(connection, withoutTerminator(sql.trim()));
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Run one read-only query through every layer and return capped rows. A column `redaction.column`
|
|
330
|
+
* masks is refused wherever the plan uses it (selected under any alias, inside an expression, in a
|
|
331
|
+
* filter, or within a whole row); a matching result name is masked as well.
|
|
332
|
+
*/
|
|
333
|
+
export async function queryPostgres(client, sql, options) {
|
|
334
|
+
const fetched = await gated(client, sql, options, (connection, statement) => fetchThroughCursor(connection, statement, {
|
|
335
|
+
maxRows: options.maxRows,
|
|
336
|
+
scope: checkScope(options, options.redaction?.column),
|
|
337
|
+
}));
|
|
338
|
+
if (isDbRefusal(fetched))
|
|
339
|
+
return fetched;
|
|
340
|
+
return shapeRows(fetched.columns, fetched.rows, options);
|
|
341
|
+
}
|
|
342
|
+
/** Drop plan children below `depth` so an oversized plan still says what its top does. */
|
|
343
|
+
function prunePlan(node, depth) {
|
|
344
|
+
if (Array.isArray(node))
|
|
345
|
+
return node.map((item) => prunePlan(item, depth));
|
|
346
|
+
if (typeof node !== "object" || node === null)
|
|
347
|
+
return node;
|
|
348
|
+
const out = {};
|
|
349
|
+
for (const [key, value] of Object.entries(node)) {
|
|
350
|
+
if (key === "Plans") {
|
|
351
|
+
if (depth > 0)
|
|
352
|
+
out[key] = prunePlan(value, depth - 1);
|
|
353
|
+
}
|
|
354
|
+
else
|
|
355
|
+
out[key] = key === "Plan" ? prunePlan(value, depth) : value;
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
/** `EXPLAIN (FORMAT JSON)` for one query, after the same layers as {@link queryPostgres}. */
|
|
360
|
+
export async function explainPostgres(client, sql, options) {
|
|
361
|
+
const explained = await gated(client, sql, options, (connection, statement) => explainThroughServer(connection, statement, {
|
|
362
|
+
analyze: options.analyze === true,
|
|
363
|
+
scope: checkScope(options),
|
|
364
|
+
}));
|
|
365
|
+
if (isDbRefusal(explained))
|
|
366
|
+
return explained;
|
|
367
|
+
const full = JSON.stringify(explained.plan);
|
|
368
|
+
if (new TextEncoder().encode(full).byteLength <= options.maxResultBytes) {
|
|
369
|
+
return { plan: explained.plan, analyzed: options.analyze === true, truncated: false };
|
|
370
|
+
}
|
|
371
|
+
const fit = fitToBytes(32, (depth) => prunePlan(explained.plan, depth), options.maxResultBytes);
|
|
372
|
+
return { plan: fit?.value ?? null, analyzed: options.analyze === true, truncated: true };
|
|
373
|
+
}
|
|
374
|
+
const RELKINDS = {
|
|
375
|
+
r: "table",
|
|
376
|
+
p: "partitioned table",
|
|
377
|
+
v: "view",
|
|
378
|
+
m: "materialized view",
|
|
379
|
+
f: "foreign table",
|
|
380
|
+
};
|
|
381
|
+
const jsonValues = (value) => {
|
|
382
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
383
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
384
|
+
};
|
|
385
|
+
const jsonStrings = (value) => asStrings(jsonValues(value));
|
|
386
|
+
/**
|
|
387
|
+
* Describe the readable tables and views in the allowed schemas from `pg_catalog`, with queries this
|
|
388
|
+
* module writes (no caller SQL runs), so it works on any role: columns, primary keys, foreign keys,
|
|
389
|
+
* indexes and the planner's row estimate.
|
|
390
|
+
*/
|
|
391
|
+
export async function readPostgresSchema(client, options) {
|
|
392
|
+
const schemas = [...(options.schemas ?? ["public"])];
|
|
393
|
+
const excluded = new Set((options.exclude ?? []).map((name) => name.toLowerCase()));
|
|
394
|
+
const isExcluded = (schema, name) => excluded.has(name.toLowerCase()) || excluded.has(`${schema}.${name}`.toLowerCase());
|
|
395
|
+
const wanted = options.table?.toLowerCase();
|
|
396
|
+
const matchesWanted = (schema, name) => wanted === undefined ||
|
|
397
|
+
name.toLowerCase() === wanted ||
|
|
398
|
+
`${schema}.${name}`.toLowerCase() === wanted;
|
|
399
|
+
return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
|
|
400
|
+
const relations = records(await connection `
|
|
401
|
+
SELECT n.nspname::text AS schema, c.relname::text AS name, c.relkind::text AS kind,
|
|
402
|
+
c.reltuples::float8 AS estimate,
|
|
403
|
+
to_json(ARRAY(
|
|
404
|
+
WITH RECURSIVE reach(oid) AS (
|
|
405
|
+
SELECT c.oid
|
|
406
|
+
UNION
|
|
407
|
+
SELECT next.oid FROM reach, LATERAL (
|
|
408
|
+
SELECT i.inhparent AS oid FROM pg_catalog.pg_inherits i WHERE i.inhrelid = reach.oid
|
|
409
|
+
UNION ALL
|
|
410
|
+
SELECT d.refobjid FROM pg_catalog.pg_rewrite w
|
|
411
|
+
JOIN pg_catalog.pg_depend d ON d.classid = 'pg_catalog.pg_rewrite'::regclass
|
|
412
|
+
AND d.objid = w.oid AND d.refclassid = 'pg_catalog.pg_class'::regclass
|
|
413
|
+
WHERE w.ev_class = reach.oid
|
|
414
|
+
) AS next
|
|
415
|
+
)
|
|
416
|
+
SELECT pg_catalog.json_build_array(pn.nspname::text, pc.relname::text) FROM reach
|
|
417
|
+
JOIN pg_catalog.pg_class pc ON pc.oid = reach.oid
|
|
418
|
+
JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
|
|
419
|
+
WHERE reach.oid <> c.oid)) AS reaches
|
|
420
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
421
|
+
WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f') AND NOT c.relispartition
|
|
422
|
+
AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
|
|
423
|
+
AND pg_catalog.has_table_privilege(c.oid, 'SELECT')
|
|
424
|
+
ORDER BY 1, 2`);
|
|
425
|
+
const columns = records(await connection `
|
|
426
|
+
SELECT n.nspname::text AS schema, c.relname::text AS table, a.attname::text AS name,
|
|
427
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS type, NOT a.attnotnull AS nullable,
|
|
428
|
+
pg_catalog.pg_get_expr(d.adbin, d.adrelid) AS default_value,
|
|
429
|
+
EXISTS (SELECT 1 FROM pg_catalog.pg_constraint k
|
|
430
|
+
WHERE k.conrelid = c.oid AND k.contype = 'p' AND a.attnum = ANY(k.conkey)) AS primary_key
|
|
431
|
+
FROM pg_catalog.pg_attribute a
|
|
432
|
+
JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
|
|
433
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
434
|
+
LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
435
|
+
WHERE a.attnum > 0 AND NOT a.attisdropped AND c.relkind IN ('r', 'p', 'v', 'm', 'f')
|
|
436
|
+
AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
|
|
437
|
+
ORDER BY 1, 2, a.attnum`);
|
|
438
|
+
const foreignKeys = records(await connection `
|
|
439
|
+
SELECT n.nspname::text AS schema, c.relname::text AS table,
|
|
440
|
+
to_json(ARRAY(SELECT a.attname::text FROM unnest(k.conkey) WITH ORDINALITY AS u(num, ord)
|
|
441
|
+
JOIN pg_catalog.pg_attribute a ON a.attrelid = k.conrelid AND a.attnum = u.num ORDER BY u.ord)) AS columns,
|
|
442
|
+
fn.nspname::text AS ref_schema, fc.relname::text AS ref_table,
|
|
443
|
+
to_json(ARRAY(SELECT a.attname::text FROM unnest(k.confkey) WITH ORDINALITY AS u(num, ord)
|
|
444
|
+
JOIN pg_catalog.pg_attribute a ON a.attrelid = k.confrelid AND a.attnum = u.num ORDER BY u.ord)) AS ref_columns
|
|
445
|
+
FROM pg_catalog.pg_constraint k
|
|
446
|
+
JOIN pg_catalog.pg_class c ON c.oid = k.conrelid
|
|
447
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
448
|
+
JOIN pg_catalog.pg_class fc ON fc.oid = k.confrelid
|
|
449
|
+
JOIN pg_catalog.pg_namespace fn ON fn.oid = fc.relnamespace
|
|
450
|
+
WHERE k.contype = 'f'
|
|
451
|
+
AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
|
|
452
|
+
ORDER BY 1, 2, k.conname`);
|
|
453
|
+
const indexes = records(await connection `
|
|
454
|
+
SELECT n.nspname::text AS schema, t.relname::text AS table, i.relname::text AS name,
|
|
455
|
+
x.indisunique AS is_unique,
|
|
456
|
+
to_json(ARRAY(SELECT pg_catalog.pg_get_indexdef(x.indexrelid, k, true)
|
|
457
|
+
FROM generate_series(1, x.indnkeyatts) AS k ORDER BY k)) AS columns
|
|
458
|
+
FROM pg_catalog.pg_index x
|
|
459
|
+
JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid
|
|
460
|
+
JOIN pg_catalog.pg_class t ON t.oid = x.indrelid
|
|
461
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace
|
|
462
|
+
WHERE n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
|
|
463
|
+
ORDER BY 1, 2, 3`);
|
|
464
|
+
const key = (schema, name) => `${String(schema)}.${String(name)}`;
|
|
465
|
+
const text = (value) => options.redaction?.text?.(value) ?? value;
|
|
466
|
+
// What the query scope would refuse is hidden too: a table inheriting from an excluded one, and a
|
|
467
|
+
// view reading one (or reading outside the allowed schemas).
|
|
468
|
+
const allowedSchemas = new Set(schemas.map((name) => name.toLowerCase()));
|
|
469
|
+
const outOfScope = (ancestor) => {
|
|
470
|
+
const [schema, name] = asStrings(ancestor);
|
|
471
|
+
return (schema === undefined ||
|
|
472
|
+
name === undefined ||
|
|
473
|
+
!allowedSchemas.has(schema.toLowerCase()) ||
|
|
474
|
+
isExcluded(schema, name));
|
|
475
|
+
};
|
|
476
|
+
const visible = relations.filter((row) => !isExcluded(String(row.schema), String(row.name)) &&
|
|
477
|
+
!jsonValues(row.reaches).some(outOfScope));
|
|
478
|
+
const shown = visible.filter((row) => matchesWanted(String(row.schema), String(row.name)));
|
|
479
|
+
if (wanted !== undefined && shown.length === 0) {
|
|
480
|
+
return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", `${JSON.stringify(options.table)} is not an exposed table or view of this database`);
|
|
481
|
+
}
|
|
482
|
+
const tables = shown.map((row) => {
|
|
483
|
+
const id = key(row.schema, row.name);
|
|
484
|
+
const kind = RELKINDS[String(row.kind)] ?? "table";
|
|
485
|
+
const estimate = Number(row.estimate);
|
|
486
|
+
return {
|
|
487
|
+
schema: String(row.schema),
|
|
488
|
+
name: String(row.name),
|
|
489
|
+
kind,
|
|
490
|
+
rowEstimate: kind === "view" || !Number.isFinite(estimate) || estimate < 0
|
|
491
|
+
? null
|
|
492
|
+
: Math.round(estimate),
|
|
493
|
+
columns: columns
|
|
494
|
+
.filter((column) => key(column.schema, column.table) === id)
|
|
495
|
+
.map((column) => ({
|
|
496
|
+
name: String(column.name),
|
|
497
|
+
type: String(column.type),
|
|
498
|
+
nullable: column.nullable === true,
|
|
499
|
+
default: typeof column.default_value === "string" ? text(column.default_value) : null,
|
|
500
|
+
primaryKey: column.primary_key === true,
|
|
501
|
+
redacted: options.redaction?.column?.(String(column.name)) === true,
|
|
502
|
+
})),
|
|
503
|
+
foreignKeys: foreignKeys
|
|
504
|
+
.filter((fk) => key(fk.schema, fk.table) === id)
|
|
505
|
+
.map((fk) => ({
|
|
506
|
+
columns: jsonStrings(fk.columns),
|
|
507
|
+
references: {
|
|
508
|
+
schema: String(fk.ref_schema),
|
|
509
|
+
table: String(fk.ref_table),
|
|
510
|
+
columns: jsonStrings(fk.ref_columns),
|
|
511
|
+
},
|
|
512
|
+
})),
|
|
513
|
+
indexes: indexes
|
|
514
|
+
.filter((index) => key(index.schema, index.table) === id)
|
|
515
|
+
.map((index) => ({
|
|
516
|
+
name: String(index.name),
|
|
517
|
+
unique: index.is_unique === true,
|
|
518
|
+
columns: jsonStrings(index.columns),
|
|
519
|
+
})),
|
|
520
|
+
};
|
|
521
|
+
});
|
|
522
|
+
return { tables, excludedCount: relations.length - visible.length };
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
const quoteIdent = (name) => `"${name.replaceAll('"', '""')}"`;
|
|
526
|
+
/**
|
|
527
|
+
* Write the SQL for a login role that can read the allowed schemas and nothing else: `pg_read_all_data`
|
|
528
|
+
* on Postgres 14+ when nothing is excluded; otherwise `GRANT SELECT` per schema plus default
|
|
529
|
+
* privileges, and a `REVOKE` for each excluded table and its partitions (`pg_read_all_data` would
|
|
530
|
+
* override a REVOKE, so it is not used then). Nothing is executed.
|
|
531
|
+
*/
|
|
532
|
+
export async function postgresRoleSql(client, options) {
|
|
533
|
+
const role = options.role ?? "nifra_dev_reader";
|
|
534
|
+
const schemas = options.schemas ?? ["public"];
|
|
535
|
+
const exclude = (options.exclude ?? []).map((name) => name.toLowerCase());
|
|
536
|
+
return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
|
|
537
|
+
const [facts] = records(await connection `
|
|
538
|
+
SELECT pg_catalog.current_database()::text AS database,
|
|
539
|
+
pg_catalog.current_setting('server_version_num')::int AS version`);
|
|
540
|
+
const excluded = exclude.length === 0
|
|
541
|
+
? []
|
|
542
|
+
: records(await connection `
|
|
543
|
+
WITH RECURSIVE picked AS (
|
|
544
|
+
SELECT c.oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
545
|
+
WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f')
|
|
546
|
+
AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${[...schemas]}::json)))
|
|
547
|
+
AND (lower(c.relname) = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${exclude}::json)))
|
|
548
|
+
OR lower(n.nspname || '.' || c.relname) = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${exclude}::json))))
|
|
549
|
+
UNION
|
|
550
|
+
SELECT i.inhrelid FROM pg_catalog.pg_inherits i JOIN picked p ON i.inhparent = p.oid
|
|
551
|
+
)
|
|
552
|
+
SELECT n.nspname::text AS schema, c.relname::text AS name
|
|
553
|
+
FROM picked JOIN pg_catalog.pg_class c ON c.oid = picked.oid
|
|
554
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
555
|
+
ORDER BY 1, 2`);
|
|
556
|
+
const database = String(facts?.database ?? "");
|
|
557
|
+
const version = Number(facts?.version ?? 0);
|
|
558
|
+
const who = quoteIdent(role);
|
|
559
|
+
const statements = [
|
|
560
|
+
`-- Run as the owner of the app's tables (or a superuser). Then set the password from psql with \\password ${role}`,
|
|
561
|
+
`-- and point devDatabase.url at postgres://${role}:<password>@localhost/${database}`,
|
|
562
|
+
`CREATE ROLE ${who} LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;`,
|
|
563
|
+
`ALTER ROLE ${who} SET default_transaction_read_only = on;`,
|
|
564
|
+
`GRANT CONNECT ON DATABASE ${quoteIdent(database)} TO ${who};`,
|
|
565
|
+
];
|
|
566
|
+
if (version >= 140000 && excluded.length === 0) {
|
|
567
|
+
statements.push(`GRANT pg_read_all_data TO ${who};`);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
for (const schema of schemas) {
|
|
571
|
+
statements.push(`GRANT USAGE ON SCHEMA ${quoteIdent(schema)} TO ${who};`, `GRANT SELECT ON ALL TABLES IN SCHEMA ${quoteIdent(schema)} TO ${who};`, `ALTER DEFAULT PRIVILEGES IN SCHEMA ${quoteIdent(schema)} GRANT SELECT ON TABLES TO ${who};`);
|
|
572
|
+
}
|
|
573
|
+
for (const row of excluded) {
|
|
574
|
+
statements.push(`REVOKE SELECT ON TABLE ${quoteIdent(String(row.schema))}.${quoteIdent(String(row.name))} FROM ${who};`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return { role, database, serverVersionNum: version, statements };
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"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"}
|