Answer prose round 1: 0 🔴, 1 🟡 (0 misread); the secrets checklist reads in one pass, and four comments shrink or say granted

This commit is contained in:
2026-11-03 21:46:57 +01:00
parent 69f0371b89
commit a3b8632d5c
6 changed files with 12 additions and 15 deletions
-2
View File
@@ -28,8 +28,6 @@ export function identityPayload(email: string, password: string) {
};
}
// Coarse-permission grant: `Permission:<permission>#granted@user:<id>`. Subject ids are `user:<kratos-id>`
// (namespaces.keto.ts) — the source of truth the login flow projects into the JWT permissions.
export function permissionTuple(userId: string, permission: string) {
return { namespace: "Permission", object: permission, relation: "granted", subject_id: `user:${userId}` };
}
+1 -1
View File
@@ -39,7 +39,7 @@ export interface CompletedLogin {
// The coarse permissions a user holds — directly (`Permission:<name>#granted@user:<id>`) or transitively via a
// group granted the permission. Enumerates the defined permissions (the distinct objects in the Permission
// namespace) and asks Keto to resolve each membership, so a permission granted to a group reaches the JWT —
// namespace) and asks Keto to check each grant, so a permission granted to a group reaches the JWT —
// matching the OPL model and the admin "Effective access" view. At login/refresh only, never per
// request; permission count is small, so the per-permission checks are cheap and run in parallel.
export async function readPermissions(keto: KetoClient, userId: string): Promise<string[]> {
+2 -2
View File
@@ -117,8 +117,8 @@ function flowPage(kratos: KratosPublic, flowType: FlowType, secureCookies: boole
// A code flow's POST (flow-view.ts → CODE_FLOWS). Trims the pasted one-time code, since Kratos
// refuses one with whitespace around it, then passes the form on to Kratos and sends the browser
// where Kratos redirects. Kratos' csrf_token field guards the POST, so the host's own CSRF token
// isn't checked here. `submitFlow` throws only on a 5xx or an unreachable Kratos, so of
// `flowFailure`'s branches only the 503 fires here.
// isn't checked here. `submitFlow` throws only on a 5xx or an unreachable Kratos, so only
// `flowFailure`'s 503 branch can fire here.
function codeFlowSubmit(kratos: KratosPublic, flowType: FlowType, secureCookies: boolean): BuiltinRoute["handler"] {
return async (ctx: RequestContext): Promise<RouteResult> => {
const pathname = ctx.url.pathname;
+2 -3
View File
@@ -139,9 +139,8 @@ test("findConflicts: each single slot (`home`/`dashboard`) may have one owner
assert.deepEqual(findConflicts([p({ id: "a", dashboard: handler, home: handler }), p({ id: "b" })]).filter((c) => c.kind === "home" || c.kind === "dashboard"), []);
});
// Drift guard: RESERVED_PLUGIN_IDS is a hand-maintained mirror of the host's own top-level mounts —
// a folder claiming one would silently shadow a built-in route. Only the auth flows derive from
// AUTH_FLOWS; every other mount is listed by hand, so a new top-level route needs a line here too.
// Drift guard: every mount but the auth flows is listed by hand, so a new top-level built-in route
// needs a line here too.
test("RESERVED_PLUGIN_IDS covers every built-in top-level mount; `home` (the / field) and `admin` (a plugin) are NOT reserved", () => {
const seg = (path: string): string => path.split("/")[1] ?? ""; // first segment of "/x/y"
const builtins = new Set<string>([