mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-20 09:09:35 +02:00
refactor(html-view): validate the mint body with a Zod schema
POST /api/sessions/:id/html-view read `path` off an unchecked cast. It now goes through HtmlViewOpenSchema in schemas.ts (a non-empty string of at most 4096 characters without NUL or line breaks, no other keys) via parseBody, like the other file routes, and keeps its "Missing path parameter" message for an absent path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
69aa5724e7
commit
432faf7254
3 files changed
+32
-7
No files matched your search
@@ -32,7 +32,8 @@ import { basename, dirname, extname, isAbsolute, relative, resolve, sep } from '
|
||||
import { ApiErrorCode, createErrorResponse } from '../../types.js';
|
||||
import { isBlockedAttachmentPath, isUnderTree, loadAttachmentGuardConfig } from '../../config/attachment-guard.js';
|
||||
import { HTML_VIEW_PREFIX, htmlViewCapabilities } from '../../html-view-capabilities.js';
|
||||
import { CASES_DIR, findSessionOrFail, ownerFor, validateSessionFilePath } from '../route-helpers.js';
|
||||
import { CASES_DIR, findSessionOrFail, ownerFor, parseBody, validateSessionFilePath } from '../route-helpers.js';
|
||||
import { HtmlViewOpenSchema } from '../schemas.js';
|
||||
import { getUserSpacesDir } from '../../config/multiuser.js';
|
||||
import { exceedsDownloadLimit } from '../../config/buffer-limits.js';
|
||||
import type { SessionPort } from '../ports/index.js';
|
||||
@@ -133,12 +134,7 @@ export function registerHtmlViewRoutes(app: FastifyInstance, ctx: SessionPort):
|
||||
app.post('/api/sessions/:id/html-view', async (req, reply) => {
|
||||
const { id } = req.params as { id: string };
|
||||
const session = findSessionOrFail(ctx, id, req);
|
||||
const body = (req.body || {}) as { path?: string };
|
||||
|
||||
if (!body.path || typeof body.path !== 'string') {
|
||||
reply.code(400).send(createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Missing path parameter'));
|
||||
return;
|
||||
}
|
||||
const body = parseBody(HtmlViewOpenSchema, req.body ?? {});
|
||||
if (session.remote) {
|
||||
reply
|
||||
.code(400)
|
||||
|
||||
@@ -127,6 +127,24 @@ export const FileWriteSchema = z
|
||||
})
|
||||
.strict();
|
||||
|
||||
/**
|
||||
* Body validation for `POST /api/sessions/:id/html-view` (render a local HTML file
|
||||
* as a sandboxed page). `path` is absolute or relative to the session workspace;
|
||||
* the extension, containment, guard and broad-root checks are the route's
|
||||
* (html-view-routes.ts), after parsing.
|
||||
*/
|
||||
export const HtmlViewOpenSchema = z
|
||||
.object({
|
||||
path: z
|
||||
.string({ error: 'Missing path parameter' })
|
||||
.min(1, 'Missing path parameter')
|
||||
.max(4096)
|
||||
.refine((p) => !p.includes('\0') && !p.includes('\n') && !p.includes('\r'), {
|
||||
message: 'Invalid path',
|
||||
}),
|
||||
})
|
||||
.strict();
|
||||
|
||||
/**
|
||||
* The run-mode ids the API currently accepts: every ENABLED registry entry.
|
||||
*
|
||||
|
||||
@@ -64,6 +64,17 @@ describe('html-view routes', () => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
});
|
||||
|
||||
it('validates the body', async () => {
|
||||
const post = (payload: unknown) =>
|
||||
harness.app.inject({ method: 'POST', url: `/api/sessions/${sid}/html-view`, payload: payload as object });
|
||||
for (const payload of [{}, { path: '' }, { path: 42 }, { path: 'a\nb.html' }, { path: 'index.html', extra: 1 }]) {
|
||||
const res = await post(payload);
|
||||
expect(res.statusCode, JSON.stringify(payload)).toBe(400);
|
||||
expect(JSON.parse(res.body).success).toBe(false);
|
||||
}
|
||||
expect(JSON.parse((await post({})).body).error).toBe('Missing path parameter');
|
||||
});
|
||||
|
||||
it('refuses non-HTML files and missing files', async () => {
|
||||
expect((await open(join(workspace, 'data.json'))).statusCode).toBe(400);
|
||||
expect((await open(join(workspace, 'missing.html'))).statusCode).toBe(404);
|
||||
|
||||
Reference in new issue
Block a user