fix editor
This commit is contained in:
parent
3a7031e462
commit
f6bde50ce3
6 changed files with 305 additions and 50 deletions
83
proof_plumbing_owner_checks.js
Normal file
83
proof_plumbing_owner_checks.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Proof: the /plumbing read API only ever hands out an artifact to a caller who
|
||||
// may see the structure it BELONGS TO — including via the id-only endpoints the
|
||||
// live editor uses, and even when a private db is aliased into a public one.
|
||||
//
|
||||
// It mounts the REAL plumbing.js router on a throwaway express app (temp db),
|
||||
// with a tiny middleware that fakes a logged-in session from an ?as= query, and
|
||||
// makes real HTTP requests. So the assertions exercise the shipped access logic.
|
||||
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const express = require("express");
|
||||
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "bliss-plumbing-"));
|
||||
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();
|
||||
|
||||
// ---- scenario ------------------------------------------------------------
|
||||
const alice = model.createUser("alice", "x"); // member of the private structure
|
||||
const bob = model.createUser("bob", "x"); // outsider
|
||||
|
||||
// PRIVATE structure owned by alice: its own route, template, and db.
|
||||
const priv = model.createStructure("private-vault", alice);
|
||||
const privRoute = model.createRoute("GET", "/secret", priv, "handler(){}");
|
||||
const privTemplate = model.createTemplate(priv, "secret_tpl", "top secret", "");
|
||||
const secretsDbId = model.createDb(priv, "secrets");
|
||||
model.setStructurePrivacy(priv, true, alice);
|
||||
|
||||
// PUBLIC structure owned by bob that ALIASES alice's private db under "secrets".
|
||||
const pub = model.createStructure("public-front", bob);
|
||||
model.attachDb(pub, secretsDbId, "secrets");
|
||||
|
||||
// ---- app: real plumbing router, faked session from ?as= ------------------
|
||||
const app = express();
|
||||
app.use((req, _res, next) => {
|
||||
const as = req.query.as;
|
||||
req.session = { userId: as === "alice" ? alice : as === "bob" ? bob : null };
|
||||
next();
|
||||
});
|
||||
app.use("/plumbing", require(path.join(PROJECT, "plumbing.js")));
|
||||
const server = app.listen(0);
|
||||
const port = server.address().port;
|
||||
|
||||
async function get(url) {
|
||||
const res = await fetch(`http://127.0.0.1:${port}${url}`);
|
||||
return res.status;
|
||||
}
|
||||
|
||||
function expect(label, actual, wanted) {
|
||||
const ok = actual === wanted;
|
||||
console.log(` ${ok ? "PASS" : "FAIL"} | ${label} (got ${actual}, want ${wanted})`);
|
||||
return ok;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const results = [];
|
||||
|
||||
console.log("\nid-only endpoints (what the live editor calls):");
|
||||
results.push(expect("alice reads her private route ", await get(`/plumbing/routes/${privRoute}?as=alice`), 200));
|
||||
results.push(expect("bob reads alice's private route ", await get(`/plumbing/routes/${privRoute}?as=bob`), 404));
|
||||
results.push(expect("anon reads alice's private route ", await get(`/plumbing/routes/${privRoute}`), 404));
|
||||
results.push(expect("alice reads her private template ", await get(`/plumbing/templates/${privTemplate}?as=alice`), 200));
|
||||
results.push(expect("bob reads alice's private template", await get(`/plumbing/templates/${privTemplate}?as=bob`), 404));
|
||||
|
||||
console.log("\nstructure-scoped endpoints reject id-laundering:");
|
||||
// bob CAN see his public structure, but the route id belongs to the private one.
|
||||
results.push(expect("bob: pub structure + private routeId", await get(`/plumbing/structures/${pub}/routes/${privRoute}?as=bob`), 404));
|
||||
|
||||
console.log("\naliased private db is not leaked through a public structure:");
|
||||
results.push(expect("bob reads aliased private db ", await get(`/plumbing/structures/${pub}/dbs/${secretsDbId}?as=bob`), 404));
|
||||
results.push(expect("alice reads that db via her structure", await get(`/plumbing/structures/${priv}/dbs/${secretsDbId}?as=alice`), 200));
|
||||
|
||||
const pass = results.every(Boolean);
|
||||
console.log(`\n${pass ? "PROVEN" : "FAILED"}: plumbing gates every artifact against its own structure's access rules.`);
|
||||
|
||||
server.close();
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
process.exit(pass ? 0 : 1);
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue