mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(html-view): a ~/ HTML link renders as a page too (#547/#604 integration)
#604 makes `~/` links clickable and passes them through unchanged, since only the server knows the session host's home. The html-view mint read `~/x.html` as workspace-relative and answered 404, so the preview fell back to the source view. It now expands `~/` against this host's home, as the attachment route does, and refuses it in a Docker case (that `~` is the container's) with the same message as the attachment and folder routes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1172ef56f8
commit
5e83cd3885
4 files changed
+38
-8
No files matched your search
@@ -580,7 +580,7 @@ interface SessionFileScope {
|
||||
* host's home would open a different file than the one the agent printed, so
|
||||
* the form is refused instead of guessed.
|
||||
*/
|
||||
const DOCKER_HOME_PATH_ERROR =
|
||||
export const DOCKER_HOME_PATH_ERROR =
|
||||
'A ~/ path inside a Docker case is the container home, which this server cannot read; use the absolute path';
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,13 +29,13 @@ import type { FastifyInstance } from 'fastify';
|
||||
import { realpathSync } from 'node:fs';
|
||||
import fs from 'node:fs/promises';
|
||||
import { homedir, tmpdir } from 'node:os';
|
||||
import { basename, dirname, extname, isAbsolute, relative, resolve, sep } from 'node:path';
|
||||
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
||||
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, parseBody, validateSessionFilePath } from '../route-helpers.js';
|
||||
import { HtmlViewOpenSchema } from '../schemas.js';
|
||||
import { localFileSource, sendFileBody } from './file-routes.js';
|
||||
import { DOCKER_HOME_PATH_ERROR, localFileSource, sendFileBody } from './file-routes.js';
|
||||
import { getUserSpacesDir } from '../../config/multiuser.js';
|
||||
import { exceedsDownloadLimit } from '../../config/buffer-limits.js';
|
||||
import type { SessionPort } from '../ports/index.js';
|
||||
@@ -160,20 +160,31 @@ export function registerHtmlViewRoutes(app: FastifyInstance, ctx: SessionPort):
|
||||
reply.code(400).send(createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Not an HTML file'));
|
||||
return;
|
||||
}
|
||||
// A `~/` link (the terminal passes a home-relative path through, since only
|
||||
// the server knows the home) is this host's home, as the attachment route
|
||||
// expands it. A Docker case's `~` is the container's, so it is refused there.
|
||||
let requestedPath = body.path;
|
||||
if (requestedPath.startsWith('~/')) {
|
||||
if (session.docker) {
|
||||
reply.code(400).send(createErrorResponse(ApiErrorCode.INVALID_INPUT, DOCKER_HOME_PATH_ERROR));
|
||||
return;
|
||||
}
|
||||
requestedPath = join(homedir(), requestedPath.slice(2));
|
||||
}
|
||||
|
||||
// Inside the workspace: the same check every file route uses. Outside it: the
|
||||
// attachment guard, exactly as an external file preview is admitted.
|
||||
let resolvedPath: string;
|
||||
const inWorkspace = validateSessionFilePath(session.workingDir, body.path);
|
||||
const inWorkspace = validateSessionFilePath(session.workingDir, requestedPath);
|
||||
if (inWorkspace) {
|
||||
resolvedPath = inWorkspace.resolvedPath;
|
||||
} else {
|
||||
if (!isAbsolute(body.path)) {
|
||||
if (!isAbsolute(requestedPath)) {
|
||||
reply.code(404).send(createErrorResponse(ApiErrorCode.NOT_FOUND, 'File not found'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolvedPath = await fs.realpath(body.path);
|
||||
resolvedPath = await fs.realpath(requestedPath);
|
||||
} catch {
|
||||
reply.code(404).send(createErrorResponse(ApiErrorCode.NOT_FOUND, 'File not found'));
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user