feat: experimental version of sanboxing

This commit is contained in:
Knut Sveidqvist 2022-01-20 19:37:52 +01:00
parent 3a2f6659a6
commit 4081d7f5f6
7 changed files with 138 additions and 34 deletions

View File

@ -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() {

View File

@ -70,7 +70,7 @@
// fontFamily: 'courier',
fontSize: 18,
curve: 'basis',
// securityLevel: 'loose',
securityLevel: 'sandbox',
startOnLoad: false,
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
// themeVariables: {relationLabelColor: 'red'}

View File

@ -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) {

View File

@ -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);

View File

@ -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 */

View File

@ -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),

View File

@ -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"