mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 02:24:10 +08:00
chore(core): move database permissions to separate PR
This commit is contained in:
@@ -5,8 +5,6 @@ import { sqliteLayer, supportsForeignKeyToggle, supportsTuningPragmas } from "#s
|
||||
import { Context, Effect, Layer, Schema, Semaphore } from "effect"
|
||||
import type { SqlClient } from "effect/unstable/sql"
|
||||
import { Global } from "@opencode/util/global"
|
||||
import { closeSync, existsSync, openSync } from "node:fs"
|
||||
import { chmod } from "node:fs/promises"
|
||||
import { isAbsolute, join } from "path"
|
||||
import { DatabaseMigration } from "./migration.js"
|
||||
import { makeGlobalNode } from "@opencode/util/effect/app-node"
|
||||
@@ -72,31 +70,13 @@ export function layer(options: Options = { path: ":memory:" }) {
|
||||
Layer.provide(sqliteLayer({ filename })),
|
||||
)
|
||||
const filename = options.path ?? ":memory:"
|
||||
if (filename === ":memory:") return provide(filename)
|
||||
const file = isAbsolute(filename) ? filename : join((yield* Global.Service).data, filename)
|
||||
yield* Effect.promise(() => restrictToOwner(file))
|
||||
return provide(file)
|
||||
if (filename === ":memory:" || isAbsolute(filename)) return provide(filename)
|
||||
const global = yield* Global.Service
|
||||
return provide(join(global.data, filename))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// SQLite creates new sidecars with the database's mode, but does not tighten existing sidecars.
|
||||
// Windows relies on the user profile directory's inherited ACLs instead of POSIX modes.
|
||||
async function restrictToOwner(filename: string) {
|
||||
if (process.platform === "win32") return
|
||||
// Opening and closing an existing database can release another connection's POSIX locks.
|
||||
// Create missing files synchronously so another fiber cannot open one before it is restricted.
|
||||
if (!existsSync(filename)) closeSync(openSync(filename, "a", 0o600))
|
||||
await chmod(filename, 0o600)
|
||||
await Promise.all(
|
||||
[`${filename}-wal`, `${filename}-shm`].map((file) =>
|
||||
chmod(file, 0o600).catch((error: NodeJS.ErrnoException) => {
|
||||
if (error.code !== "ENOENT") throw error
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// The database service over an injected SqlClient, for runtimes that receive
|
||||
// database storage instead of opening a filesystem path. Any client provided
|
||||
// here still goes through the pragma guards and migrations; Global is required
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { chmod, stat, writeFile } from "node:fs/promises"
|
||||
import path from "path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Database } from "@opencode/core/database/database"
|
||||
import { Global } from "@opencode/util/global"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
|
||||
// Read while the connection is open: SQLite may remove the sidecars on close.
|
||||
const openModes = (filename: string) =>
|
||||
Effect.runPromise(
|
||||
Effect.gen(function* () {
|
||||
yield* Layer.build(Database.layer({ path: filename }))
|
||||
return yield* Effect.promise(() =>
|
||||
Promise.all(["", "-wal", "-shm"].map(async (suffix) => (await stat(filename + suffix)).mode & 0o777)),
|
||||
)
|
||||
}).pipe(Effect.scoped, Effect.provideService(Global.Service, Global.make({ data: path.dirname(filename) }))),
|
||||
)
|
||||
|
||||
describe.skipIf(process.platform === "win32")("Database file permissions", () => {
|
||||
test("creates the database and sidecars readable only by the owner", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
|
||||
expect(await openModes(path.join(tmp.path, "opencode.db"))).toEqual([0o600, 0o600, 0o600])
|
||||
})
|
||||
|
||||
test("tightens an existing database and sidecars", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
const filename = path.join(tmp.path, "opencode.db")
|
||||
await Promise.all(
|
||||
["", "-wal", "-shm"].map(async (suffix) => {
|
||||
await writeFile(filename + suffix, "")
|
||||
await chmod(filename + suffix, 0o644)
|
||||
}),
|
||||
)
|
||||
|
||||
expect(await openModes(filename)).toEqual([0o600, 0o600, 0o600])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user