Sanitize Cytoscape tooltip labels to prevent XSS
Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { useMapPathStore } from '@/stores/mapPathStore';
|
||||
import { getTimeLabel } from '@/module/timeLabel.js';
|
||||
import { createTooltipContent } from '@/module/tooltipContent.js';
|
||||
import { useCytoscapeStore } from '@/stores/cytoscapeStore';
|
||||
import { SAVE_KEY_NAME } from '@/constants/constants.js';
|
||||
|
||||
@@ -271,8 +272,7 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
const node = event.target;
|
||||
let ref = node.popperRef()
|
||||
let dummyDomEle = document.createElement('div');
|
||||
let content = document.createElement('div');
|
||||
content.innerHTML = node.data("label")
|
||||
let content = createTooltipContent(node.data('label'));
|
||||
tip = new tippy(dummyDomEle, { // tippy props:
|
||||
getReferenceClientRect: ref.getBoundingClientRect,
|
||||
trigger: 'manual',
|
||||
|
||||
@@ -13,6 +13,7 @@ import cytoscape from 'cytoscape';
|
||||
import dagre from 'cytoscape-dagre';
|
||||
import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { createTooltipContent } from '@/module/tooltipContent.js';
|
||||
|
||||
cytoscape.use( dagre );
|
||||
|
||||
@@ -95,8 +96,7 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
||||
const node = event.target
|
||||
let ref = node.popperRef()
|
||||
let dummyDomEle = document.createElement('div');
|
||||
let content = document.createElement('div');
|
||||
content.innerHTML = node.data("label")
|
||||
let content = createTooltipContent(node.data('label'));
|
||||
tip = new tippy(dummyDomEle, { // tippy props:
|
||||
getReferenceClientRect: ref.getBoundingClientRect,
|
||||
trigger: 'manual',
|
||||
|
||||
17
src/module/tooltipContent.js
Normal file
17
src/module/tooltipContent.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2026-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// codex@openai.com (Codex), 2026/03/08
|
||||
/** @module tooltipContent Safe tooltip content builder utilities. */
|
||||
|
||||
/**
|
||||
* Creates a tooltip content element with untrusted input rendered as text.
|
||||
*
|
||||
* @param {string} label - The tooltip label text from runtime data.
|
||||
* @returns {HTMLDivElement} A div element with text-only content.
|
||||
*/
|
||||
export function createTooltipContent(label) {
|
||||
const content = document.createElement('div');
|
||||
content.textContent = String(label ?? '');
|
||||
return content;
|
||||
}
|
||||
19
tests/unit/module/tooltipContent.test.js
Normal file
19
tests/unit/module/tooltipContent.test.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// The Lucia project.
|
||||
// Copyright 2026-2026 DSP, inc. All rights reserved.
|
||||
// Authors:
|
||||
// codex@openai.com (Codex), 2026/03/08
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createTooltipContent } from '@/module/tooltipContent.js';
|
||||
|
||||
describe('createTooltipContent', () => {
|
||||
it('renders untrusted label as plain text', () => {
|
||||
const label = '<img src=x onerror=alert(1)>Node';
|
||||
|
||||
const content = createTooltipContent(label);
|
||||
|
||||
expect(content.textContent).toBe(label);
|
||||
expect(content.innerHTML).toContain('<img');
|
||||
expect(content.querySelector('img')).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user