mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-29 12:19:41 +02:00
Compare commits
67 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
77f2a57155 | ||
![]() |
91ec41762a | ||
![]() |
7abe279b4a | ||
![]() |
ce04c82356 | ||
![]() |
7a6d73762e | ||
![]() |
20bbe6cfb0 | ||
![]() |
583d905b88 | ||
![]() |
40479aef77 | ||
![]() |
ab276ccdfe | ||
![]() |
5178de7fef | ||
![]() |
d3d5f12d3c | ||
![]() |
db51adcc26 | ||
![]() |
f38c6a661a | ||
![]() |
3d016ba800 | ||
![]() |
bfeb3a8b3c | ||
![]() |
e73894bfbd | ||
![]() |
c2c753ce7d | ||
![]() |
a352cf3ae5 | ||
![]() |
35f3d57cf3 | ||
![]() |
9bd5652b88 | ||
![]() |
0924a1cef0 | ||
![]() |
0b56686f4d | ||
![]() |
9a398c0a76 | ||
![]() |
db3edb61b4 | ||
![]() |
f4a3f33f36 | ||
![]() |
2305bbed93 | ||
![]() |
a900a8cc17 | ||
![]() |
d48a4bd13d | ||
![]() |
d5c4ea30db | ||
![]() |
979960e8e4 | ||
![]() |
24ee6746f4 | ||
![]() |
4c76b4189c | ||
![]() |
c6dfc9306f | ||
![]() |
d3577eb59b | ||
![]() |
066b7a0d0b | ||
![]() |
be46ace02e | ||
![]() |
65592e0541 | ||
![]() |
f4c335ad2f | ||
![]() |
e6fc278861 | ||
![]() |
73045b5c6d | ||
![]() |
f6d1f6c825 | ||
![]() |
eae664937d | ||
![]() |
fabaddb10e | ||
![]() |
b18cb6933a | ||
![]() |
6d3ebab529 | ||
![]() |
4369b6cda2 | ||
![]() |
952d360010 | ||
![]() |
f84b6237cf | ||
![]() |
6f800be33b | ||
![]() |
52af0476c1 | ||
![]() |
b4fbe1b0f0 | ||
![]() |
835ae1734c | ||
![]() |
1127ce7b50 | ||
![]() |
9d8273771a | ||
![]() |
0f9722393e | ||
![]() |
e2bb8478d0 | ||
![]() |
a353300707 | ||
![]() |
77b2e4211d | ||
![]() |
868c2ece91 | ||
![]() |
354daee3a1 | ||
![]() |
fcc51f4d94 | ||
![]() |
94f7af1190 | ||
![]() |
1059873b10 | ||
![]() |
4cf2d71eb4 | ||
![]() |
f7b73be561 | ||
![]() |
a349fd3aba | ||
![]() |
295b428d62 |
4
.github/workflows/update-browserlist.yml
vendored
4
.github/workflows/update-browserlist.yml
vendored
@@ -1,5 +1,7 @@
|
|||||||
name: Update Browserslist
|
name: Update Browserslist
|
||||||
on:
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 7 * * 1'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -13,4 +15,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
author_name: ${{ github.actor }}
|
author_name: ${{ github.actor }}
|
||||||
author_email: ${{ github.actor }}@users.noreply.github.com
|
author_email: ${{ github.actor }}@users.noreply.github.com
|
||||||
message: 'Update Browserslist'
|
message: 'chore: update browsers list'
|
||||||
|
0
.husky/commit-msg
Normal file → Executable file
0
.husky/commit-msg
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
@@ -43,7 +43,7 @@ __The following are some examples of the diagrams, charts and graphs that can be
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><pre>
|
<td><pre>
|
||||||
graph TD
|
flowchart TD
|
||||||
A[Hard] -->|Text| B(Round)
|
A[Hard] -->|Text| B(Round)
|
||||||
B --> C{Decision}
|
B --> C{Decision}
|
||||||
C -->|One| D[Result 1]
|
C -->|One| D[Result 1]
|
||||||
|
@@ -110,4 +110,9 @@ describe('XSS', () => {
|
|||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
cy.get('#the-malware').should('not.exist');
|
cy.get('#the-malware').should('not.exist');
|
||||||
});
|
});
|
||||||
|
it('should sanitize cardinalities properly in class diagrams', () => {
|
||||||
|
cy.visit('http://localhost:9000/xss18.html');
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('#the-malware').should('not.exist');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -183,11 +183,58 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
cy.get('svg');
|
cy.get('svg');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render entities with keys', () => {
|
||||||
|
renderGraph(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
AUTHOR_WITH_LONG_ENTITY_NAME {
|
||||||
|
string name PK
|
||||||
|
}
|
||||||
|
AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
|
||||||
|
BOOK {
|
||||||
|
float price
|
||||||
|
string author FK
|
||||||
|
string title PK
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render entities with comments', () => {
|
||||||
|
renderGraph(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
AUTHOR_WITH_LONG_ENTITY_NAME {
|
||||||
|
string name "comment"
|
||||||
|
}
|
||||||
|
AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
|
||||||
|
BOOK {
|
||||||
|
string author
|
||||||
|
string title "author comment"
|
||||||
|
float price "price comment"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
|
|
||||||
it('should render entities with keys and comments', () => {
|
it('should render entities with keys and comments', () => {
|
||||||
renderGraph(
|
renderGraph(
|
||||||
`
|
`
|
||||||
erDiagram
|
erDiagram
|
||||||
BOOK { string title PK "comment"}
|
AUTHOR_WITH_LONG_ENTITY_NAME {
|
||||||
|
string name PK "comment"
|
||||||
|
}
|
||||||
|
AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
|
||||||
|
BOOK {
|
||||||
|
string description
|
||||||
|
float price "price comment"
|
||||||
|
string title PK "title comment"
|
||||||
|
string author FK
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
|
107
cypress/platform/xss15.html
Normal file
107
cypress/platform/xss15.html
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/* background: rgb(221, 208, 208); */
|
||||||
|
/* background:#333; */
|
||||||
|
font-family: 'Arial';
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
h1 { color: grey;}
|
||||||
|
.mermaid2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mermaid svg {
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
.malware {
|
||||||
|
position: fixed;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
height: 150px;
|
||||||
|
background: red;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>Security check</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div id="diagram" class="mermaid"></div>
|
||||||
|
<div id="res" class=""></div>
|
||||||
|
<script src="./mermaid.js"></script>
|
||||||
|
<script>
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
// console.error('Mermaid error: ', err);
|
||||||
|
};
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: 'forest',
|
||||||
|
arrowMarkerAbsolute: true,
|
||||||
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
|
logLevel: 0,
|
||||||
|
state: {
|
||||||
|
defaultRenderer: 'dagre-d3',
|
||||||
|
},
|
||||||
|
flowchart: {
|
||||||
|
// defaultRenderer: 'dagre-wrapper',
|
||||||
|
nodeSpacing: 10,
|
||||||
|
curve: 'cardinal',
|
||||||
|
htmlLabels: true,
|
||||||
|
},
|
||||||
|
htmlLabels: true,
|
||||||
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
|
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||||
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
|
// fontFamily: '"times", sans-serif',
|
||||||
|
// fontFamily: 'courier',
|
||||||
|
fontSize: 18,
|
||||||
|
curve: 'basis',
|
||||||
|
securityLevel: 'strict',
|
||||||
|
startOnLoad: false,
|
||||||
|
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||||
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
|
});
|
||||||
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
function xssAttack() {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.id = 'the-malware';
|
||||||
|
div.className = 'malware';
|
||||||
|
div.innerHTML = 'XSS Succeeded';
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
|
throw new Error('XSS Succeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagram = `sequenceDiagram
|
||||||
|
participant John
|
||||||
|
links John: {"XSS": "javas`;
|
||||||
|
diagram += `cript:alert('AudioParam')"}`;
|
||||||
|
|
||||||
|
// var diagram = "stateDiagram-v2\n";
|
||||||
|
// diagram += "<img/src='1'/onerror"
|
||||||
|
// diagram += "=xssAttack()> --> B";
|
||||||
|
console.log(diagram);
|
||||||
|
// document.querySelector('#diagram').innerHTML = diagram;
|
||||||
|
mermaid.render('diagram', diagram, (res) => {
|
||||||
|
console.log(res);
|
||||||
|
document.querySelector('#res').innerHTML = res;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
106
cypress/platform/xss16.html
Normal file
106
cypress/platform/xss16.html
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/* background: rgb(221, 208, 208); */
|
||||||
|
/* background:#333; */
|
||||||
|
font-family: 'Arial';
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
h1 { color: grey;}
|
||||||
|
.mermaid2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mermaid svg {
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
.malware {
|
||||||
|
position: fixed;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
height: 150px;
|
||||||
|
background: red;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>Security check</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div id="diagram" class="mermaid"></div>
|
||||||
|
<div id="res" class=""></div>
|
||||||
|
<script src="./mermaid.js"></script>
|
||||||
|
<script>
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
// console.error('Mermaid error: ', err);
|
||||||
|
};
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: 'forest',
|
||||||
|
arrowMarkerAbsolute: true,
|
||||||
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
|
logLevel: 0,
|
||||||
|
state: {
|
||||||
|
defaultRenderer: 'dagre-d3',
|
||||||
|
},
|
||||||
|
flowchart: {
|
||||||
|
// defaultRenderer: 'dagre-wrapper',
|
||||||
|
nodeSpacing: 10,
|
||||||
|
curve: 'cardinal',
|
||||||
|
htmlLabels: true,
|
||||||
|
},
|
||||||
|
htmlLabels: true,
|
||||||
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
|
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||||
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
|
// fontFamily: '"times", sans-serif',
|
||||||
|
// fontFamily: 'courier',
|
||||||
|
fontSize: 18,
|
||||||
|
curve: 'basis',
|
||||||
|
securityLevel: 'loose',
|
||||||
|
startOnLoad: false,
|
||||||
|
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||||
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
|
});
|
||||||
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
function xssAttack() {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.id = 'the-malware';
|
||||||
|
div.className = 'malware';
|
||||||
|
div.innerHTML = 'XSS Succeeded';
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
|
throw new Error('XSS Succeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagram = `sequenceDiagram
|
||||||
|
participant Alice
|
||||||
|
links Alice: { "Click me!" : "javasjavascript:cript:alert('goose')" }`;
|
||||||
|
|
||||||
|
// // var diagram = "stateDiagram-v2\n";
|
||||||
|
// // diagram += "<img/src='1'/onerror"
|
||||||
|
// diagram += '//via.placeholder.com/64\' width=64 />"]';
|
||||||
|
// console.log(diagram);
|
||||||
|
// document.querySelector('#diagram').innerHTML = diagram;
|
||||||
|
mermaid.render('diagram', diagram, (res) => {
|
||||||
|
console.log(res);
|
||||||
|
document.querySelector('#res').innerHTML = res;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
106
cypress/platform/xss17.html
Normal file
106
cypress/platform/xss17.html
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/* background: rgb(221, 208, 208); */
|
||||||
|
/* background:#333; */
|
||||||
|
font-family: 'Arial';
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
h1 { color: grey;}
|
||||||
|
.mermaid2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mermaid svg {
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
.malware {
|
||||||
|
position: fixed;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
height: 150px;
|
||||||
|
background: red;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>Security check</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div id="diagram" class="mermaid"></div>
|
||||||
|
<div id="res" class=""></div>
|
||||||
|
<script src="./mermaid.js"></script>
|
||||||
|
<script>
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
// console.error('Mermaid error: ', err);
|
||||||
|
};
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: 'forest',
|
||||||
|
arrowMarkerAbsolute: true,
|
||||||
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
|
logLevel: 0,
|
||||||
|
state: {
|
||||||
|
defaultRenderer: 'dagre-d3',
|
||||||
|
},
|
||||||
|
flowchart: {
|
||||||
|
// defaultRenderer: 'dagre-wrapper',
|
||||||
|
nodeSpacing: 10,
|
||||||
|
curve: 'cardinal',
|
||||||
|
htmlLabels: true,
|
||||||
|
},
|
||||||
|
htmlLabels: true,
|
||||||
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
|
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||||
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
|
// fontFamily: '"times", sans-serif',
|
||||||
|
// fontFamily: 'courier',
|
||||||
|
fontSize: 18,
|
||||||
|
curve: 'basis',
|
||||||
|
securityLevel: 'loose',
|
||||||
|
startOnLoad: false,
|
||||||
|
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||||
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
|
});
|
||||||
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
function xssAttack() {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.id = 'the-malware';
|
||||||
|
div.className = 'malware';
|
||||||
|
div.innerHTML = 'XSS Succeeded';
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
|
throw new Error('XSS Succeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagram = `sequenceDiagram
|
||||||
|
participant Alice
|
||||||
|
link Alice: Click Me!@javasjavascript:cript:alert("goose")`;
|
||||||
|
|
||||||
|
// // var diagram = "stateDiagram-v2\n";
|
||||||
|
// // diagram += "<img/src='1'/onerror"
|
||||||
|
// diagram += '//via.placeholder.com/64\' width=64 />"]';
|
||||||
|
// console.log(diagram);
|
||||||
|
// document.querySelector('#diagram').innerHTML = diagram;
|
||||||
|
mermaid.render('diagram', diagram, (res) => {
|
||||||
|
console.log(res);
|
||||||
|
document.querySelector('#res').innerHTML = res;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
105
cypress/platform/xss18.html
Normal file
105
cypress/platform/xss18.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/* background: rgb(221, 208, 208); */
|
||||||
|
/* background:#333; */
|
||||||
|
font-family: 'Arial';
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
h1 { color: grey;}
|
||||||
|
.mermaid2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mermaid svg {
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
.malware {
|
||||||
|
position: fixed;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
height: 150px;
|
||||||
|
background: red;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>Security check</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div id="diagram" class="mermaid"></div>
|
||||||
|
<div id="res" class=""></div>
|
||||||
|
<script src="./mermaid.js"></script>
|
||||||
|
<script>
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
// console.error('Mermaid error: ', err);
|
||||||
|
};
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: 'forest',
|
||||||
|
arrowMarkerAbsolute: true,
|
||||||
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
|
logLevel: 0,
|
||||||
|
state: {
|
||||||
|
defaultRenderer: 'dagre-d3',
|
||||||
|
},
|
||||||
|
flowchart: {
|
||||||
|
// defaultRenderer: 'dagre-wrapper',
|
||||||
|
nodeSpacing: 10,
|
||||||
|
curve: 'cardinal',
|
||||||
|
htmlLabels: true,
|
||||||
|
},
|
||||||
|
htmlLabels: true,
|
||||||
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
|
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||||
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
|
// fontFamily: '"times", sans-serif',
|
||||||
|
// fontFamily: 'courier',
|
||||||
|
fontSize: 18,
|
||||||
|
curve: 'basis',
|
||||||
|
// securityLevel: 'loose',
|
||||||
|
startOnLoad: false,
|
||||||
|
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||||
|
// themeVariables: {relationLabelColor: 'red'}
|
||||||
|
});
|
||||||
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
function xssAttack() {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.id = 'the-malware';
|
||||||
|
div.className = 'malware';
|
||||||
|
div.innerHTML = 'XSS Succeeded';
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
|
throw new Error('XSS Succeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagram = `classDiagram
|
||||||
|
Class "<img/src='x'/onerror=xssAttack(1)>" <--> "<img/src='x'/onerror=xssAttack(2)>" C2: Cool label`;
|
||||||
|
|
||||||
|
// // var diagram = "stateDiagram-v2\n";
|
||||||
|
// // diagram += "<img/src='1'/onerror"
|
||||||
|
// diagram += '//via.placeholder.com/64\' width=64 />"]';
|
||||||
|
// console.log(diagram);
|
||||||
|
// document.querySelector('#diagram').innerHTML = diagram;
|
||||||
|
mermaid.render('diagram', diagram, (res) => {
|
||||||
|
console.log(res);
|
||||||
|
document.querySelector('#res').innerHTML = res;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@@ -37,6 +37,13 @@ https://codepen.io/janzeteachesit/pen/OWWZKN
|
|||||||
|
|
||||||
https://codepen.io/Ryuno-Ki/pen/LNxwgR
|
https://codepen.io/Ryuno-Ki/pen/LNxwgR
|
||||||
|
|
||||||
|
## Mermaid in open source docs
|
||||||
|
|
||||||
|
[K8s.io Diagram Guide](https://kubernetes.io/docs/contribute/style/diagram-guide/)
|
||||||
|
|
||||||
|
[K8s.dev blog: Improve your documentation with Mermaid.js diagrams](https://www.kubernetes.dev/blog/2021/12/01/improve-your-documentation-with-mermaid.js-diagrams/)
|
||||||
|
|
||||||
|
|
||||||
## Jupyter Integration with mermaid-js
|
## Jupyter Integration with mermaid-js
|
||||||
|
|
||||||
Here's an example of Python integration with mermaid-js which uses the mermaid.ink service, that displays the graph in a Jupyter notebook.
|
Here's an example of Python integration with mermaid-js which uses the mermaid.ink service, that displays the graph in a Jupyter notebook.
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
- 📔 Introduction
|
- 📔 Introduction
|
||||||
|
|
||||||
- [About Mermaid](README.md)
|
- [About Mermaid](README.md)
|
||||||
- [Deployment](n00b-gettingStarted.md)
|
- [Deployment](n00b-gettingStarted.md)
|
||||||
- [Syntax and Configuration](n00b-syntaxReference.md)
|
- [Syntax and Configuration](n00b-syntaxReference.md)
|
||||||
|
|
||||||
- 📊 Diagram Syntax
|
- 📊 Diagram Syntax
|
||||||
- [Flowchart](flowchart.md)
|
- [Flowchart](flowchart.md)
|
||||||
- [Sequence diagram](sequenceDiagram.md)
|
- [Sequence diagram](sequenceDiagram.md)
|
||||||
- [Class Diagram](classDiagram.md)
|
- [Class Diagram](classDiagram.md)
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
- [Requirement Diagram](requirementDiagram.md)
|
- [Requirement Diagram](requirementDiagram.md)
|
||||||
- [Other Examples](examples.md)
|
- [Other Examples](examples.md)
|
||||||
|
|
||||||
- ⚙️ Deployment and Configuration
|
- ⚙️ Deployment and Configuration
|
||||||
|
|
||||||
- [Tutorials](Tutorials.md)
|
- [Tutorials](Tutorials.md)
|
||||||
- [API-Usage](usage.md)
|
- [API-Usage](usage.md)
|
||||||
@@ -26,12 +26,13 @@
|
|||||||
- [Mermaid CLI](mermaidCLI.md)
|
- [Mermaid CLI](mermaidCLI.md)
|
||||||
- [Advanced usage](n00b-advanced.md)
|
- [Advanced usage](n00b-advanced.md)
|
||||||
|
|
||||||
- 📚 Misc
|
- 📚 Misc
|
||||||
- [Use-Cases and Integrations](integrations.md)
|
- [Use-Cases and Integrations](integrations.md)
|
||||||
- [FAQ](faq.md)
|
- [FAQ](faq.md)
|
||||||
|
|
||||||
- 🙌 Contributions and Community
|
- 🙌 Contributions and Community
|
||||||
- [Overview for Beginners](n00b-overview.md)
|
- [Overview for Beginners](n00b-overview.md)
|
||||||
- [Development and Contribution ](development.md)
|
- [Development and Contribution ](development.md)
|
||||||
- [Changelog](CHANGELOG.md)
|
- [Changelog](CHANGELOG.md)
|
||||||
- [Adding Diagrams ](newDiagram.md)
|
- [Adding Diagrams ](newDiagram.md)
|
||||||
|
- [Security ](security.md)
|
||||||
|
@@ -137,20 +137,20 @@ The `type` and `name` values must begin with an alphabetic character and may con
|
|||||||
|
|
||||||
#### Attribute Keys and Comments
|
#### Attribute Keys and Comments
|
||||||
|
|
||||||
Attributes may also have a `key` or comment defined. Keys can be "PK" or "FK", for Primary Key or Foreign Key. And a `comment` is defined by quotes at the end of an attribute. Comments themselves cannot have quote characters in them.
|
Attributes may also have a `key` or comment defined. Keys can be "PK" or "FK", for Primary Key or Foreign Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
erDiagram
|
erDiagram
|
||||||
CAR ||--o{ NAMED-DRIVER : allows
|
CAR ||--o{ NAMED-DRIVER : allows
|
||||||
CAR {
|
CAR {
|
||||||
string allowedDriver FK 'The license of the allowed driver'
|
string allowedDriver FK "The license of the allowed driver"
|
||||||
string registrationNumber
|
string registrationNumber
|
||||||
string make
|
string make
|
||||||
string model
|
string model
|
||||||
}
|
}
|
||||||
PERSON ||--o{ NAMED-DRIVER : is
|
PERSON ||--o{ NAMED-DRIVER : is
|
||||||
PERSON {
|
PERSON {
|
||||||
string driversLicense PK 'The license #'
|
string driversLicense PK "The license #"
|
||||||
string firstName
|
string firstName
|
||||||
string lastName
|
string lastName
|
||||||
int age
|
int age
|
||||||
|
@@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
|
|
||||||
It is important to remember that when a date, day, or collection of dates specific to a task are "excluded", the Gantt Chart will accomodate those changes by extending an equal number of days, towards the right, not by creating a gap inside the task.
|
It is important to remember that when a date, day, or collection of dates specific to a task are "excluded", the Gantt Chart will accomodate those changes by extending an equal number of days, towards the right, not by creating a gap inside the task.
|
||||||
As shown here 
|
As shown here 
|
||||||
|
|
||||||
|
|
||||||
However, if the excluded dates are between two tasks that are set to start consecutively, the excluded dates will be skipped graphically and left blank, and the following task will begin after the end of the excluded dates.
|
However, if the excluded dates are between two tasks that are set to start consecutively, the excluded dates will be skipped graphically and left blank, and the following task will begin after the end of the excluded dates.
|
||||||
As shown here 
|
As shown here 
|
||||||
|
|
||||||
A Gantt chart is useful for tracking the amount of time it would take before a project is finished, but it can also be used to graphically represent "non-working days", with a few tweaks.
|
A Gantt chart is useful for tracking the amount of time it would take before a project is finished, but it can also be used to graphically represent "non-working days", with a few tweaks.
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||||
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
||||||
- [Joplin](https://joplinapp.org) (**Native support**)
|
- [Joplin](https://joplinapp.org) (**Native support**)
|
||||||
|
- [Notion](https://notion.so) (**Native support**)
|
||||||
- [GitHub](https://github.com)
|
- [GitHub](https://github.com)
|
||||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||||
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
||||||
@@ -74,7 +75,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Flowcharts](https://www.dokuwiki.org/plugin:flowcharts?s[]=mermaid)
|
- [Flowcharts](https://www.dokuwiki.org/plugin:flowcharts?s[]=mermaid)
|
||||||
- [ComboStrap](https://combostrap.com/mermaid)
|
- [ComboStrap](https://combostrap.com/mermaid)
|
||||||
- [TiddlyWiki](https://tiddlywiki.com/)
|
- [TiddlyWiki](https://tiddlywiki.com/)
|
||||||
- [mermaid-tw5-plugin](https://github.com/michaeljmcd/mermaid-tw5-plugin)
|
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
|
||||||
|
- [tw5-mermaid: wrapper for Mermaid Live](https://github.com/jasonmhoule/tw5-mermaid)
|
||||||
|
|
||||||
## Editor Plugins
|
## Editor Plugins
|
||||||
|
|
||||||
|
@@ -95,7 +95,10 @@ c. The `mermaid.initialize()` call, which dictates the appearance of diagrams an
|
|||||||
<body>
|
<body>
|
||||||
Here is a mermaid diagram:
|
Here is a mermaid diagram:
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD A[Client] --> B[Load Balancer] B --> C[Server01] B --> D[Server02]
|
graph TD
|
||||||
|
A[Client] --> B[Load Balancer]
|
||||||
|
B --> C[Server01]
|
||||||
|
B --> D[Server02]
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
```
|
```
|
||||||
@@ -137,13 +140,19 @@ Rendering in Mermaid is initalized by `mermaid.initialize()` call. You can place
|
|||||||
|
|
||||||
Here is one mermaid diagram:
|
Here is one mermaid diagram:
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD A[Client] --> B[Load Balancer] B --> C[Server1] B --> D[Server2]
|
graph TD
|
||||||
|
A[Client] --> B[Load Balancer]
|
||||||
|
B --> C[Server1]
|
||||||
|
B --> D[Server2]
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
And here is another:
|
And here is another:
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD A[Client] -->|tcp_123| B(Load Balancer) B -->|tcp_456| C[Server1] B
|
graph TD
|
||||||
-->|tcp_456| D[Server2]
|
A[Client] -->|tcp_123|
|
||||||
|
B(Load Balancer)
|
||||||
|
B -->|tcp_456| C[Server1]
|
||||||
|
B -->|tcp_456| D[Server2]
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -159,10 +168,16 @@ In this example mermaid.js is referenced in `src` as a separate JavaScript file,
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph LR A --- B B-->C[fa:fa-ban forbidden] B-->D(fa:fa-spinner);
|
graph LR
|
||||||
|
A --- B
|
||||||
|
B-->C[fa:fa-ban forbidden]
|
||||||
|
B-->D(fa:fa-spinner);
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid">
|
<div class="mermaid">
|
||||||
graph TD A[Client] --> B[Load Balancer] B --> C[Server1] B --> D[Server2]
|
graph TD
|
||||||
|
A[Client] --> B[Load Balancer]
|
||||||
|
B --> C[Server1]
|
||||||
|
B --> D[Server2]
|
||||||
</div>
|
</div>
|
||||||
<script src="The\Path\In\Your\Package\mermaid.js"></script>
|
<script src="The\Path\In\Your\Package\mermaid.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
17
docs/security.md
Normal file
17
docs/security.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Security
|
||||||
|
The Mermaid team takes the security of Mermaid and the applications that use Mermaid seriously. This page describes how to report any vulnerabilities you may find, and lists best practices to minimize the risk of introducing a vulnerability.
|
||||||
|
|
||||||
|
## Reporting vulnerabilities
|
||||||
|
To report a vulnerability, please e-mail security@mermaid.live with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue.
|
||||||
|
|
||||||
|
We aim to reply within three working days, probably much sooner.
|
||||||
|
|
||||||
|
You should expect a close collaboration as we work to resolve the issue you have reported. Please reach out to security@mermaid.live again if you do not receive prompt attention and regular updates.
|
||||||
|
|
||||||
|
You may also reach out to the team via our public Slack chat channels; however, please make sure to e-mail security@mernaid.live when reporting an issue, and avoid revealing information about vulnerabilities in public as that could that could put users at risk.
|
||||||
|
|
||||||
|
## Best practices
|
||||||
|
|
||||||
|
Keep current with the latest Mermaid releases. We regularly update Mermaid, and these updates may fix security defects discovered in previous versions. Check the Mermaid release notes for security-related updates.
|
||||||
|
|
||||||
|
Keep your application’s dependencies up to date. Make sure you upgrade your package dependencies to keep the dependencies up to date. Avoid pinning to specific versions for your dependencies and, if you do, make sure you check periodically to see if your dependencies have had security updates, and update the pin accordingly.
|
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "8.13.5",
|
"version": "8.13.9",
|
||||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"main": "dist/mermaid.core.js",
|
"main": "dist/mermaid.core.js",
|
||||||
"module": "dist/mermaid.esm.min.mjs",
|
"module": "dist/mermaid.esm.min.mjs",
|
||||||
@@ -72,17 +72,17 @@
|
|||||||
"@babel/eslint-parser": "^7.14.7",
|
"@babel/eslint-parser": "^7.14.7",
|
||||||
"@babel/preset-env": "^7.14.7",
|
"@babel/preset-env": "^7.14.7",
|
||||||
"@babel/register": "^7.14.5",
|
"@babel/register": "^7.14.5",
|
||||||
"@commitlint/cli": "^15.0.0",
|
"@commitlint/cli": "^16.0.0",
|
||||||
"@commitlint/config-conventional": "^15.0.0",
|
"@commitlint/config-conventional": "^16.0.0",
|
||||||
"@percy/cli": "^1.0.0-beta.58",
|
"@percy/cli": "^1.0.0-beta.58",
|
||||||
"@percy/cypress": "^3.1.0",
|
"@percy/cypress": "^3.1.0",
|
||||||
"@percy/migrate": "^0.11.0",
|
"@percy/migrate": "^0.11.0",
|
||||||
"babel-jest": "^27.0.6",
|
"babel-jest": "^27.0.6",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"concurrently": "^6.2.2",
|
"concurrently": "^7.0.0",
|
||||||
"coveralls": "^3.0.2",
|
"coveralls": "^3.0.2",
|
||||||
"css-to-string-loader": "^0.1.3",
|
"css-to-string-loader": "^0.1.3",
|
||||||
"cypress": "9.1.1",
|
"cypress": "9.2.0",
|
||||||
"documentation": "13.2.0",
|
"documentation": "13.2.0",
|
||||||
"eslint": "^8.2.0",
|
"eslint": "^8.2.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
@@ -35,7 +35,7 @@ const config = {
|
|||||||
theme: 'default',
|
theme: 'default',
|
||||||
themeVariables: theme['default'].getThemeVariables(),
|
themeVariables: theme['default'].getThemeVariables(),
|
||||||
themeCSS: undefined,
|
themeCSS: undefined,
|
||||||
/* **maxTextSize** - The maximum allowed size of the users text diamgram */
|
/* **maxTextSize** - The maximum allowed size of the users text diagram */
|
||||||
maxTextSize: 50000,
|
maxTextSize: 50000,
|
||||||
darkMode: false,
|
darkMode: false,
|
||||||
|
|
||||||
|
@@ -96,6 +96,16 @@ export const addRelation = function (relation) {
|
|||||||
relation.id1 = splitClassNameAndType(relation.id1).className;
|
relation.id1 = splitClassNameAndType(relation.id1).className;
|
||||||
relation.id2 = splitClassNameAndType(relation.id2).className;
|
relation.id2 = splitClassNameAndType(relation.id2).className;
|
||||||
|
|
||||||
|
relation.relationTitle1 = common.sanitizeText(
|
||||||
|
relation.relationTitle1.trim(),
|
||||||
|
configApi.getConfig()
|
||||||
|
);
|
||||||
|
|
||||||
|
relation.relationTitle2 = common.sanitizeText(
|
||||||
|
relation.relationTitle2.trim(),
|
||||||
|
configApi.getConfig()
|
||||||
|
);
|
||||||
|
|
||||||
relations.push(relation);
|
relations.push(relation);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -13,6 +13,24 @@ export const getRows = (s) => {
|
|||||||
return str.split('#br#');
|
return str.split('#br#');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const removeEscapes = (text) => {
|
||||||
|
let newStr = text.replace(/\\u[\dA-F]{4}/gi, function (match) {
|
||||||
|
return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(newStr);
|
||||||
|
|
||||||
|
newStr = newStr.replace(/\\x([0-9a-f]{2})/gi, (_, c) => String.fromCharCode(parseInt(c, 16)));
|
||||||
|
newStr = newStr.replace(/\\[\d\d\d]{3}/gi, function (match) {
|
||||||
|
return String.fromCharCode(parseInt(match.replace(/\\/g, ''), 8));
|
||||||
|
});
|
||||||
|
newStr = newStr.replace(/\\[\d\d\d]{2}/gi, function (match) {
|
||||||
|
return String.fromCharCode(parseInt(match.replace(/\\/g, ''), 8));
|
||||||
|
});
|
||||||
|
|
||||||
|
return newStr;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes script tags from a text
|
* Removes script tags from a text
|
||||||
*
|
*
|
||||||
@@ -40,13 +58,12 @@ export const removeScript = (txt) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let decodedText = removeEscapes(rs);
|
||||||
rs = rs.replace(/script>/gi, '#');
|
decodedText = decodedText.replace(/script>/gi, '#');
|
||||||
rs = rs.replace(/script>/gi, '#');
|
decodedText = decodedText.replace(/javascript:/gi, '#');
|
||||||
rs = rs.replace(/javascript:/gi, '#');
|
decodedText = decodedText.replace(/onerror=/gi, 'onerror:');
|
||||||
rs = rs.replace(/onerror=/gi, 'onerror:');
|
decodedText = decodedText.replace(/<iframe/gi, '');
|
||||||
rs = rs.replace(/<iframe/gi, '');
|
return decodedText;
|
||||||
return rs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sanitizeMore = (text, config) => {
|
const sanitizeMore = (text, config) => {
|
||||||
@@ -62,7 +79,7 @@ const sanitizeMore = (text, config) => {
|
|||||||
if (htmlLabels) {
|
if (htmlLabels) {
|
||||||
const level = config.securityLevel;
|
const level = config.securityLevel;
|
||||||
|
|
||||||
if (level === 'antiscript') {
|
if (level === 'antiscript' || level === 'strict') {
|
||||||
txt = removeScript(txt);
|
txt = removeScript(txt);
|
||||||
} else if (level !== 'loose') {
|
} else if (level !== 'loose') {
|
||||||
// eslint-disable-line
|
// eslint-disable-line
|
||||||
@@ -171,4 +188,5 @@ export default {
|
|||||||
removeScript,
|
removeScript,
|
||||||
getUrl,
|
getUrl,
|
||||||
evaluate,
|
evaluate,
|
||||||
|
removeEscapes,
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { removeScript } from './common';
|
import { sanitizeText, removeScript, removeEscapes } from './common';
|
||||||
|
|
||||||
describe('when securityLevel is antiscript, all script must be removed', function () {
|
describe('when securityLevel is antiscript, all script must be removed', function () {
|
||||||
it('should remove all script block, script inline.', function () {
|
it('should remove all script block, script inline.', function () {
|
||||||
@@ -24,3 +24,60 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
|||||||
expect(isEqual).toEqual(true);
|
expect(isEqual).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('remove escape code in text', function () {
|
||||||
|
it('should remove a unicode colon', function () {
|
||||||
|
const labelString = '\\u003A';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual(':');
|
||||||
|
});
|
||||||
|
it('should remove a hex colon', function () {
|
||||||
|
const labelString = '\\x3A';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual(':');
|
||||||
|
});
|
||||||
|
it('should remove a oct colon', function () {
|
||||||
|
const labelString = '\\72';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual(':');
|
||||||
|
});
|
||||||
|
it('should remove a oct colon 3 numbers', function () {
|
||||||
|
const labelString = '\\072';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual(':');
|
||||||
|
});
|
||||||
|
it('should remove multiple colons 3 numbers', function () {
|
||||||
|
const labelString = '\\072\\072\\72';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual(':::');
|
||||||
|
});
|
||||||
|
it('should handle greater and smaller then', function () {
|
||||||
|
const labelString = '\\74\\076';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual('<>');
|
||||||
|
});
|
||||||
|
it('should handle letters', function () {
|
||||||
|
const labelString = '\\u0073\\143ri\\x70\\u0074\\x3A';
|
||||||
|
|
||||||
|
const result = removeEscapes(labelString);
|
||||||
|
expect(result).toEqual('script:');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Sanitize text', function () {
|
||||||
|
it('should remove script tag', function () {
|
||||||
|
const maliciousStr = 'javajavascript:script:alert(1)';
|
||||||
|
const result = sanitizeText(maliciousStr, {
|
||||||
|
securityLevel: 'strict',
|
||||||
|
flowchart: { htmlLabels: true },
|
||||||
|
});
|
||||||
|
console.log('result', result);
|
||||||
|
expect(result).not.toContain('javascript:alert(1)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@@ -40,7 +40,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
const attributeNodes = []; // Intermediate storage for attribute nodes created so that we can do a second pass
|
const attributeNodes = []; // Intermediate storage for attribute nodes created so that we can do a second pass
|
||||||
let hasKeyType = false;
|
let hasKeyType = false;
|
||||||
let hasComment = false;
|
let hasComment = false;
|
||||||
let maxWidth = 0;
|
|
||||||
let maxTypeWidth = 0;
|
let maxTypeWidth = 0;
|
||||||
let maxNameWidth = 0;
|
let maxNameWidth = 0;
|
||||||
let maxKeyWidth = 0;
|
let maxKeyWidth = 0;
|
||||||
@@ -48,9 +47,19 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
let cumulativeHeight = labelBBox.height + heightPadding * 2;
|
let cumulativeHeight = labelBBox.height + heightPadding * 2;
|
||||||
let attrNum = 1;
|
let attrNum = 1;
|
||||||
|
|
||||||
|
// Check to see if any of the attributes has a key or a comment
|
||||||
|
attributes.forEach((item) => {
|
||||||
|
if (item.attributeKeyType !== undefined) {
|
||||||
|
hasKeyType = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.attributeComment !== undefined) {
|
||||||
|
hasComment = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
attributes.forEach((item) => {
|
attributes.forEach((item) => {
|
||||||
const attrPrefix = `${entityTextNode.node().id}-attr-${attrNum}`;
|
const attrPrefix = `${entityTextNode.node().id}-attr-${attrNum}`;
|
||||||
let nodeWidth = 0;
|
|
||||||
let nodeHeight = 0;
|
let nodeHeight = 0;
|
||||||
|
|
||||||
// Add a text node for the attribute type
|
// Add a text node for the attribute type
|
||||||
@@ -91,16 +100,14 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
const nameBBox = nameNode.node().getBBox();
|
const nameBBox = nameNode.node().getBBox();
|
||||||
maxTypeWidth = Math.max(maxTypeWidth, typeBBox.width);
|
maxTypeWidth = Math.max(maxTypeWidth, typeBBox.width);
|
||||||
maxNameWidth = Math.max(maxNameWidth, nameBBox.width);
|
maxNameWidth = Math.max(maxNameWidth, nameBBox.width);
|
||||||
nodeWidth += typeBBox.width;
|
|
||||||
nodeWidth += nameBBox.width;
|
|
||||||
|
|
||||||
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
|
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
|
||||||
|
|
||||||
if (hasKeyType || item.attributeKeyType !== undefined) {
|
if (hasKeyType) {
|
||||||
const keyTypeNode = groupNode
|
const keyTypeNode = groupNode
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('class', 'er entityLabel')
|
.attr('class', 'er entityLabel')
|
||||||
.attr('id', `${attrPrefix}-name`)
|
.attr('id', `${attrPrefix}-key`)
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', 0)
|
.attr('y', 0)
|
||||||
.attr('dominant-baseline', 'middle')
|
.attr('dominant-baseline', 'middle')
|
||||||
@@ -113,17 +120,15 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
|
|
||||||
attributeNode.kn = keyTypeNode;
|
attributeNode.kn = keyTypeNode;
|
||||||
const keyTypeBBox = keyTypeNode.node().getBBox();
|
const keyTypeBBox = keyTypeNode.node().getBBox();
|
||||||
nodeWidth += keyTypeBBox.width;
|
maxKeyWidth = Math.max(maxKeyWidth, keyTypeBBox.width);
|
||||||
maxKeyWidth = Math.max(maxKeyWidth, nodeWidth);
|
|
||||||
nodeHeight = Math.max(nodeHeight, keyTypeBBox.height);
|
nodeHeight = Math.max(nodeHeight, keyTypeBBox.height);
|
||||||
hasKeyType = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasComment || item.attributeComment !== undefined) {
|
if (hasComment) {
|
||||||
const commentNode = groupNode
|
const commentNode = groupNode
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('class', 'er entityLabel')
|
.attr('class', 'er entityLabel')
|
||||||
.attr('id', `${attrPrefix}-name`)
|
.attr('id', `${attrPrefix}-comment`)
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', 0)
|
.attr('y', 0)
|
||||||
.attr('dominant-baseline', 'middle')
|
.attr('dominant-baseline', 'middle')
|
||||||
@@ -136,25 +141,35 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
|
|
||||||
attributeNode.cn = commentNode;
|
attributeNode.cn = commentNode;
|
||||||
const commentNodeBBox = commentNode.node().getBBox();
|
const commentNodeBBox = commentNode.node().getBBox();
|
||||||
nodeWidth += commentNodeBBox.width;
|
maxCommentWidth = Math.max(maxCommentWidth, commentNodeBBox.width);
|
||||||
maxCommentWidth = Math.max(nodeWidth, nameBBox.width);
|
|
||||||
nodeHeight = Math.max(nodeHeight, commentNodeBBox.height);
|
nodeHeight = Math.max(nodeHeight, commentNodeBBox.height);
|
||||||
hasComment = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeNode.height = nodeHeight;
|
attributeNode.height = nodeHeight;
|
||||||
// Keep a reference to the nodes so that we can iterate through them later
|
// Keep a reference to the nodes so that we can iterate through them later
|
||||||
attributeNodes.push(attributeNode);
|
attributeNodes.push(attributeNode);
|
||||||
maxWidth = Math.max(maxWidth, nodeWidth);
|
|
||||||
cumulativeHeight += nodeHeight + heightPadding * 2;
|
cumulativeHeight += nodeHeight + heightPadding * 2;
|
||||||
attrNum += 1;
|
attrNum += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let widthPaddingFactor = 4;
|
||||||
|
if (hasKeyType) {
|
||||||
|
widthPaddingFactor += 2;
|
||||||
|
}
|
||||||
|
if (hasComment) {
|
||||||
|
widthPaddingFactor += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxWidth = maxTypeWidth + maxNameWidth + maxKeyWidth + maxCommentWidth;
|
||||||
|
|
||||||
// Calculate the new bounding box of the overall entity, now that attributes have been added
|
// Calculate the new bounding box of the overall entity, now that attributes have been added
|
||||||
const bBox = {
|
const bBox = {
|
||||||
width: Math.max(
|
width: Math.max(
|
||||||
conf.minEntityWidth,
|
conf.minEntityWidth,
|
||||||
Math.max(labelBBox.width + conf.entityPadding * 2, maxWidth + widthPadding * 4)
|
Math.max(
|
||||||
|
labelBBox.width + conf.entityPadding * 2,
|
||||||
|
maxWidth + widthPadding * widthPaddingFactor
|
||||||
|
)
|
||||||
),
|
),
|
||||||
height:
|
height:
|
||||||
attributes.length > 0
|
attributes.length > 0
|
||||||
@@ -162,10 +177,13 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
: Math.max(conf.minEntityHeight, labelBBox.height + conf.entityPadding * 2),
|
: Math.max(conf.minEntityHeight, labelBBox.height + conf.entityPadding * 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
// There might be some spare width for padding out attributes if the entity name is very long
|
|
||||||
const spareWidth = Math.max(0, bBox.width - maxWidth - widthPadding * 4);
|
|
||||||
|
|
||||||
if (attributes.length > 0) {
|
if (attributes.length > 0) {
|
||||||
|
// There might be some spare width for padding out attributes if the entity name is very long
|
||||||
|
const spareColumnWidth = Math.max(
|
||||||
|
0,
|
||||||
|
(bBox.width - maxWidth - widthPadding * widthPaddingFactor) / (widthPaddingFactor / 2)
|
||||||
|
);
|
||||||
|
|
||||||
// Position the entity label near the top of the entity bounding box
|
// Position the entity label near the top of the entity bounding box
|
||||||
entityTextNode.attr(
|
entityTextNode.attr(
|
||||||
'transform',
|
'transform',
|
||||||
@@ -180,9 +198,10 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
// Calculate the alignment y co-ordinate for the type/name of the attribute
|
// Calculate the alignment y co-ordinate for the type/name of the attribute
|
||||||
const alignY = heightOffset + heightPadding + attributeNode.height / 2;
|
const alignY = heightOffset + heightPadding + attributeNode.height / 2;
|
||||||
|
|
||||||
// Position the type of the attribute
|
// Position the type attribute
|
||||||
attributeNode.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
|
attributeNode.tn.attr('transform', 'translate(' + widthPadding + ',' + alignY + ')');
|
||||||
|
|
||||||
|
// TODO Handle spareWidth in attr('width')
|
||||||
// Insert a rectangle for the type
|
// Insert a rectangle for the type
|
||||||
const typeRect = groupNode
|
const typeRect = groupNode
|
||||||
.insert('rect', '#' + attributeNode.tn.node().id)
|
.insert('rect', '#' + attributeNode.tn.node().id)
|
||||||
@@ -192,65 +211,73 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
|||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
.attr('y', heightOffset)
|
.attr('y', heightOffset)
|
||||||
.attr('width', maxTypeWidth * 2 + spareWidth / 2)
|
.attr('width', maxTypeWidth + widthPadding * 2 + spareColumnWidth)
|
||||||
.attr('height', attributeNode.tn.node().getBBox().height + heightPadding * 2);
|
.attr('height', attributeNode.height + heightPadding * 2);
|
||||||
|
|
||||||
// Position the name of the attribute
|
const nameXOffset = parseFloat(typeRect.attr('x')) + parseFloat(typeRect.attr('width'));
|
||||||
|
|
||||||
|
// Position the name attribute
|
||||||
attributeNode.nn.attr(
|
attributeNode.nn.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
'translate(' + (nameXOffset + widthPadding) + ',' + alignY + ')'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert a rectangle for the name
|
// Insert a rectangle for the name
|
||||||
groupNode
|
const nameRect = groupNode
|
||||||
.insert('rect', '#' + attributeNode.nn.node().id)
|
.insert('rect', '#' + attributeNode.nn.node().id)
|
||||||
.attr('class', `er ${attribStyle}`)
|
.attr('class', `er ${attribStyle}`)
|
||||||
.attr('fill', conf.fill)
|
.attr('fill', conf.fill)
|
||||||
.attr('fill-opacity', '100%')
|
.attr('fill-opacity', '100%')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
.attr('x', nameXOffset)
|
||||||
.attr('y', heightOffset)
|
.attr('y', heightOffset)
|
||||||
.attr('width', maxNameWidth + widthPadding * 2 + spareWidth / 2)
|
.attr('width', maxNameWidth + widthPadding * 2 + spareColumnWidth)
|
||||||
.attr('height', attributeNode.nn.node().getBBox().height + heightPadding * 2);
|
.attr('height', attributeNode.height + heightPadding * 2);
|
||||||
|
|
||||||
|
let keyTypeAndCommentXOffset =
|
||||||
|
parseFloat(nameRect.attr('x')) + parseFloat(nameRect.attr('width'));
|
||||||
|
|
||||||
if (hasKeyType) {
|
if (hasKeyType) {
|
||||||
// Position the name of the attribute
|
// Position the key type attribute
|
||||||
attributeNode.kn.attr(
|
attributeNode.kn.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
'translate(' + (keyTypeAndCommentXOffset + widthPadding) + ',' + alignY + ')'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert a rectangle for the name
|
// Insert a rectangle for the key type
|
||||||
groupNode
|
const keyTypeRect = groupNode
|
||||||
.insert('rect', '#' + attributeNode.kn.node().id)
|
.insert('rect', '#' + attributeNode.kn.node().id)
|
||||||
.attr('class', `er ${attribStyle}`)
|
.attr('class', `er ${attribStyle}`)
|
||||||
.attr('fill', conf.fill)
|
.attr('fill', conf.fill)
|
||||||
.attr('fill-opacity', '100%')
|
.attr('fill-opacity', '100%')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
.attr('x', keyTypeAndCommentXOffset)
|
||||||
.attr('y', heightOffset)
|
.attr('y', heightOffset)
|
||||||
.attr('width', maxKeyWidth + widthPadding * 2 + spareWidth / 2)
|
.attr('width', maxKeyWidth + widthPadding * 2 + spareColumnWidth)
|
||||||
.attr('height', attributeNode.kn.node().getBBox().height + heightPadding * 2);
|
.attr('height', attributeNode.height + heightPadding * 2);
|
||||||
|
|
||||||
|
keyTypeAndCommentXOffset =
|
||||||
|
parseFloat(keyTypeRect.attr('x')) + parseFloat(keyTypeRect.attr('width'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasComment) {
|
if (hasComment) {
|
||||||
// Position the name of the attribute
|
// Position the comment attribute
|
||||||
attributeNode.cn.attr(
|
attributeNode.cn.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' + (parseFloat(typeRect.attr('width')) + widthPadding) + ',' + alignY + ')'
|
'translate(' + (keyTypeAndCommentXOffset + widthPadding) + ',' + alignY + ')'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert a rectangle for the name
|
// Insert a rectangle for the comment
|
||||||
groupNode
|
groupNode
|
||||||
.insert('rect', '#' + attributeNode.cn.node().id)
|
.insert('rect', '#' + attributeNode.cn.node().id)
|
||||||
.attr('class', `er ${attribStyle}`)
|
.attr('class', `er ${attribStyle}`)
|
||||||
.attr('fill', conf.fill)
|
.attr('fill', conf.fill)
|
||||||
.attr('fill-opacity', '100%')
|
.attr('fill-opacity', '100%')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('x', `${typeRect.attr('x') + typeRect.attr('width')}`)
|
.attr('x', keyTypeAndCommentXOffset)
|
||||||
.attr('y', heightOffset)
|
.attr('y', heightOffset)
|
||||||
.attr('width', maxCommentWidth + widthPadding * 2 + spareWidth / 2)
|
.attr('width', maxCommentWidth + widthPadding * 2 + spareColumnWidth)
|
||||||
.attr('height', attributeNode.cn.node().getBBox().height + heightPadding * 2);
|
.attr('height', attributeNode.height + heightPadding * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the height offset to move to the next row
|
// Increment the height offset to move to the next row
|
||||||
|
@@ -98,8 +98,8 @@ attributes
|
|||||||
attribute
|
attribute
|
||||||
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
|
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
|
||||||
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
|
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
|
||||||
| attributeType attributeName COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
|
| attributeType attributeName attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
|
||||||
| attributeType attributeName attributeKeyType COMMENT { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
|
| attributeType attributeName attributeKeyType attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
|
||||||
;
|
;
|
||||||
|
|
||||||
attributeType
|
attributeType
|
||||||
@@ -114,6 +114,10 @@ attributeKeyType
|
|||||||
: ATTRIBUTE_KEY { $$=$1; }
|
: ATTRIBUTE_KEY { $$=$1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
attributeComment
|
||||||
|
: COMMENT { $$=$1.replace(/"/g, ''); }
|
||||||
|
;
|
||||||
|
|
||||||
relSpec
|
relSpec
|
||||||
: cardinality relType cardinality
|
: cardinality relType cardinality
|
||||||
{
|
{
|
||||||
|
@@ -59,6 +59,7 @@ describe('when parsing ER diagram it...', function () {
|
|||||||
const entities = erDb.getEntities();
|
const entities = erDb.getEntities();
|
||||||
expect(Object.keys(entities).length).toBe(1);
|
expect(Object.keys(entities).length).toBe(1);
|
||||||
expect(entities[entity].attributes.length).toBe(1);
|
expect(entities[entity].attributes.length).toBe(1);
|
||||||
|
expect(entities[entity].attributes[0].attributeComment).toBe('comment');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow an entity with a single attribute to be defined with a key and a comment', function () {
|
it('should allow an entity with a single attribute to be defined with a key and a comment', function () {
|
||||||
|
@@ -17,7 +17,9 @@ function getId() {
|
|||||||
*/
|
*/
|
||||||
function isfastforwardable(currentCommit, otherCommit) {
|
function isfastforwardable(currentCommit, otherCommit) {
|
||||||
log.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id);
|
log.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id);
|
||||||
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit) {
|
let cnt = 0;
|
||||||
|
while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit && cnt < 1000) {
|
||||||
|
cnt++;
|
||||||
// only if other branch has more commits
|
// only if other branch has more commits
|
||||||
if (otherCommit.parent == null) break;
|
if (otherCommit.parent == null) break;
|
||||||
if (Array.isArray(otherCommit.parent)) {
|
if (Array.isArray(otherCommit.parent)) {
|
||||||
|
@@ -227,7 +227,9 @@ function renderCommitHistory(svg, commitid, branches, direction) {
|
|||||||
let commit;
|
let commit;
|
||||||
const numCommits = Object.keys(allCommitsDict).length;
|
const numCommits = Object.keys(allCommitsDict).length;
|
||||||
if (typeof commitid === 'string') {
|
if (typeof commitid === 'string') {
|
||||||
|
let cnt = 0;
|
||||||
do {
|
do {
|
||||||
|
cnt++;
|
||||||
commit = allCommitsDict[commitid];
|
commit = allCommitsDict[commitid];
|
||||||
log.debug('in renderCommitHistory', commit.id, commit.seq);
|
log.debug('in renderCommitHistory', commit.id, commit.seq);
|
||||||
if (svg.select('#node-' + commitid).size() > 0) {
|
if (svg.select('#node-' + commitid).size() > 0) {
|
||||||
@@ -293,7 +295,7 @@ function renderCommitHistory(svg, commitid, branches, direction) {
|
|||||||
.text(', ' + commit.message);
|
.text(', ' + commit.message);
|
||||||
}
|
}
|
||||||
commitid = commit.parent;
|
commitid = commit.parent;
|
||||||
} while (commitid && allCommitsDict[commitid]);
|
} while (commitid && allCommitsDict[commitid] && cnt < 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(commitid)) {
|
if (Array.isArray(commitid)) {
|
||||||
@@ -313,7 +315,9 @@ function renderCommitHistory(svg, commitid, branches, direction) {
|
|||||||
*/
|
*/
|
||||||
function renderLines(svg, commit, direction, branchColor) {
|
function renderLines(svg, commit, direction, branchColor) {
|
||||||
branchColor = branchColor || 0;
|
branchColor = branchColor || 0;
|
||||||
while (commit.seq > 0 && !commit.lineDrawn) {
|
let cnt = 0;
|
||||||
|
while (commit.seq > 0 && !commit.lineDrawn && cnt < 1000) {
|
||||||
|
cnt++;
|
||||||
if (typeof commit.parent === 'string') {
|
if (typeof commit.parent === 'string') {
|
||||||
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor);
|
svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor);
|
||||||
commit.lineDrawn = true;
|
commit.lineDrawn = true;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import common from '../common/common';
|
import common from '../common/common';
|
||||||
import { addFunction } from '../../interactionDb';
|
import { addFunction } from '../../interactionDb';
|
||||||
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
const rectElem = elem.append('rect');
|
const rectElem = elem.append('rect');
|
||||||
@@ -19,12 +20,12 @@ export const drawRect = function (elem, rectData) {
|
|||||||
return rectElem;
|
return rectElem;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sanitizeUrl = function (s) {
|
// const sanitizeUrl = function (s) {
|
||||||
return s
|
// return s
|
||||||
.replace(/&/g, '&')
|
// .replace(/&/g, '&')
|
||||||
.replace(/</g, '<')
|
// .replace(/</g, '<')
|
||||||
.replace(/javascript:/g, '');
|
// .replace(/javascript:/g, '');
|
||||||
};
|
// };
|
||||||
|
|
||||||
const addPopupInteraction = (id, actorCnt) => {
|
const addPopupInteraction = (id, actorCnt) => {
|
||||||
addFunction(() => {
|
addFunction(() => {
|
||||||
@@ -1055,4 +1056,5 @@ export default {
|
|||||||
popupMenu,
|
popupMenu,
|
||||||
popdownMenu,
|
popdownMenu,
|
||||||
fixLifeLineHeights,
|
fixLifeLineHeights,
|
||||||
|
sanitizeUrl,
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const svgDraw = require('./svgDraw');
|
const svgDraw = require('./svgDraw').default;
|
||||||
const { MockD3 } = require('d3');
|
const { MockD3 } = require('d3');
|
||||||
|
|
||||||
describe('svgDraw', function () {
|
describe('svgDraw', function () {
|
||||||
@@ -124,4 +124,18 @@ describe('svgDraw', function () {
|
|||||||
expect(rect.lower).toHaveBeenCalled();
|
expect(rect.lower).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('sanitizeUrl', function () {
|
||||||
|
it('it should sanitize malicious urls', function () {
|
||||||
|
const maliciousStr = 'javascript:script:alert(1)';
|
||||||
|
const result = svgDraw.sanitizeUrl(maliciousStr);
|
||||||
|
console.log('result', result);
|
||||||
|
expect(result).not.toContain('javascript:alert(1)');
|
||||||
|
});
|
||||||
|
it('it should not sanitize non dangerous urls', function () {
|
||||||
|
const maliciousStr = 'javajavascript:script:alert(1)';
|
||||||
|
const result = svgDraw.sanitizeUrl(maliciousStr);
|
||||||
|
console.log('result', result);
|
||||||
|
expect(result).not.toContain('javascript:alert(1)');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user