mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 00:49:41 +02:00
COD-37 add server-side attachment pipeline (registry, magic-link, path guard)
Adds the foundation for serving local files to the browser as live external attachments with a stable id, so requests never carry arbitrary absolute paths. - attachment-registry: in-memory, session-scoped registry. registerExternalAttachment validates an absolute path, resolves symlinks, enforces the path guard, and mints an `att_<uuid>` id; records are cleared when the session is removed. - attachment path guard: a configurable blocklist (secret locations + /root,/etc trees, extendable via attachmentBlockedPaths / CODEMAN_ATTACHMENT_BLOCKED_PATHS) plus an optional, default-off workspace-confinement mode. Shares one sensitive-path blocklist (web/sensitive-path.ts) with /api/download, which is refactored to use the extracted module instead of an inline copy. - terminal magic links: the session scans output for codeman://attach?path=... and emits `attachmentRequested`; the web server registers the file and broadcasts an `attachment:detected` SSE event. `codeman attach <path>` (CLI) prints the magic link or POSTs directly when a session id is known. - image watcher: detects png/pdf/docx/pptx dropped into a session's working dir and emits `attachment:detected`. - routes: POST /api/sessions/:id/attachments (register) and GET /api/sessions/:id/attachments/:attachmentId/raw (serve), both re-checking the guard before streaming. Document previews/thumbnails and the attachment-history drawer build on this foundation and land separately. Verified: tsc --noEmit, lint, format, frontend-syntax, full test:ci (2846 passed), and a server boot smoke (/api/status 200).
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @fileoverview Parses terminal magic links that request attachment cards.
|
||||
*/
|
||||
|
||||
import { isAbsolute } from 'node:path';
|
||||
import { isSupportedAttachmentExtension } from './attachment-registry.js';
|
||||
|
||||
const MAGIC_LINK_RE = /codeman:\/\/attach\?([^\s<>"']+)/g;
|
||||
|
||||
export function parseAttachmentMagicLinks(data: string): string[] {
|
||||
const results: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const match of data.matchAll(MAGIC_LINK_RE)) {
|
||||
const query = trimTrailingPunctuation(match[1] || '');
|
||||
try {
|
||||
const params = new URLSearchParams(query);
|
||||
const filePath = params.get('path');
|
||||
if (!filePath || !isAbsolute(filePath)) continue;
|
||||
const extension = filePath.split('.').pop()?.toLowerCase() || '';
|
||||
if (!isSupportedAttachmentExtension(extension)) continue;
|
||||
if (seen.has(filePath)) continue;
|
||||
seen.add(filePath);
|
||||
results.push(filePath);
|
||||
} catch {
|
||||
// Ignore malformed terminal text. Magic links are advisory.
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function trimTrailingPunctuation(value: string): string {
|
||||
return value.replace(/[),.;:]+$/g, '');
|
||||
}
|
||||
Reference in New Issue
Block a user