Merge #797: the File Viewer edits workspace dotenv files

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-11 20:23:25 +01:00
commit 18b309fc02
13 files changed
+417 -34

No files matched your search

+8 -2
View File
@@ -126,9 +126,15 @@ export async function loadAttachmentGuardConfig(): Promise<AttachmentGuardConfig
*
* NOTE: this is intentionally a SUPERSET of `isSensitivePath` so `/api/download`
* behavior is NOT changed — only attachment registration/serving uses this.
* `opts` passes through to `isSensitivePath` (the File Viewer editor's
* `allowDotenv`).
*/
export function isBlockedAttachmentPath(absPath: string, blockedTrees: readonly string[]): boolean {
if (isSensitivePath(absPath)) return true;
export function isBlockedAttachmentPath(
absPath: string,
blockedTrees: readonly string[],
opts: { allowDotenv?: boolean } = {}
): boolean {
if (isSensitivePath(absPath, opts)) return true;
return blockedTrees.some((tree) => isUnderTree(absPath, tree));
}
+11 -3
View File
@@ -8,9 +8,13 @@
*
* Design (docs/file-viewer-edit-plan.md):
* - ALLOWLIST of text extensions/basenames, not a blocklist — matching the
* attachment-guard precedent. `svg` and `env` are deliberately absent: svg is
* treated as untrusted on the read side, and `.env` is sensitive-path blocked
* anyway; excluding them here keeps a single obvious refusal.
* attachment-guard precedent. `svg` and `env` are deliberately absent from
* the extension list: svg is treated as untrusted on the read side, and the
* list doubles as the attachment list, which must not gain `*.env`. Dotenv
* files (`.env`, `.env.*`, `*.env`) are editable by NAME in
* isEditableFileName instead: the plain preview already shows them, and the
* routes pass `allowDotenv` to the sensitive-path check, which keeps every
* other credential pattern.
* - The `.git/` subtree is denied outright: `.git/hooks/*` is code execution and
* a corrupted index looks unrecoverable to a user who wanted to fix a typo.
* - EOL helpers exist because a browser <textarea> normalizes to LF; the server
@@ -116,6 +120,10 @@ export const EDITABLE_BASENAMES: ReadonlySet<string> = new Set([
export function isEditableFileName(fileName: string): boolean {
const lower = fileName.toLowerCase();
if (EDITABLE_BASENAMES.has(lower)) return true;
// Dotenv in every spelling, by name: `.env.local`'s extension is `local`, and
// `env` must stay off EDITABLE_EXTENSIONS, which is also the cross-workspace
// attachment list (TEXT_ATTACHMENT_EXTENSIONS) that never accepted `*.env`.
if (lower.endsWith('.env') || lower.startsWith('.env.')) return true;
const dot = lower.lastIndexOf('.');
// No extension (or a bare dotfile like `.bashrc`): only the basename list applies.
if (dot <= 0) return false;