chore: version 2.0.0-alpha.2 + changelog
Search view: read ?q from hash, call /api/search, group hits by kind with rank + space_id; sidebar filters for kinds and space_id; updates on Enter or filter change. Bumps package.json + server.js VERSION to 2.0.0-alpha.2 and pins the /health version assertion to match. CHANGELOG: full Plan 2 entry covering API surface, capability tiering, audit chain extension (approve/reject events), and the SPA shell. Security: adds safeHref() to dom.js and applies it everywhere an API-supplied URL becomes href / src (reference media block + reference source_url anchor + resource url anchor). javascript: and other non-http(s)/mailto schemes from agent-suggested content can no longer execute in the owner's browser. Plan 2 surface is feature-complete: 22/22 tasks landed, 185 tests across 43 files, SPA renders end-to-end including the suggest -> approve agent flow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
// Reference detail: media block + summary + metadata + tag attach/detach + linked-from.
|
||||
import { api } from '../api.js';
|
||||
import { el, mount, clear } from '../dom.js';
|
||||
import { el, mount, clear, safeHref } from '../dom.js';
|
||||
|
||||
function mediaBlock(ref) {
|
||||
if (ref.kind === 'image' && (ref.source_url || ref.blob_path)) {
|
||||
return el('img', { src: ref.source_url || ref.blob_path, style: { maxWidth: '100%', borderRadius: '4px', border: '1px solid var(--border)' } });
|
||||
const src = safeHref(ref.source_url || ref.blob_path);
|
||||
if (src === '#') return el('span', { class: 'muted' }, '(image url rejected by scheme check)');
|
||||
return el('img', { src, style: { maxWidth: '100%', borderRadius: '4px', border: '1px solid var(--border)' } });
|
||||
}
|
||||
if (ref.kind === 'video' && ref.source_url) {
|
||||
const yt = ref.source_url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]{6,})/);
|
||||
@@ -16,10 +18,10 @@ function mediaBlock(ref) {
|
||||
referrerpolicy: 'no-referrer'
|
||||
});
|
||||
}
|
||||
return el('a', { href: ref.source_url, target: '_blank', rel: 'noopener noreferrer' }, ref.source_url);
|
||||
return el('a', { href: safeHref(ref.source_url), target: '_blank', rel: 'noopener noreferrer' }, ref.source_url);
|
||||
}
|
||||
if (ref.source_url) {
|
||||
return el('a', { href: ref.source_url, target: '_blank', rel: 'noopener noreferrer' }, ref.source_url);
|
||||
return el('a', { href: safeHref(ref.source_url), target: '_blank', rel: 'noopener noreferrer' }, ref.source_url);
|
||||
}
|
||||
return el('span', { class: 'muted' }, '(no source)');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user