fix: file-link and session-sidebar review follow-ups from 1.19.0

Five post-merge review items from PRs #306 (clickable file paths) and
#307 (session sidebar):

- constants.js FILE_PREVIEW_EXTENSIONS gains the media extensions it was
  missing vs the single-source sets in attachment-registry.ts (m4v ogv
  ogg oga m4a aac flac opus), so an in-workspace .m4a opens the preview
  player instead of the log viewer; new test/media-extension-parity.test.ts
  pins all three copies (constants.js, panels-ui.js, attachment-registry.ts)
  against each other.
- FILE_PATH_LINK_PATTERN drops `etc` from its root alternation: /etc is
  unconditionally in DEFAULT_BLOCKED_TREES, so every /etc link 403'd.
  Negative cases added to the link-provider and response-viewer tests.
- updateSidebarCount() counts the rows actually on the sidebar list
  (session rows + web-tab rows, minus filtered-out ones) instead of
  this.sessions.size, and applySidebarFilter() refreshes it so the count
  follows the filter box per keystroke.
- The incremental-render connection-line gate now also fires in sidebar
  layout (this._lineageEdgeCount is permanently 0 there), matching the
  strip-scroll listener widened in #307, so a badge changing row heights
  redraws subagent/ultracode connectors.
- isSensitivePath() blocks ~/.claude.json, ~/.claude/settings.json and
  ~/.claude/settings.local.json (credential-bearing by schema), anchored
  to homedir() read at check time so case-level .claude/settings*.json
  files stay servable in the File Viewer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-16 20:34:34 +02:00
parent f07905b193
commit 24ed43935c
9 changed files with 209 additions and 13 deletions
+31
View File
@@ -16,6 +16,7 @@
* feature), so the "stays attachable" cases matter just as much: over-blocking
* breaks the publish skill and the review-card loop.
*/
import { homedir } from 'node:os';
import { describe, expect, it } from 'vitest';
import { isSensitivePath } from '../src/web/sensitive-path.js';
@@ -122,4 +123,34 @@ describe('isSensitivePath', () => {
expect(isSensitivePath('/srv/app/looks-innocent')).toBe(false);
expect(isSensitivePath(`${HOME}/.ssh/looks-innocent`)).toBe(true);
});
describe('home-anchored Claude config (credential-bearing by schema)', () => {
// ~/.claude/settings.json can hold `env: {ANTHROPIC_API_KEY}` and
// `apiKeyHelper` by schema (settings.local.json shares it), and
// ~/.claude.json holds account/OAuth-adjacent state. These are anchored to
// the REAL homedir, read at CHECK time — test/setup.ts points HOME at a
// per-file fixture, so a homedir() captured at module load would be a
// different directory than the one this suite resolves.
const home = homedir();
it.each([
['claude account state', `${home}/.claude.json`],
['claude user settings', `${home}/.claude/settings.json`],
['claude user local settings', `${home}/.claude/settings.local.json`],
])('blocks the %s', (_label, path) => {
expect(isSensitivePath(path)).toBe(true);
});
// A blanket `/\.claude\/settings\.json$/` would also catch every CASE-level
// settings file, which users legitimately view and edit in the File Viewer
// (model override, hooks) — the home anchor is what keeps those servable.
it.each([
['a case-level .claude/settings.json', '/srv/app/.claude/settings.json'],
['a case-level .claude/settings.local.json', '/srv/app/.claude/settings.local.json'],
['a .claude/settings.json under some OTHER home', `${HOME}/.claude/settings.json`],
['a .claude.json under some OTHER home', `${HOME}/.claude.json`],
])('keeps %s servable', (_label, path) => {
expect(isSensitivePath(path)).toBe(false);
});
});
});