// Proof: a private structure's DB cannot be mounted by a handler running for a // user who isn't a member — even when that DB is aliased into a PUBLIC structure. // // It drives the REAL model code from db.js (canAccessStructure, getDbsForStructure, // attachDb, ...) against a throwaway database in a temp dir, and reproduces the // gate from index.js:192-211 (makeLibs) verbatim so the throw is the app's, not ours. const fs = require("fs"); const os = require("os"); const path = require("path"); // db.js opens "./dbs/0.sqlite" and writes user dbs under "./dbs" relative to CWD. // Point CWD at a fresh temp dir so we never touch the real data. const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "bliss-proof-")); fs.mkdirSync(path.join(tmp, "dbs")); process.chdir(tmp); const PROJECT = "/home/happy/code/bliss"; const model = require(path.join(PROJECT, "db.js")); model.applyMigrations(); // ---- the exact gate from index.js makeLibs (lines 192-211), copied verbatim ---- // Given the structure being served and the requesting user, return a db(alias) // resolver identical to what a route handler receives via require('db'). function makeDbResolver(structureId, memberUserId) { const dbs = {}; const forbidden = new Set(); for (let appDb of model.getDbsForStructure(structureId)) { const ownDb = String(appDb.db_struct_id) === String(structureId); if (!ownDb && !model.canAccessStructure(memberUserId, appDb.db_struct_id)) { forbidden.add(appDb.alias); continue; } model.getDbInstance(appDb.id); // would-mount } return (alias) => { if (forbidden.has(alias)) { throw new Error(`db '${alias}' is private; you do not have access`); } return dbs[alias]; }; } function attempt(label, structureId, userId, alias) { try { makeDbResolver(structureId, userId)(alias); console.log(` MOUNTED | ${label}`); return "mounted"; } catch (e) { console.log(` BLOCKED | ${label} -> ${e.message}`); return "blocked"; } } // ---- scenario ---- const alice = model.createUser("alice", "x"); // member of the private structure const bob = model.createUser("bob", "x"); // outsider // PRIVATE structure owned by alice, with its own DB "secrets". const priv = model.createStructure("private-vault", alice); const secretsDbId = model.createDb(priv, "secrets"); model.setStructurePrivacy(priv, true, alice); // flips private=1, keeps alice a member // PUBLIC structure that ALIASES the private DB in under the same name. const pub = model.createStructure("public-front", bob); model.attachDb(pub, secretsDbId, "secrets"); console.log(`\nprivate structure #${priv} owns db #${secretsDbId} ("secrets"), private=${model.getStructure(priv).private}`); console.log(`public structure #${pub} aliases that same db in as "secrets", private=${model.getStructure(pub).private}\n`); console.log("Serving the PUBLIC structure, handler calls require('db')('secrets'):"); const r1 = attempt("as bob (outsider) ", pub, bob, "secrets"); const r2 = attempt("as anonymous / WS (userId=null)", pub, null, "secrets"); const r3 = attempt("as alice (member of private)", pub, alice, "secrets"); console.log("\nSanity — serving the PRIVATE structure itself (its OWN db, ownDb=true):"); const r4 = attempt("as alice (member) ", priv, alice, "secrets"); // ---- assertions ---- const pass = r1 === "blocked" && r2 === "blocked" && r3 === "mounted" && r4 === "mounted"; console.log( `\n${pass ? "PROVEN" : "FAILED"}: aliasing a private db into a public structure does NOT leak it — ` + `only users who can access the owning structure can mount it.`, ); fs.rmSync(tmp, { recursive: true, force: true }); process.exit(pass ? 0 : 1);