mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-10 01:29:49 +02:00
Merge branch 'develop' into standardized-pie-definitions
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import { sanitizeText, removeScript, parseGenericTypes } from './common.js';
|
import { sanitizeText, removeScript, parseGenericTypes } from './common.js';
|
||||||
|
|
||||||
describe('when securityLevel is antiscript, all script must be removed', function () {
|
describe('when securityLevel is antiscript, all script must be removed', () => {
|
||||||
/**
|
/**
|
||||||
* @param {string} original The original text
|
* @param original - The original text
|
||||||
* @param {string} result The expected sanitized text
|
* @param result - The expected sanitized text
|
||||||
*/
|
*/
|
||||||
function compareRemoveScript(original, result) {
|
function compareRemoveScript(original: string, result: string) {
|
||||||
expect(removeScript(original).trim()).toEqual(result);
|
expect(removeScript(original).trim()).toEqual(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should remove all script block, script inline.', function () {
|
it('should remove all script block, script inline.', () => {
|
||||||
const labelString = `1
|
const labelString = `1
|
||||||
Act1: Hello 1<script src="http://abc.com/script1.js"></script>1
|
Act1: Hello 1<script src="http://abc.com/script1.js"></script>1
|
||||||
<b>Act2</b>:
|
<b>Act2</b>:
|
||||||
@@ -25,7 +25,7 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
|||||||
compareRemoveScript(labelString, exactlyString);
|
compareRemoveScript(labelString, exactlyString);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove all javascript urls', function () {
|
it('should remove all javascript urls', () => {
|
||||||
compareRemoveScript(
|
compareRemoveScript(
|
||||||
`This is a <a href="javascript:runHijackingScript();">clean link</a> + <a href="javascript:runHijackingScript();">clean link</a>
|
`This is a <a href="javascript:runHijackingScript();">clean link</a> + <a href="javascript:runHijackingScript();">clean link</a>
|
||||||
and <a href="javascript:bipassedMining();">me too</a>`,
|
and <a href="javascript:bipassedMining();">me too</a>`,
|
||||||
@@ -34,11 +34,11 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect malicious images', function () {
|
it('should detect malicious images', () => {
|
||||||
compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`);
|
compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect iframes', function () {
|
it('should detect iframes', () => {
|
||||||
compareRemoveScript(
|
compareRemoveScript(
|
||||||
`<iframe src="http://abc.com/script1.js"></iframe>
|
`<iframe src="http://abc.com/script1.js"></iframe>
|
||||||
<iframe src="http://example.com/iframeexample"></iframe>`,
|
<iframe src="http://example.com/iframeexample"></iframe>`,
|
||||||
@@ -47,8 +47,8 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Sanitize text', function () {
|
describe('Sanitize text', () => {
|
||||||
it('should remove script tag', function () {
|
it('should remove script tag', () => {
|
||||||
const maliciousStr = 'javajavascript:script:alert(1)';
|
const maliciousStr = 'javajavascript:script:alert(1)';
|
||||||
const result = sanitizeText(maliciousStr, {
|
const result = sanitizeText(maliciousStr, {
|
||||||
securityLevel: 'strict',
|
securityLevel: 'strict',
|
||||||
@@ -58,8 +58,8 @@ describe('Sanitize text', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generic parser', function () {
|
describe('generic parser', () => {
|
||||||
it('should parse generic types', function () {
|
it('should parse generic types', () => {
|
||||||
expect(parseGenericTypes('test~T~')).toEqual('test<T>');
|
expect(parseGenericTypes('test~T~')).toEqual('test<T>');
|
||||||
expect(parseGenericTypes('test~Array~Array~string~~~')).toEqual('test<Array<Array<string>>>');
|
expect(parseGenericTypes('test~Array~Array~string~~~')).toEqual('test<Array<Array<string>>>');
|
||||||
expect(parseGenericTypes('test~Array~Array~string[]~~~')).toEqual(
|
expect(parseGenericTypes('test~Array~Array~string[]~~~')).toEqual(
|
@@ -1,6 +1,7 @@
|
|||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { MermaidConfig } from '../../config.type.js';
|
import { MermaidConfig } from '../../config.type.js';
|
||||||
|
|
||||||
|
// Remove and ignore br:s
|
||||||
export const lineBreakRegex = /<br\s*\/?>/gi;
|
export const lineBreakRegex = /<br\s*\/?>/gi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
58
packages/mermaid/src/diagrams/common/commonTypes.ts
Normal file
58
packages/mermaid/src/diagrams/common/commonTypes.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
export interface RectData {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
fill: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
stroke: string;
|
||||||
|
class?: string;
|
||||||
|
color?: string;
|
||||||
|
rx?: number;
|
||||||
|
ry?: number;
|
||||||
|
attrs?: Record<string, string | number>;
|
||||||
|
anchor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Bound {
|
||||||
|
startx: number;
|
||||||
|
stopx: number;
|
||||||
|
starty: number;
|
||||||
|
stopy: number;
|
||||||
|
fill: string;
|
||||||
|
stroke: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextData {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
anchor: string;
|
||||||
|
text: string;
|
||||||
|
textMargin: number;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextObject {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
fill?: string;
|
||||||
|
anchor?: string;
|
||||||
|
'text-anchor': string;
|
||||||
|
style: string;
|
||||||
|
textMargin: number;
|
||||||
|
rx: number;
|
||||||
|
ry: number;
|
||||||
|
tspan: boolean;
|
||||||
|
valign?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type D3RectElement = d3.Selection<SVGRectElement, unknown, Element | null, unknown>;
|
||||||
|
|
||||||
|
export type D3UseElement = d3.Selection<SVGUseElement, unknown, Element | null, unknown>;
|
||||||
|
|
||||||
|
export type D3ImageElement = d3.Selection<SVGImageElement, unknown, Element | null, unknown>;
|
||||||
|
|
||||||
|
export type D3TextElement = d3.Selection<SVGTextElement, unknown, Element | null, unknown>;
|
||||||
|
|
||||||
|
export type D3TSpanElement = d3.Selection<SVGTSpanElement, unknown, Element | null, unknown>;
|
@@ -1,114 +0,0 @@
|
|||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
|
||||||
const rectElem = elem.append('rect');
|
|
||||||
rectElem.attr('x', rectData.x);
|
|
||||||
rectElem.attr('y', rectData.y);
|
|
||||||
rectElem.attr('fill', rectData.fill);
|
|
||||||
rectElem.attr('stroke', rectData.stroke);
|
|
||||||
rectElem.attr('width', rectData.width);
|
|
||||||
rectElem.attr('height', rectData.height);
|
|
||||||
rectElem.attr('rx', rectData.rx);
|
|
||||||
rectElem.attr('ry', rectData.ry);
|
|
||||||
|
|
||||||
if (rectData.attrs !== 'undefined' && rectData.attrs !== null) {
|
|
||||||
for (let attrKey in rectData.attrs) {
|
|
||||||
rectElem.attr(attrKey, rectData.attrs[attrKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rectData.class !== 'undefined') {
|
|
||||||
rectElem.attr('class', rectData.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rectElem;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a background rectangle
|
|
||||||
*
|
|
||||||
* @param {any} elem Diagram (reference for bounds)
|
|
||||||
* @param {any} bounds Shape of the rectangle
|
|
||||||
*/
|
|
||||||
export const drawBackgroundRect = function (elem, bounds) {
|
|
||||||
const rectElem = drawRect(elem, {
|
|
||||||
x: bounds.startx,
|
|
||||||
y: bounds.starty,
|
|
||||||
width: bounds.stopx - bounds.startx,
|
|
||||||
height: bounds.stopy - bounds.starty,
|
|
||||||
fill: bounds.fill,
|
|
||||||
stroke: bounds.stroke,
|
|
||||||
class: 'rect',
|
|
||||||
});
|
|
||||||
rectElem.lower();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawText = function (elem, textData) {
|
|
||||||
// Remove and ignore br:s
|
|
||||||
const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
|
|
||||||
|
|
||||||
const textElem = elem.append('text');
|
|
||||||
textElem.attr('x', textData.x);
|
|
||||||
textElem.attr('y', textData.y);
|
|
||||||
textElem.attr('class', 'legend');
|
|
||||||
|
|
||||||
textElem.style('text-anchor', textData.anchor);
|
|
||||||
|
|
||||||
if (textData.class !== undefined) {
|
|
||||||
textElem.attr('class', textData.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
const span = textElem.append('tspan');
|
|
||||||
span.attr('x', textData.x + textData.textMargin * 2);
|
|
||||||
span.text(nText);
|
|
||||||
|
|
||||||
return textElem;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawImage = function (elem, x, y, link) {
|
|
||||||
const imageElem = elem.append('image');
|
|
||||||
imageElem.attr('x', x);
|
|
||||||
imageElem.attr('y', y);
|
|
||||||
var sanitizedLink = sanitizeUrl(link);
|
|
||||||
imageElem.attr('xlink:href', sanitizedLink);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawEmbeddedImage = function (elem, x, y, link) {
|
|
||||||
const imageElem = elem.append('use');
|
|
||||||
imageElem.attr('x', x);
|
|
||||||
imageElem.attr('y', y);
|
|
||||||
const sanitizedLink = sanitizeUrl(link);
|
|
||||||
imageElem.attr('xlink:href', '#' + sanitizedLink);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getNoteRect = function () {
|
|
||||||
return {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
fill: '#EDF2AE',
|
|
||||||
stroke: '#666',
|
|
||||||
anchor: 'start',
|
|
||||||
rx: 0,
|
|
||||||
ry: 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTextObj = function () {
|
|
||||||
return {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
fill: undefined,
|
|
||||||
anchor: undefined,
|
|
||||||
'text-anchor': 'start',
|
|
||||||
style: '#666',
|
|
||||||
textMargin: 0,
|
|
||||||
rx: 0,
|
|
||||||
ry: 0,
|
|
||||||
tspan: true,
|
|
||||||
valign: undefined,
|
|
||||||
};
|
|
||||||
};
|
|
126
packages/mermaid/src/diagrams/common/svgDrawCommon.ts
Normal file
126
packages/mermaid/src/diagrams/common/svgDrawCommon.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
import type { Group, SVG } from '../../diagram-api/types.js';
|
||||||
|
import type {
|
||||||
|
Bound,
|
||||||
|
D3ImageElement,
|
||||||
|
D3RectElement,
|
||||||
|
D3TSpanElement,
|
||||||
|
D3TextElement,
|
||||||
|
D3UseElement,
|
||||||
|
RectData,
|
||||||
|
TextData,
|
||||||
|
TextObject,
|
||||||
|
} from './commonTypes.js';
|
||||||
|
import { lineBreakRegex } from './common.js';
|
||||||
|
|
||||||
|
export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => {
|
||||||
|
const rectElement: D3RectElement = element.append('rect');
|
||||||
|
rectElement.attr('x', rectData.x);
|
||||||
|
rectElement.attr('y', rectData.y);
|
||||||
|
rectElement.attr('fill', rectData.fill);
|
||||||
|
rectElement.attr('stroke', rectData.stroke);
|
||||||
|
rectElement.attr('width', rectData.width);
|
||||||
|
rectElement.attr('height', rectData.height);
|
||||||
|
rectData.rx !== undefined && rectElement.attr('rx', rectData.rx);
|
||||||
|
rectData.ry !== undefined && rectElement.attr('ry', rectData.ry);
|
||||||
|
|
||||||
|
if (rectData.attrs !== undefined) {
|
||||||
|
for (const attrKey in rectData.attrs) {
|
||||||
|
rectElement.attr(attrKey, rectData.attrs[attrKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rectData.class !== undefined && rectElement.attr('class', rectData.class);
|
||||||
|
|
||||||
|
return rectElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a background rectangle
|
||||||
|
*
|
||||||
|
* @param element - Diagram (reference for bounds)
|
||||||
|
* @param bounds - Shape of the rectangle
|
||||||
|
*/
|
||||||
|
export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => {
|
||||||
|
const rectData: RectData = {
|
||||||
|
x: bounds.startx,
|
||||||
|
y: bounds.starty,
|
||||||
|
width: bounds.stopx - bounds.startx,
|
||||||
|
height: bounds.stopy - bounds.starty,
|
||||||
|
fill: bounds.fill,
|
||||||
|
stroke: bounds.stroke,
|
||||||
|
class: 'rect',
|
||||||
|
};
|
||||||
|
const rectElement: D3RectElement = drawRect(element, rectData);
|
||||||
|
rectElement.lower();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => {
|
||||||
|
const nText: string = textData.text.replace(lineBreakRegex, ' ');
|
||||||
|
|
||||||
|
const textElem: D3TextElement = element.append('text');
|
||||||
|
textElem.attr('x', textData.x);
|
||||||
|
textElem.attr('y', textData.y);
|
||||||
|
textElem.attr('class', 'legend');
|
||||||
|
|
||||||
|
textElem.style('text-anchor', textData.anchor);
|
||||||
|
textData.class !== undefined && textElem.attr('class', textData.class);
|
||||||
|
|
||||||
|
const tspan: D3TSpanElement = textElem.append('tspan');
|
||||||
|
tspan.attr('x', textData.x + textData.textMargin * 2);
|
||||||
|
tspan.text(nText);
|
||||||
|
|
||||||
|
return textElem;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawImage = (elem: SVG | Group, x: number, y: number, link: string): void => {
|
||||||
|
const imageElement: D3ImageElement = elem.append('image');
|
||||||
|
imageElement.attr('x', x);
|
||||||
|
imageElement.attr('y', y);
|
||||||
|
const sanitizedLink: string = sanitizeUrl(link);
|
||||||
|
imageElement.attr('xlink:href', sanitizedLink);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const drawEmbeddedImage = (
|
||||||
|
element: SVG | Group,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
link: string
|
||||||
|
): void => {
|
||||||
|
const imageElement: D3UseElement = element.append('use');
|
||||||
|
imageElement.attr('x', x);
|
||||||
|
imageElement.attr('y', y);
|
||||||
|
const sanitizedLink: string = sanitizeUrl(link);
|
||||||
|
imageElement.attr('xlink:href', `#${sanitizedLink}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNoteRect = (): RectData => {
|
||||||
|
const noteRectData: RectData = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
fill: '#EDF2AE',
|
||||||
|
stroke: '#666',
|
||||||
|
anchor: 'start',
|
||||||
|
rx: 0,
|
||||||
|
ry: 0,
|
||||||
|
};
|
||||||
|
return noteRectData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTextObj = (): TextObject => {
|
||||||
|
const testObject: TextObject = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
'text-anchor': 'start',
|
||||||
|
style: '#666',
|
||||||
|
textMargin: 0,
|
||||||
|
rx: 0,
|
||||||
|
ry: 0,
|
||||||
|
tspan: true,
|
||||||
|
};
|
||||||
|
return testObject;
|
||||||
|
};
|
@@ -3,7 +3,7 @@ import { select, selectAll } from 'd3';
|
|||||||
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
import * as configApi from '../../config.js';
|
import * as configApi from '../../config.js';
|
||||||
import assignWithDepth from '../../assignWithDepth.js';
|
import assignWithDepth from '../../assignWithDepth.js';
|
||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
import { addFunction } from '../../interactionDb.js';
|
import { addFunction } from '../../interactionDb.js';
|
||||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { arc as d3arc } from 'd3';
|
import { arc as d3arc } from 'd3';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
return svgDrawCommon.drawRect(elem, rectData);
|
return svgDrawCommon.drawRect(elem, rectData);
|
||||||
|
Reference in New Issue
Block a user