🌐 AI搜索 & 代理 主页
Skip to content

Commit 4f68063

Browse files
Nik SamokhvalovNik Samokhvalov
authored andcommitted
fix(cli): create/alter role password must be literal
- Inline safely quoted password for CREATE/ALTER USER (bind params not allowed) - Add regression test
1 parent f52c59d commit 4f68063

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

cli/lib/init.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ function quoteIdent(ident: string): string {
3636
return `"${ident.replace(/"/g, "\"\"")}"`;
3737
}
3838

39+
function quoteLiteral(value: string): string {
40+
// Single-quote and escape embedded quotes by doubling.
41+
// This is used where Postgres grammar requires a literal (e.g., CREATE/ALTER ROLE PASSWORD).
42+
return `'${value.replace(/'/g, "''")}'`;
43+
}
44+
3945
export function maskConnectionString(dbUrl: string): string {
4046
// Hide password if present (postgresql://user:pass@host/db).
4147
try {
@@ -308,21 +314,20 @@ export async function buildInitPlan(params: {
308314

309315
const qRole = quoteIdent(monitoringUser);
310316
const qDb = quoteIdent(database);
317+
const qPw = quoteLiteral(params.monitoringPassword);
311318

312319
const steps: InitStep[] = [];
313320

314321
// Role creation/update is done in two alternative steps. Caller decides by checking role existence.
315322
if (params.roleExists === false) {
316323
steps.push({
317324
name: "create monitoring user",
318-
sql: `create user ${qRole} with password $1;`,
319-
params: [params.monitoringPassword],
325+
sql: `create user ${qRole} with password ${qPw};`,
320326
});
321327
} else if (params.roleExists === true) {
322328
steps.push({
323329
name: "update monitoring user password",
324-
sql: `alter user ${qRole} with password $1;`,
325-
params: [params.monitoringPassword],
330+
sql: `alter user ${qRole} with password ${qPw};`,
326331
});
327332
} else {
328333
// Unknown: caller will rebuild after probing role existence.

cli/test/init.test.cjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ test("buildInitPlan includes create user when role does not exist", async () =>
4040
assert.ok(!plan.steps.some((s) => s.optional));
4141
});
4242

43+
test("buildInitPlan inlines password safely for CREATE/ALTER ROLE grammar", async () => {
44+
const plan = await init.buildInitPlan({
45+
database: "mydb",
46+
monitoringUser: "postgres_ai_mon",
47+
monitoringPassword: "pa'ss",
48+
includeOptionalPermissions: false,
49+
roleExists: false,
50+
});
51+
const step = plan.steps.find((s) => s.name === "create monitoring user");
52+
assert.ok(step);
53+
assert.match(step.sql, /password 'pa''ss'/);
54+
assert.equal(step.params, undefined);
55+
});
56+
4357
test("buildInitPlan includes alter user when role exists", async () => {
4458
const plan = await init.buildInitPlan({
4559
database: "mydb",

0 commit comments

Comments
 (0)