fix(security): safe links and security headers (FLAW-28, FLAW-29)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m48s

safeHref lets only http, https and mailto become links (Markdown viewer,
outliner, legal links); javascript: and friends show as text. A
middleware sets a Content-Security-Policy (scripts only from MindSift,
no framing, connections only to this server), X-Frame-Options,
nosniff, Referrer-Policy and a Permissions-Policy. The two inline
scripts of index.html moved to files so script-src stays 'self'.

The second inline script also deleted every cache on each page load,
which undermined the offline start; it is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 19:02:13 +01:00
1 parent 3ca4ff7bce
commit b2ecda9896
13 files changed
+175 -28

No files matched your search

@@ -25,6 +25,7 @@ import { DocumentBlock, BlockType } from '../../lib/types';
import { LinkModal } from './LinkModal';
import { renderInlineMarkdown } from '../../lib/markdown';
import { api } from '../../lib/api';
import { safeHref } from '../../lib/safeUrl';
export interface SlashCommand {
type: BlockType | 'link';
@@ -1001,9 +1002,9 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
);
}
const mdMatch = part.match(/^\[(.*?)\]\((https?:\/\/.*?)\)$/);
if (mdMatch) {
if (mdMatch && safeHref(mdMatch[2])) {
const label = mdMatch[1];
const url = mdMatch[2];
const url = safeHref(mdMatch[2]) as string;
return (
<a
key={idx}
@@ -1018,11 +1019,11 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
</a>
);
}
if (/^https?:\/\//.test(part)) {
if (/^https?:\/\//.test(part) && safeHref(part)) {
return (
<a
key={idx}
href={part}
href={safeHref(part) as string}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] font-medium text-status-ok bg-status-ok/10 hover:bg-status-ok/20 rounded-btn transition-colors"