mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
feat: experimental version of sanboxing
This commit is contained in:
parent
3a2f6659a6
commit
4081d7f5f6
@ -25,15 +25,29 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>info below</div>
|
||||
<div class="flex flex-wrap">
|
||||
|
||||
<div class="mermaid" style="width: 100%; height: 20%;">
|
||||
stateDiagram-v2
|
||||
[*] --> S1
|
||||
state "Some long name" as S1
|
||||
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%;">
|
||||
classDiagram
|
||||
Animal "1" <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <--o Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
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>
|
||||
@ -59,7 +73,7 @@ stateDiagram-v2
|
||||
logLevel: 0,
|
||||
fontSize: 18,
|
||||
curve: 'cardinal',
|
||||
securityLevel: 'strict',
|
||||
securityLevel: 'sandbox',
|
||||
// themeVariables: {relationLabelColor: 'red'}
|
||||
});
|
||||
function callback() {
|
||||
|
@ -70,7 +70,7 @@
|
||||
// fontFamily: 'courier',
|
||||
fontSize: 18,
|
||||
curve: 'basis',
|
||||
// securityLevel: 'loose',
|
||||
securityLevel: 'sandbox',
|
||||
startOnLoad: false,
|
||||
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||
// themeVariables: {relationLabelColor: 'red'}
|
||||
|
@ -143,7 +143,8 @@ export const addMember = function (className, member) {
|
||||
|
||||
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
|
||||
// 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) {
|
||||
theClass.methods.push(sanitizeText(memberString));
|
||||
} else if (memberString) {
|
||||
|
@ -363,7 +363,7 @@ export const drawOld = function (text, id) {
|
||||
};
|
||||
|
||||
export const draw = function (text, id) {
|
||||
log.info('Drawing class');
|
||||
log.info('Drawing class - ', id);
|
||||
classDb.clear();
|
||||
// const parser = classDb.parser;
|
||||
// parser.yy = classDb;
|
||||
@ -379,6 +379,7 @@ export const draw = function (text, id) {
|
||||
//let dir = 'TD';
|
||||
|
||||
const conf = getConfig().flowchart;
|
||||
const securityLevel = getConfig().securityLevel;
|
||||
log.info('config:', conf);
|
||||
const nodeSpacing = conf.nodeSpacing || 50;
|
||||
const rankSpacing = conf.rankSpacing || 50;
|
||||
@ -430,11 +431,19 @@ export const draw = function (text, id) {
|
||||
// flowChartShapes.addToRenderV2(addShape);
|
||||
|
||||
// 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');
|
||||
|
||||
// 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);
|
||||
|
||||
// element.selectAll('g.node').attr('title', function() {
|
||||
@ -462,14 +471,15 @@ export const draw = function (text, id) {
|
||||
|
||||
// Add label rects for non html labels
|
||||
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++) {
|
||||
const label = labels[k];
|
||||
|
||||
// Get dimensions of label
|
||||
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('ry', 0);
|
||||
rect.setAttribute('width', dim.width);
|
||||
|
@ -83,6 +83,7 @@ const init = function () {
|
||||
let txt;
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
// element is the current div with mermaid class
|
||||
const element = nodes[i];
|
||||
|
||||
/*! 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';
|
||||
}
|
||||
|
||||
// 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') {
|
||||
// A container was provided by the caller
|
||||
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')
|
||||
.attr('id', 'd' + id)
|
||||
.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')
|
||||
.append('g');
|
||||
} 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);
|
||||
if (existingSvg) {
|
||||
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) {
|
||||
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')
|
||||
.attr('id', 'd' + id)
|
||||
// this is the seed of the svg to be rendered
|
||||
.append('svg')
|
||||
.attr('id', id)
|
||||
.attr('width', '100%')
|
||||
@ -264,10 +318,10 @@ const render = function (id, _txt, cb, container) {
|
||||
.append('g');
|
||||
}
|
||||
|
||||
window.txt = 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);
|
||||
|
||||
// insert inline style into svg
|
||||
@ -430,22 +484,51 @@ const render = function (id, _txt, cb, container) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
select(`[id="${id}"]`)
|
||||
root
|
||||
.select(`[id="${id}"]`)
|
||||
.selectAll('foreignobject > *')
|
||||
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||
|
||||
// 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);
|
||||
if (!cnf.arrowMarkerAbsolute || cnf.arrowMarkerAbsolute === 'false') {
|
||||
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);
|
||||
|
||||
// Fix for when the br tag is used
|
||||
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') {
|
||||
switch (graphType) {
|
||||
case 'flowchart':
|
||||
@ -467,11 +550,10 @@ const render = function (id, _txt, cb, container) {
|
||||
}
|
||||
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') {
|
||||
select('#d' + id)
|
||||
.node()
|
||||
.remove();
|
||||
select(tmpElementSelector).node().remove();
|
||||
}
|
||||
|
||||
return svgCode;
|
||||
@ -570,6 +652,7 @@ function updateRendererConfigs(conf) {
|
||||
errorRenderer.setConf(conf.class);
|
||||
}
|
||||
|
||||
/** To be removed */
|
||||
function reinitialize() {
|
||||
// `mermaidAPI.reinitialize: v${pkg.version}`,
|
||||
// JSON.stringify(options),
|
||||
|
@ -3306,16 +3306,11 @@ camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
caniuse-lite@^1.0.30001280:
|
||||
caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001286:
|
||||
version "1.0.30001300"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz"
|
||||
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:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user