mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 20:49:41 +02:00
Both picker endpoints are a second file-serving surface, and they inherited neither the attachment guard's confinement nor its ownership scoping. Two separate holes: 1. `sessionId` contributes that session's workingDir as a browse root, but it was resolved straight off ctx.sessions/ctx.store with no owner check, unlike the nine other session-scoped handlers in this file. A non-admin could pin ANOTHER user's working directory as a root just by passing their session id, then list and preview underneath it. Now runs canAccessOwned and reports 404, which also avoids confirming that a session id exists. 2. `Home` and `CASES_DIR` were unconditional roots for every caller. Per-user spaces live at <USER_SPACES_DIR>/<username>, which is INSIDE homedir(), so the Home root alone exposed every other user's workspace. A multi-user non-admin now gets only their own userSpacePath plus anything explicitly listed in CODEMAN_FILE_PICKER_ROOTS. /mnt/d is dropped as well: a broad host mount should be an explicit operator decision in a multi-user deployment, and operators who want it can name it in that env var. Admins and single-user mode keep the host-wide roots, so behavior is unchanged unless CODEMAN_MULTIUSER is on (opt-in, off by default). All three discriminating tests were verified to fail against the previous code: browse and preview both returned 200 instead of 404, and the roots came back as [Home, Codeman Cases, ...] instead of [My Space]. Full suite green, 3784 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
55 lines
2.1 KiB
TypeScript
55 lines
2.1 KiB
TypeScript
/**
|
|
* Shared utilities for route testing.
|
|
*
|
|
* Creates minimal Fastify instances with just the route module under test
|
|
* and a mock context. Uses app.inject() for HTTP testing without real ports.
|
|
*/
|
|
import Fastify, { type FastifyInstance } from 'fastify';
|
|
import fastifyCookie from '@fastify/cookie';
|
|
import { createMockRouteContext, type MockRouteContext } from '../mocks/index.js';
|
|
import { installRouteErrorHandler } from '../../src/web/route-error-handler.js';
|
|
|
|
export interface RouteTestHarness {
|
|
app: FastifyInstance;
|
|
ctx: MockRouteContext;
|
|
}
|
|
|
|
/**
|
|
* Creates a Fastify instance with a route module registered against a mock context.
|
|
*
|
|
* @param registerFn - The route registration function (e.g., registerSessionRoutes).
|
|
* Uses `any` for ctx parameter because route functions expect typed port intersections
|
|
* that MockRouteContext satisfies structurally but not nominally.
|
|
* @param ctxOptions - Optional overrides for the mock context. `authUser` stands
|
|
* in for what the auth middleware would attach in multi-user mode; without it
|
|
* `getAuthUser()` falls back to a synthetic admin, which passes every
|
|
* ownership check and would make a scoping test pass vacuously.
|
|
*/
|
|
export async function createRouteTestHarness(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
registerFn: (app: FastifyInstance, ctx: any) => void,
|
|
ctxOptions?: { sessionId?: string; authUser?: { username: string; role: 'admin' | 'user' } }
|
|
): Promise<RouteTestHarness> {
|
|
const app = Fastify({ logger: false });
|
|
|
|
// Register cookie plugin — some routes access req.cookies
|
|
await app.register(fastifyCookie);
|
|
|
|
if (ctxOptions?.authUser) {
|
|
const authUser = ctxOptions.authUser;
|
|
app.addHook('onRequest', async (req) => {
|
|
(req as unknown as { authUser: typeof authUser }).authUser = authUser;
|
|
});
|
|
}
|
|
|
|
const ctx = createMockRouteContext(ctxOptions);
|
|
|
|
registerFn(app, ctx);
|
|
// Mirror production: structured errors thrown by route helpers (findSessionOrFail,
|
|
// parseBody) are rendered to {success:false} bodies at the right status.
|
|
installRouteErrorHandler(app);
|
|
await app.ready();
|
|
|
|
return { app, ctx };
|
|
}
|