harden everything more
This commit is contained in:
parent
bba2500473
commit
389c1c1cdc
2 changed files with 70 additions and 2 deletions
67
db.js
67
db.js
|
|
@ -71,7 +71,71 @@ function getUserById(id) {
|
|||
|
||||
const dbCache = new LRUCache({ max: 25 });
|
||||
|
||||
// User-supplied SQL only ever runs against one database file. SQLite's ATTACH
|
||||
// lets a single connection open ANY other file on disk — dbs/0.sqlite (the prime
|
||||
// db: users, sessions, password hashes, every structure's metadata) or any other
|
||||
// tenant's dbs/N.sqlite — so it is a cross-tenant read/write primitive we must
|
||||
// deny on every handle we hand to structure code (repl, db library, route + WS
|
||||
// handlers all get their `sql` from getDbInstance). better-sqlite3 13.x exposes
|
||||
// no authorizer and no attach-limit, so we screen the SQL text. SQLite has no
|
||||
// dynamic-SQL exec, so a statement can only ATTACH if the keyword appears
|
||||
// literally in the text passed here — after we strip the only places it could
|
||||
// otherwise hide (comments and quoted literals/identifiers), a bare ATTACH/DETACH
|
||||
// token is unambiguous. That makes this a complete gate, not a heuristic filter.
|
||||
function assertNoAttach(sql) {
|
||||
if (typeof sql !== "string") return;
|
||||
const stripped = sql
|
||||
.replace(/--[^\n]*/g, " ") // line comments
|
||||
.replace(/\/\*[\s\S]*?\*\//g, " ") // block comments
|
||||
.replace(/'(?:[^']|'')*'/g, " ") // '...' string literals
|
||||
.replace(/"(?:[^"]|"")*"/g, " ") // "..." quoted identifiers
|
||||
.replace(/\[[^\]]*\]/g, " ") // [...] quoted identifiers
|
||||
.replace(/`(?:[^`]|``)*`/g, " "); // `...` quoted identifiers
|
||||
if (/\b(?:attach|detach)\b/i.test(stripped)) {
|
||||
throw new Error("ATTACH/DETACH is not allowed on a Bliss database");
|
||||
}
|
||||
}
|
||||
|
||||
// Methods that take a SQL string as their first argument — screen it.
|
||||
const SQL_ENTRYPOINTS = new Set(["prepare", "exec", "pragma"]);
|
||||
// Methods that reach outside the single db file entirely — never expose them.
|
||||
const BLOCKED_METHODS = new Set(["loadExtension", "backup", "serialize"]);
|
||||
|
||||
// Wrap a raw connection so structure code can use it normally but cannot escape
|
||||
// its one file. Everything passes through untouched except: SQL entrypoints are
|
||||
// screened for ATTACH, and file-reaching methods throw. A Proxy (rather than a
|
||||
// hand-rolled facade) keeps every other property/method — .transaction,
|
||||
// .function, .inTransaction, .name, etc. — working exactly as before.
|
||||
function guardConnection(raw) {
|
||||
return new Proxy(raw, {
|
||||
get(target, prop, receiver) {
|
||||
if (BLOCKED_METHODS.has(prop)) {
|
||||
return () => {
|
||||
throw new Error(`${String(prop)} is disabled on a Bliss database`);
|
||||
};
|
||||
}
|
||||
const value = target[prop];
|
||||
if (typeof value !== "function") return value;
|
||||
if (SQL_ENTRYPOINTS.has(prop)) {
|
||||
return function (sql, ...rest) {
|
||||
assertNoAttach(sql);
|
||||
return value.call(target, sql, ...rest);
|
||||
};
|
||||
}
|
||||
return value.bind(target);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getDbInstance(dbId) {
|
||||
// The prime db is never a user database. Kernel code uses the module-level
|
||||
// `db` handle above; nothing legitimately fetches prime through here. Refuse
|
||||
// it outright so a prime connection can never leak into user code even if an
|
||||
// upstream access check regresses — defense-in-depth behind the ATTACH guard.
|
||||
if (Number(dbId) === PRIME_DB_ID) {
|
||||
throw new Error("the prime db is not accessible");
|
||||
}
|
||||
|
||||
let dbInstance = dbCache.get(dbId);
|
||||
|
||||
if (!dbInstance) {
|
||||
|
|
@ -80,7 +144,8 @@ function getDbInstance(dbId) {
|
|||
dbCache.set(dbId, dbInstance);
|
||||
}
|
||||
|
||||
return dbInstance;
|
||||
// Never hand out the raw connection: it could ATTACH another tenant's file.
|
||||
return guardConnection(dbInstance);
|
||||
}
|
||||
|
||||
function getAllRoutes() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue