mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-15 13:29:40 +02:00
feat: experimental version of sanboxing
This commit is contained in:
@@ -25,15 +25,29 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>info below</div>
|
<div class="mermaid" style="width: 100%;">
|
||||||
<div class="flex flex-wrap">
|
classDiagram
|
||||||
|
Animal "1" <|-- Duck
|
||||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
Animal <|-- Fish
|
||||||
stateDiagram-v2
|
Animal <--o Zebra
|
||||||
[*] --> S1
|
Animal : +int age
|
||||||
state "Some long name" as S1
|
Animal : +String gender
|
||||||
|
Animal: +isMammal()
|
||||||
</div>
|
Animal: +mate()
|
||||||
|
class Duck{
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
class Fish{
|
||||||
|
-int sizeInFeet
|
||||||
|
-canEat()
|
||||||
|
}
|
||||||
|
class Zebra{
|
||||||
|
+bool is_wild
|
||||||
|
+run()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -59,7 +73,7 @@ stateDiagram-v2
|
|||||||
logLevel: 0,
|
logLevel: 0,
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
curve: 'cardinal',
|
curve: 'cardinal',
|
||||||
securityLevel: 'strict',
|
securityLevel: 'sandbox',
|
||||||
// themeVariables: {relationLabelColor: 'red'}
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
});
|
});
|
||||||
function callback() {
|
function callback() {
|
||||||
|
@@ -70,7 +70,7 @@
|
|||||||
// fontFamily: 'courier',
|
// fontFamily: 'courier',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
curve: 'basis',
|
curve: 'basis',
|
||||||
// securityLevel: 'loose',
|
securityLevel: 'sandbox',
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||||
// themeVariables: {relationLabelColor: 'red'}
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
|
@@ -143,7 +143,8 @@ export const addMember = function (className, member) {
|
|||||||
|
|
||||||
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
|
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
|
||||||
// Remove leading and trailing brackets
|
// Remove leading and trailing brackets
|
||||||
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
|
theClass.annotations.push(memberString.substring(2, memberString.length - 2));
|
||||||
|
// theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
|
||||||
} else if (memberString.indexOf(')') > 0) {
|
} else if (memberString.indexOf(')') > 0) {
|
||||||
theClass.methods.push(sanitizeText(memberString));
|
theClass.methods.push(sanitizeText(memberString));
|
||||||
} else if (memberString) {
|
} else if (memberString) {
|
||||||
|
@@ -363,7 +363,7 @@ export const drawOld = function (text, id) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const draw = function (text, id) {
|
export const draw = function (text, id) {
|
||||||
log.info('Drawing class');
|
log.info('Drawing class - ', id);
|
||||||
classDb.clear();
|
classDb.clear();
|
||||||
// const parser = classDb.parser;
|
// const parser = classDb.parser;
|
||||||
// parser.yy = classDb;
|
// parser.yy = classDb;
|
||||||
@@ -379,6 +379,7 @@ export const draw = function (text, id) {
|
|||||||
//let dir = 'TD';
|
//let dir = 'TD';
|
||||||
|
|
||||||
const conf = getConfig().flowchart;
|
const conf = getConfig().flowchart;
|
||||||
|
const securityLevel = getConfig().securityLevel;
|
||||||
log.info('config:', conf);
|
log.info('config:', conf);
|
||||||
const nodeSpacing = conf.nodeSpacing || 50;
|
const nodeSpacing = conf.nodeSpacing || 50;
|
||||||
const rankSpacing = conf.rankSpacing || 50;
|
const rankSpacing = conf.rankSpacing || 50;
|
||||||
@@ -430,11 +431,19 @@ export const draw = function (text, id) {
|
|||||||
// flowChartShapes.addToRenderV2(addShape);
|
// flowChartShapes.addToRenderV2(addShape);
|
||||||
|
|
||||||
// Set up an SVG group so that we can translate the final graph.
|
// Set up an SVG group so that we can translate the final graph.
|
||||||
const svg = select(`[id="${id}"]`);
|
let sandboxElement;
|
||||||
|
if (securityLevel === 'sandbox') {
|
||||||
|
sandboxElement = select('#i' + id);
|
||||||
|
}
|
||||||
|
const root =
|
||||||
|
securityLevel === 'sandbox'
|
||||||
|
? select(sandboxElement.nodes()[0].contentDocument.body)
|
||||||
|
: select('body');
|
||||||
|
const svg = root.select(`[id="${id}"]`);
|
||||||
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||||
|
|
||||||
// Run the renderer. This is what draws the final graph.
|
// Run the renderer. This is what draws the final graph.
|
||||||
const element = select('#' + id + ' g');
|
const element = root.select('#' + id + ' g');
|
||||||
render(element, g, ['aggregation', 'extension', 'composition', 'dependency'], 'classDiagram', id);
|
render(element, g, ['aggregation', 'extension', 'composition', 'dependency'], 'classDiagram', id);
|
||||||
|
|
||||||
// element.selectAll('g.node').attr('title', function() {
|
// element.selectAll('g.node').attr('title', function() {
|
||||||
@@ -462,14 +471,15 @@ export const draw = function (text, id) {
|
|||||||
|
|
||||||
// Add label rects for non html labels
|
// Add label rects for non html labels
|
||||||
if (!conf.htmlLabels) {
|
if (!conf.htmlLabels) {
|
||||||
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||||
|
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
||||||
for (let k = 0; k < labels.length; k++) {
|
for (let k = 0; k < labels.length; k++) {
|
||||||
const label = labels[k];
|
const label = labels[k];
|
||||||
|
|
||||||
// Get dimensions of label
|
// Get dimensions of label
|
||||||
const dim = label.getBBox();
|
const dim = label.getBBox();
|
||||||
|
|
||||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
rect.setAttribute('rx', 0);
|
rect.setAttribute('rx', 0);
|
||||||
rect.setAttribute('ry', 0);
|
rect.setAttribute('ry', 0);
|
||||||
rect.setAttribute('width', dim.width);
|
rect.setAttribute('width', dim.width);
|
||||||
|
@@ -83,6 +83,7 @@ const init = function () {
|
|||||||
let txt;
|
let txt;
|
||||||
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
// element is the current div with mermaid class
|
||||||
const element = nodes[i];
|
const element = nodes[i];
|
||||||
|
|
||||||
/*! Check if previously processed */
|
/*! Check if previously processed */
|
||||||
|
@@ -232,10 +232,40 @@ const render = function (id, _txt, cb, container) {
|
|||||||
txt = 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
txt = 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let d3Iframe;
|
||||||
|
let root;
|
||||||
|
if (cnf.securityLevel === 'sandbox') {
|
||||||
|
// IF we are in sandboxed mode, we do everyting mermaid related
|
||||||
|
// in a sandboxed div
|
||||||
|
const iframe = select('body')
|
||||||
|
.append('iframe')
|
||||||
|
.attr('id', 'i' + id)
|
||||||
|
.attr('sandbox', '');
|
||||||
|
// const iframeBody = ;
|
||||||
|
root = select(iframe.nodes()[0].contentDocument.body);
|
||||||
|
} else {
|
||||||
|
root = select('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
// In regular execurtion the container will be the div with a mermaid class
|
||||||
if (typeof container !== 'undefined') {
|
if (typeof container !== 'undefined') {
|
||||||
|
// A container was provided by the caller
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
select(container)
|
if (cnf.securityLevel === 'sandbox') {
|
||||||
|
// IF we are in sandboxed mode, we do everyting mermaid related
|
||||||
|
// in a sandboxed div
|
||||||
|
const iframe = select(container)
|
||||||
|
.append('iframe')
|
||||||
|
.attr('id', 'i' + id)
|
||||||
|
.attr('sandbox', '');
|
||||||
|
// const iframeBody = ;
|
||||||
|
root = select(iframe.nodes()[0].contentDocument.body);
|
||||||
|
} else {
|
||||||
|
root = select(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
root
|
||||||
.append('div')
|
.append('div')
|
||||||
.attr('id', 'd' + id)
|
.attr('id', 'd' + id)
|
||||||
.attr('style', 'font-family: ' + cnf.fontFamily)
|
.attr('style', 'font-family: ' + cnf.fontFamily)
|
||||||
@@ -245,18 +275,42 @@ const render = function (id, _txt, cb, container) {
|
|||||||
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
||||||
.append('g');
|
.append('g');
|
||||||
} else {
|
} else {
|
||||||
|
// No container was provided
|
||||||
|
// If there is an existsing element with the id, we remove it
|
||||||
|
// this likley a previously rendered diagram
|
||||||
const existingSvg = document.getElementById(id);
|
const existingSvg = document.getElementById(id);
|
||||||
if (existingSvg) {
|
if (existingSvg) {
|
||||||
existingSvg.remove();
|
existingSvg.remove();
|
||||||
}
|
}
|
||||||
const element = document.querySelector('#' + 'd' + id);
|
|
||||||
|
// Remove previous tpm element if it exists
|
||||||
|
let element;
|
||||||
|
if (cnf.securityLevel === 'sandbox') {
|
||||||
|
element = document.querySelector('#' + 'd' + id);
|
||||||
|
} else {
|
||||||
|
element = document.querySelector('#' + 'i' + id);
|
||||||
|
}
|
||||||
if (element) {
|
if (element) {
|
||||||
element.remove();
|
element.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
select('body')
|
// if (cnf.securityLevel === 'sandbox') {
|
||||||
|
// const iframe = select('body')
|
||||||
|
// .append('iframe')
|
||||||
|
// .attr('id', 'i' + id)
|
||||||
|
// .attr('sandbox', '');
|
||||||
|
// // const iframeBody = ;
|
||||||
|
// root = select(iframe.nodes()[0].contentDocument.body);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Add the tmp div used for rendering with the id `d${id}`
|
||||||
|
// d+id it will contain a svg with the id "id"
|
||||||
|
|
||||||
|
// This is the temporary div
|
||||||
|
root
|
||||||
.append('div')
|
.append('div')
|
||||||
.attr('id', 'd' + id)
|
.attr('id', 'd' + id)
|
||||||
|
// this is the seed of the svg to be rendered
|
||||||
.append('svg')
|
.append('svg')
|
||||||
.attr('id', id)
|
.attr('id', id)
|
||||||
.attr('width', '100%')
|
.attr('width', '100%')
|
||||||
@@ -264,10 +318,10 @@ const render = function (id, _txt, cb, container) {
|
|||||||
.append('g');
|
.append('g');
|
||||||
}
|
}
|
||||||
|
|
||||||
window.txt = txt;
|
|
||||||
txt = encodeEntities(txt);
|
txt = encodeEntities(txt);
|
||||||
|
|
||||||
const element = select('#d' + id).node();
|
// Get the tmp element containing the the svg
|
||||||
|
const element = root.select('#d' + id).node();
|
||||||
const graphType = utils.detectType(txt, cnf);
|
const graphType = utils.detectType(txt, cnf);
|
||||||
|
|
||||||
// insert inline style into svg
|
// insert inline style into svg
|
||||||
@@ -430,22 +484,51 @@ const render = function (id, _txt, cb, container) {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
select(`[id="${id}"]`)
|
root
|
||||||
|
.select(`[id="${id}"]`)
|
||||||
.selectAll('foreignobject > *')
|
.selectAll('foreignobject > *')
|
||||||
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||||
|
|
||||||
// Fix for when the base tag is used
|
// Fix for when the base tag is used
|
||||||
let svgCode = select('#d' + id).node().innerHTML;
|
let svgCode = root.select('#d' + id).node().innerHTML;
|
||||||
log.debug('cnf.arrowMarkerAbsolute', cnf.arrowMarkerAbsolute);
|
log.debug('cnf.arrowMarkerAbsolute', cnf.arrowMarkerAbsolute);
|
||||||
if (!cnf.arrowMarkerAbsolute || cnf.arrowMarkerAbsolute === 'false') {
|
if (!cnf.arrowMarkerAbsolute || cnf.arrowMarkerAbsolute === 'false') {
|
||||||
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
|
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const iframe = document.createElement('iframe');
|
||||||
|
// iframe.setAttribute('frameBorder', '0');
|
||||||
|
// iframe.setAttribute('id', id);
|
||||||
|
// iframe.setAttribute('sanbox', '');
|
||||||
|
// iframe.setAttribute('src', 'about:blank');
|
||||||
|
// iframe.contentWindow.document.body = svgCode;
|
||||||
|
// element.innerHTML = '';
|
||||||
|
// // element.appendChild(iframe);
|
||||||
|
// // element.innerHTML = '<iframe sandbox>' + svgCode + '</iframe>';
|
||||||
|
|
||||||
svgCode = decodeEntities(svgCode);
|
svgCode = decodeEntities(svgCode);
|
||||||
|
|
||||||
// Fix for when the br tag is used
|
// Fix for when the br tag is used
|
||||||
svgCode = svgCode.replace(/<br>/g, '<br/>');
|
svgCode = svgCode.replace(/<br>/g, '<br/>');
|
||||||
|
|
||||||
|
if (cnf.securityLevel === 'sandbox') {
|
||||||
|
// const newSvgCode =
|
||||||
|
// '<iframe id="' +
|
||||||
|
// id +
|
||||||
|
// '" sandbox src="data:text/html;base64,' +
|
||||||
|
// btoa(btoa(unescape(encodeURIComponent(svgCode)))) +
|
||||||
|
// '"></iframe>';
|
||||||
|
// svgCode = newSvgCode;
|
||||||
|
// svgCode = `<iframe src="data:text/html;base64,V2VsY29tZSB0byA8Yj5iYXNlNjQuZ3VydTwvYj4h">
|
||||||
|
// The “iframe” tag is not supported by your browser.
|
||||||
|
// </iframe>`;
|
||||||
|
svgCode = `<iframe style="width:100%;height:100%;" src="data:text/html;base64,${btoa(
|
||||||
|
svgCode
|
||||||
|
)}" sandbox>
|
||||||
|
The “iframe” tag is not supported by your browser.
|
||||||
|
</iframe>`;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof cb !== 'undefined') {
|
if (typeof cb !== 'undefined') {
|
||||||
switch (graphType) {
|
switch (graphType) {
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
@@ -467,11 +550,10 @@ const render = function (id, _txt, cb, container) {
|
|||||||
}
|
}
|
||||||
attachFunctions();
|
attachFunctions();
|
||||||
|
|
||||||
const node = select('#d' + id).node();
|
const tmpElementSelector = cnf.securityLevel === 'sandbox' ? '#i' + id : '#d' + id;
|
||||||
|
const node = select(tmpElementSelector).node();
|
||||||
if (node !== null && typeof node.remove === 'function') {
|
if (node !== null && typeof node.remove === 'function') {
|
||||||
select('#d' + id)
|
select(tmpElementSelector).node().remove();
|
||||||
.node()
|
|
||||||
.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return svgCode;
|
return svgCode;
|
||||||
@@ -570,6 +652,7 @@ function updateRendererConfigs(conf) {
|
|||||||
errorRenderer.setConf(conf.class);
|
errorRenderer.setConf(conf.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** To be removed */
|
||||||
function reinitialize() {
|
function reinitialize() {
|
||||||
// `mermaidAPI.reinitialize: v${pkg.version}`,
|
// `mermaidAPI.reinitialize: v${pkg.version}`,
|
||||||
// JSON.stringify(options),
|
// JSON.stringify(options),
|
||||||
|
@@ -3306,16 +3306,11 @@ camelcase@^6.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001280:
|
caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001286:
|
||||||
version "1.0.30001300"
|
version "1.0.30001300"
|
||||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz"
|
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz"
|
||||||
integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==
|
integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001286:
|
|
||||||
version "1.0.30001300"
|
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz#11ab6c57d3eb6f964cba950401fd00a146786468"
|
|
||||||
integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==
|
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||||
|
Reference in New Issue
Block a user