fix: harden store writes, strict schemas, null cfg
This commit is contained in:
1 parent
46906e3f5b
commit
51aa6e3258
7 files changed
+96
-26
No files matched your search
@@ -36,6 +36,12 @@ export const readConfigFile = (path: string | undefined): ConnectionConfig[] =>
|
||||
console.error('dbmole: config file ' + path + ' is not valid JSON, ignoring')
|
||||
return []
|
||||
}
|
||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||
console.error(
|
||||
'dbmole: config file ' + path + ' must contain { "connections": [...] }, ignoring'
|
||||
)
|
||||
return []
|
||||
}
|
||||
const connections = (parsed as { connections?: unknown }).connections
|
||||
if (!Array.isArray(connections)) {
|
||||
console.error(
|
||||
|
||||
+20
-5
@@ -1,4 +1,4 @@
|
||||
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
|
||||
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { type ConnectionConfig, connectionConfigSchema } from './types.js'
|
||||
@@ -47,10 +47,25 @@ let writeSeq = 0
|
||||
|
||||
export const writeStore = (path: string, connections: ConnectionConfig[]): void => {
|
||||
mkdirSync(dirname(path), { recursive: true, mode: 0o700 })
|
||||
writeSeq += 1
|
||||
const tmp = path + '.' + process.pid + '.' + writeSeq + '.tmp'
|
||||
const items = connections.map((c) => JSON.stringify(c, null, 4))
|
||||
const content = items.length === 0 ? '[]\n' : '[\n' + items.join(',\n') + '\n]\n'
|
||||
writeFileSync(tmp, content, { mode: 0o600 })
|
||||
renameSync(tmp, path)
|
||||
let tmp: string
|
||||
for (;;) {
|
||||
writeSeq += 1
|
||||
tmp = path + '.' + process.pid + '.' + writeSeq + '.tmp'
|
||||
try {
|
||||
writeFileSync(tmp, content, { mode: 0o600, flag: 'wx' })
|
||||
break
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
renameSync(tmp, path)
|
||||
} catch (error) {
|
||||
rmSync(tmp, { force: true })
|
||||
throw error
|
||||
}
|
||||
}
|
||||
+25
-21
@@ -1,27 +1,31 @@
|
||||
import * as z from 'zod'
|
||||
|
||||
export const sshConfigSchema = z.object({
|
||||
host: z.string().min(1),
|
||||
port: z.number().int().positive().default(22),
|
||||
user: z.string().min(1),
|
||||
password: z.string().optional(),
|
||||
privateKey: z.string().optional(),
|
||||
privateKeyPath: z.string().optional(),
|
||||
passphrase: z.string().optional(),
|
||||
agent: z.boolean().optional()
|
||||
})
|
||||
export const sshConfigSchema = z
|
||||
.object({
|
||||
host: z.string().min(1),
|
||||
port: z.number().int().positive().default(22),
|
||||
user: z.string().min(1),
|
||||
password: z.string().optional(),
|
||||
privateKey: z.string().optional(),
|
||||
privateKeyPath: z.string().optional(),
|
||||
passphrase: z.string().optional(),
|
||||
agent: z.boolean().optional()
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const connectionConfigSchema = z.object({
|
||||
name: z.string().regex(/^[a-zA-Z0-9_-]+$/, 'name must match [a-zA-Z0-9_-]+'),
|
||||
type: z.enum(['postgres', 'mysql']),
|
||||
host: z.string().min(1),
|
||||
port: z.number().int().positive().optional(),
|
||||
user: z.string().min(1),
|
||||
password: z.string().optional(),
|
||||
database: z.string().optional(),
|
||||
readonly: z.boolean().default(false),
|
||||
ssh: sshConfigSchema.optional()
|
||||
})
|
||||
export const connectionConfigSchema = z
|
||||
.object({
|
||||
name: z.string().regex(/^[a-zA-Z0-9_-]+$/, 'name must match [a-zA-Z0-9_-]+'),
|
||||
type: z.enum(['postgres', 'mysql']),
|
||||
host: z.string().min(1),
|
||||
port: z.number().int().positive().optional(),
|
||||
user: z.string().min(1),
|
||||
password: z.string().optional(),
|
||||
database: z.string().optional(),
|
||||
readonly: z.boolean().default(false),
|
||||
ssh: sshConfigSchema.optional()
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type SshConfig = z.infer<typeof sshConfigSchema>
|
||||
export type ConnectionConfig = z.infer<typeof connectionConfigSchema>
|
||||
|
||||
Reference in new issue
Block a user