mirror of
https://github.com/penpot/penpot.git
synced 2026-09-23 20:36:15 +00:00
* ✨ Enable closed schemas for RPC methods * 🐛 Fix duplicate make-dummy-request test helper definition The branch added a variadic DummyRequest/make-dummy-request pair but left the pre-existing single-arg definition in place. Because it was loaded last, zero-arg (make-dummy-request) calls added by prepare-rpc-params and rpc-nitrate-test threw ArityException, which broke 384 tests and caused 14 downstream assertion failures. Remove the stale duplicate so the variadic definition is the only one, and drop the now-unused yrq alias and duplicate yres alias. AI-assisted-by: deepseek-v4.1-flash * ✨ Add focused tests for make-dummy-request helper Pin the call contract of make-dummy-request, which the suite uses in three styles: no arguments, a single options map, and keyword arguments. The helper's redefinition shadowing in 8ca95adb98 was only caught by a full-suite run with hundreds of unrelated errors; these tests fail locally in a focused --focus run. Cover the zero-arg defaults, map and keyword overrides, the :body-bytes -> ByteArrayInputStream wrapping, :body-stream precedence, and cookie readback. Also clarify the docstring to list all supported call styles. AI-assisted-by: deepseek-v4.1-flash * 🚑 Prevent RPC client params from overriding auth context Strip qualified keys from decoded request params before merging them with the server-built auth context, so transit bodies can no longer override ::profile-id, ::auth-type or ::token-perms. Adds a regression test proving the override and the fix. AI-assisted-by: muse-spark-1.3-contributor * 📚 Merge backend subtleties memories under generic name Rename rpc-db-worker-subtleties to subtleties and fold in http-storage-filedata-subtleties, so the name no longer enumerates topics. Update all mem: references accordingly. AI-assisted-by: muse-spark-1.3-contributor * ✨ Add realistic tests for RPC auth override Cover the transit wire vector and the real wrapped :get-profile method with two database profiles, proving a session cannot read another profile by smuggling :app.rpc/profile-id in the body. AI-assisted-by: muse-spark-1.3-contributor * ✨ Add e2e test for RPC auth context override Parametrize rpcPost with contentType, accept and query so e2e can send hand-written transit bodies without new dependencies. The new test proves a transit-smuggled :app.rpc/profile-id no longer overrides the session in get-profile. Also fix the demo email assertion in auth-flow to the current uuid format. AI-assisted-by: muse-spark-1.3-contributor
48 lines
1.8 KiB
JavaScript
48 lines
1.8 KiB
JavaScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { setupTestProfile } from "./helpers/auth.mjs";
|
|
import { rpcPost } from "./helpers/client.mjs";
|
|
|
|
describe("RPC auth context override", () => {
|
|
it("transit body cannot override server profile-id (get-profile)", async () => {
|
|
// ATTACK SCENARIO:
|
|
// 1. Attacker A logs in and gets a session cookie (server binds
|
|
// profile-id A to the request).
|
|
// 2. A POSTs a transit body smuggling the namespaced key
|
|
// :app.rpc/profile-id with victim B's id. Transit is a
|
|
// superset of JSON, so the body is hand-written, no client
|
|
// library needed: {"~:app.rpc/profile-id": "~u<uuid>"}.
|
|
// 3. get-profile only reads ::rpc/profile-id and its schema
|
|
// ([:map]) accepts anything.
|
|
//
|
|
// EXPECTED BEHAVIOR AFTER FIX: the response holds A's profile.
|
|
// Before the fix it held B's profile (id + email leak).
|
|
const attacker = await setupTestProfile();
|
|
const victim = await setupTestProfile();
|
|
|
|
const transitBody =
|
|
`{"~:app.rpc/profile-id": "~u${victim.profile.id}"}`;
|
|
|
|
const res = await rpcPost("get-profile", transitBody, {
|
|
cookieToken: attacker.cookie,
|
|
contentType: "application/transit+json",
|
|
accept: "application/transit+json",
|
|
query: "transit_verbose=1",
|
|
});
|
|
|
|
assert.equal(res.status, 200);
|
|
// Verbose transit map, dependency-free parse:
|
|
// {"~:id":"~u<uuid>","~:email":"...",...}
|
|
const data = JSON.parse(res.body);
|
|
assert.equal(
|
|
data["~:id"],
|
|
`~u${attacker.profile.id}`,
|
|
"profile must come from the session, not the request body"
|
|
);
|
|
assert.ok(
|
|
!res.body.includes(victim.profile.id),
|
|
"victim id must not leak into the response"
|
|
);
|
|
});
|
|
});
|