security improvements
This commit is contained in:
@@ -13,7 +13,7 @@ import HelpPanelManager from './HelpPanelManager';
|
||||
import ContextMenu from './ContextMenu';
|
||||
import * as api from './api';
|
||||
import { pickNativeDirectorySelection, pickNativeFileSelection } from './nativePicker';
|
||||
import { embedWorkflow, extractWorkflow } from './pngMetadata';
|
||||
import { embedWorkflow, extractWorkflow, sanitizeJson } from './pngMetadata';
|
||||
import { captureViewportBlob as captureWorkflowViewportBlob } from './workflowCapture';
|
||||
import tonoIconUrl from '../../resources/icon_1024.png';
|
||||
import { hydrateWorkflowState } from './workflowHydration';
|
||||
@@ -1708,7 +1708,7 @@ function Flow() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data = JSON.parse(await file.text());
|
||||
data = sanitizeJson(JSON.parse(await file.text()));
|
||||
}
|
||||
await applyMaybePackedWorkflow(data);
|
||||
setStatus({ text: 'Workflow loaded.', level: 'info' });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
// Open external links in new tabs
|
||||
const renderer = new marked.Renderer();
|
||||
@@ -172,7 +173,7 @@ function HelpContent({ content, onOpenDoc }: HelpContentProps) {
|
||||
const headings = useMemo(() => parseHeadings(md), [md]);
|
||||
const html = useMemo(() => {
|
||||
let rendered: string;
|
||||
try { rendered = marked.parse(md) as string; } catch { rendered = md; }
|
||||
try { rendered = DOMPurify.sanitize(marked.parse(md) as string); } catch { rendered = md; }
|
||||
return injectHeadingIds(rendered, headings);
|
||||
}, [md, headings]);
|
||||
|
||||
@@ -207,7 +208,7 @@ function JournalTab({ content, onChange, onOpenDoc }: JournalTabProps) {
|
||||
let headings: Heading[] = [];
|
||||
if (!isEditing && content?.trim()) {
|
||||
headings = parseHeadings(content);
|
||||
try { renderedHtml = injectHeadingIds(marked.parse(content) as string, headings); } catch { renderedHtml = content; }
|
||||
try { renderedHtml = injectHeadingIds(DOMPurify.sanitize(marked.parse(content) as string), headings); } catch { renderedHtml = content; }
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useContext, useRef, useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { NodeResizeControl, useStore } from '@xyflow/react';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { NodeContext } from './CustomNode';
|
||||
import type { NodeContextValue } from './types';
|
||||
|
||||
@@ -81,7 +82,7 @@ function TextNoteNode({ id, data }: TextNoteNodeProps) {
|
||||
|
||||
const renderedHtml = useMemo(() => {
|
||||
if (!text.trim()) return '';
|
||||
return marked.parse(text);
|
||||
return DOMPurify.sanitize(marked.parse(text) as string);
|
||||
}, [text]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -90,7 +90,24 @@ function parseTextChunk(type: string, chunkData: Uint8Array) {
|
||||
const translatedEnd = chunkData.indexOf(0, offset);
|
||||
if (translatedEnd === -1) return null;
|
||||
|
||||
return JSON.parse(decoder.decode(chunkData.subarray(translatedEnd + 1)));
|
||||
return sanitizeJson(JSON.parse(decoder.decode(chunkData.subarray(translatedEnd + 1))));
|
||||
}
|
||||
|
||||
// ── JSON sanitisation ────────────────────────────────────────────────
|
||||
|
||||
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
||||
|
||||
/**
|
||||
* Recursively strip keys that could cause prototype pollution.
|
||||
*/
|
||||
export function sanitizeJson(value: unknown): unknown {
|
||||
if (value === null || typeof value !== 'object') return value;
|
||||
if (Array.isArray(value)) return value.map(sanitizeJson);
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
||||
if (!DANGEROUS_KEYS.has(k)) result[k] = sanitizeJson(v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Public API ───────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user