Merge pull request #878 from knsv/i847-cross-site-scripting

I847 cross site scripting
This commit is contained in:
Knut Sveidqvist
2019-07-14 06:47:42 -07:00
committed by GitHub
21 changed files with 2402 additions and 218 deletions

122
dist/xssi.html vendored Normal file
View File

@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
<script>
function xss(x){
alert(x + ' cause an xss attack');
}
</script>
</head>
<body>
<div class="mermaid">
gantt
title Exclusive end dates <strong>If bold then xss</strong> (Manual date should end on 3d)
dateFormat YYYY-MM-DD
axisFormat %d
section Section1
2 Days: 1, 2019-01-01,2d
Manual Date: 2, 2019-01-01,2019-01-03
</div>
<img src=xss.png />
<div class="mermaid">
graph TD
A["<strong>If bold then xss</strong>Christmas"] -->|Get <strong>If bold then xss</strong> money| B(Go <strong>If bold then xss</strong> shopping)
B --> C{Let me thinksssss<br/>ssssssssssssssssssssss<br />sssssssssssssssssssssssssss}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
</div>
<div class="mermaid">
graph TB
subgraph "<strong>If bold then xss</strong>"
a1-->a2
end
</div>
<div class="mermaid">
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
click A "index.html#link-clicked" "link test"
click B testClick "click test"
classDef someclass fill:#f96;
class A someclass;
</div>
<div class="mermaid">
sequenceDiagram
participant "Alice"
participant Bob
participant John as John<br/>Second Line
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you <strong>If bold then xss</strong>John?
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
Bob-->Alice: Checking with John...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
</div>
<div class="mermaid">
classDiagram
Class01 <|-- AveryLongClass : Co<strong>If bold then xss</strong>ol
Class03 "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07 .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
</div>
<script src="./mermaid.js"></script>
<script>
mermaid.initialize({
theme: 'forest',
// themeCSS: '.node rect { fill: red; }',
logLevel: 3,
flowchart: { curve: 'linear' },
gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50 },
// sequenceDiagram: { actorMargin: 300 } // deprecated
});
</script>
<script>
function ganttTestClick(a, b, c){
console.log("a:", a)
console.log("b:", b)
console.log("c:", c)
}
function testClick(nodeId) {
console.log("clicked", nodeId)
var originalBgColor = document.querySelector('body').style.backgroundColor
document.querySelector('body').style.backgroundColor = 'yellow'
setTimeout(function() {
document.querySelector('body').style.backgroundColor = originalBgColor
}, 100)
}
</script>
</body>
</html>

View File

@@ -1,103 +1,228 @@
# mermaidAPI <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
> **Warning** This file is generated automatically from the comments of [mermaidAPI.js](https://github.com/knsv/mermaid/blob/master/src/mermaidAPI.js) file. Please read that file **instead** for up-to-date information. ## mermaidAPI
This is the api to be used when handling the integration with the web page instead of using the default integration (mermaid.js). This is the api to be used when handling the integration with the web page instead of using the default integration
(mermaid.js).
The core of this api is the **render** function that given a graph definitionas text renders the graph/diagram and returns a svg element for the graph. It is is then up to the user of the API to make use of the svg, either insert it somewhere in the page or something completely different.
The core of this api is the **render** function that given a graph definitionas text renders the graph/diagram and
returns a svg element for the graph. It is is then up to the user of the API to make use of the svg, either insert it
somewhere in the page or something completely different.
## Configuration ## Configuration
These are the default options which can be overridden with the initialization call as in the example below: These are the default options which can be overridden with the initialization call as in the example below:
```javascript mermaid.initialize({
mermaid.initialize({ flowchart:{
flowchart:{ htmlLabels: false
htmlLabels: false }
} });
});
```
## theme
theme , the CSS style sheet
**theme** - Choose one of the built-in themes: default, forest, dark or neutral. To disable any pre-defined mermaid theme, use "null".
**themeCSS** - Use your own CSS. This overrides **theme**.
"theme": "forest",
"themeCSS": ".node rect { fill: red; }"
## logLevel ## logLevel
Decides the amount of logging to be used. This option decides the amount of logging to be used.
- debug: 1 - debug: 1
- info: 2 - info: 2
- warn: 3 - warn: 3
- error: 4 - error: 4
- fatal: 5 - fatal: (**default**) 5
**cloneCssStyles** - This options controls whether or not the css rules should be copied into the generated svg startOnLoad - This options controls whether or mermaid starts when the page loads ## securityLevel
**arrowMarkerAbsolute** - This options controls whether or arrow markers in html code will be absolute paths or an anchor, #. This matters if you are using base tag settings.
Sets the level of trust to be used on the parsed diagrams.
- **true**: (**default**) tags in text are encoded, click functionality is disabeled
- **false**: tags in text are allowed, click functionality is enabled
## startOnLoad
This options controls whether or mermaid starts when the page loads
**Default value true**.
## arrowMarkerAbsolute
This options controls whether or arrow markers in html code will be absolute paths or
an anchor, #. This matters if you are using base tag settings.
**Default value false**.
## flowchart ## flowchart
The object containing configurations specific for flowcharts The object containing configurations specific for flowcharts
**htmlLabels** - Flag for setting whether or not a html tag should be used for rendering labels on the edges
**useMaxWidth** - Flag for setting whether or not a all available width should be used for the diagram.
### htmlLabels
Flag for setting whether or not a html tag should be used for rendering labels
on the edges.
**Default value true**.
### curve
**Default value linear**.
## sequence ## sequence
The object containing configurations specific for sequence diagrams The object containing configurations specific for sequence diagrams
**diagramMarginX** - margin to the right and left of the sequence diagram ### diagramMarginX
**diagramMarginY** - margin to the over and under the sequence diagram
**actorMargin** - Margin between actors
**width** - Width of actor boxes
**height** - Height of actor boxes
**boxMargin** - Margin around loop boxes
**boxTextMargin** - margin around the text in loop/alt/opt boxes
**noteMargin** - margin around notes
**messageMargin** - Space between messages
**mirrorActors** - mirror actors under diagram
**bottomMarginAdj** - Depending on css styling this might need adjustment. Prolongs the edge of the diagram downwards
**useMaxWidth** - when this flag is set the height and width is set to 100% and is then scaling with the available space if not the absolute space required is used
margin to the right and left of the sequence diagram
**Default value 50**.
### diagramMarginY
margin to the over and under the sequence diagram.
**Default value 10**.
### actorMargin
Margin between actors.
**Default value 50**.
### width
Width of actor boxes
**Default value 150**.
### height
Height of actor boxes
**Default value 65**.
### boxMargin
Margin around loop boxes
**Default value 10**.
### boxTextMargin
margin around the text in loop/alt/opt boxes
**Default value 5**.
### noteMargin
margin around notes.
**Default value 10**.
### messageMargin
Space between messages.
**Default value 35**.
### mirrorActors
mirror actors under diagram.
**Default value true**.
### bottomMarginAdj
Depending on css styling this might need adjustment.
Prolongs the edge of the diagram downwards.
**Default value 1**.
### useMaxWidth
when this flag is set the height and width is set to 100% and is then scaling with the
available space if not the absolute space required is used.
**Default value true**.
### rightAngles
This will display arrows that start and begin at the same node as right angles, rather than a curve
**Default value false**.
### showSequenceNumbers
This will show the node numbers
**Default value false**.
## gantt ## gantt
The object containing configurations specific for gantt diagrams The object containing configurations specific for gantt diagrams\*
**titleTopMargin** - margin top for the text over the gantt diagram ### titleTopMargin
**barHeight** - the height of the bars in the graph
**barGap** - the margin between the different activities in the gantt diagram
**topPadding** - margin between title and gantt diagram and between axis and gantt diagram.
**leftPadding** - the space allocated for the section name to the left of the activities.
**gridLineStartPadding** - Vertical starting position of the grid lines
**fontSize** - font size ...
**fontFamily** - font family ...
**numberSectionStyles** - the number of alternating section styles
**axisFormatter** - formatting of the axis, this might need adjustment to match your locale and preferences
Margin top for the text over the gantt diagram
**Default value 25**.
## parse ### barHeight
Function that parses a mermaid diagram definition. If parsing fails the parseError callback is called and an error is thrown. The height of the bars in the graph
**Default value 20**.
### barGap
## version The margin between the different activities in the gantt diagram.
**Default value 4**.
Function returning version information ### topPadding
Margin between title and gantt diagram and between axis and gantt diagram.
**Default value 50**.
### leftPadding
The space allocated for the section name to the left of the activities.
**Default value 75**.
### gridLineStartPadding
Vertical starting position of the grid lines.
**Default value 35**.
### fontSize
Font size ...
**Default value 11**.
### fontFamily
font family ...
**Default value '"Open-Sans", "sans-serif"'**.
### numberSectionStyles
The number of alternating section styles.
**Default value 4**.
### axisFormat
Datetime format of the axis, this might need adjustment to match your locale and preferences
**Default value '%Y-%m-%d'**.
## render ## render
Function that renders a svg with a graph from a chart definition. Usage example below: Function that renders an svg with a graph from a chart definition. Usage example below.
```javascript ```js
mermaidAPI.initialize({ mermaidAPI.initialize({
startOnLoad: true startOnLoad:true
}) });
$(function() { $(function(){
var graphDefinition = 'graph TB\na-->b' const graphDefinition = 'graph TB\na-->b';
var cb = function(svgGraph) { const cb = function(svgGraph){
console.log(svgGraph) console.log(svgGraph);
} };
mermaidAPI.render('id1',graphDefinition,cb) mermaidAPI.render('id1',graphDefinition,cb);
}) });
``` ```
### Parameters
- `id` the id of the element to be rendered
- `txt` the graph definition
- `cb` callback which is called after rendering is finished with the svg code as inparam.
- `container` selector to element in which a div with the graph temporarily will be inserted. In one is
provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
completed.

View File

@@ -1,19 +1,23 @@
/* eslint-env jest */ /* eslint-env jest */
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'
export const mermaidUrl = (graphStr, options) => { export const mermaidUrl = (graphStr, options, api) => {
const obj = { const obj = {
code: graphStr, code: graphStr,
mermaid: options mermaid: options
} }
const objStr = JSON.stringify(obj) const objStr = JSON.stringify(obj)
// console.log(Base64) let url = 'http://localhost:9000/e2e.html?graph=' + Base64.encodeURI(objStr)
return 'http://localhost:9000/e2e.html?graph=' + Base64.encodeURI(objStr) if (api) {
url = 'http://localhost:9000/xss.html?graph=' + graphStr
}
return url
} }
export const imgSnapshotTest = async (page, graphStr, options) => { export const imgSnapshotTest = async (page, graphStr, options, api) => {
return new Promise(async resolve => { return new Promise(async resolve => {
const url = mermaidUrl(graphStr, options) const url = mermaidUrl(graphStr, options, api)
await page.goto(url) await page.goto(url)

View File

@@ -1,4 +1,5 @@
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'
import mermaid from '../../dist/mermaid.core'
/** /**
* ##contentLoaded * ##contentLoaded
@@ -22,6 +23,29 @@ const contentLoaded = function () {
global.mermaid.init() global.mermaid.init()
} }
} }
const contentLoadedApi = function () {
let pos = document.location.href.indexOf('?graph=')
if (pos > 0) {
pos = pos + 7
const graphBase64 = document.location.href.substr(pos)
const graphObj = JSON.parse(Base64.decode(graphBase64))
// const graph = 'hello'
console.log(graphObj)
const div = document.createElement('div')
div.id = 'block'
div.className = 'mermaid'
// div.innerHTML = graphObj.code
document.getElementsByTagName('body')[0].appendChild(div)
global.mermaid.initialize(graphObj.mermaid)
console.log('apa')
mermaid.render('newid', graphObj.code, (svgCode, bindFunctions) => {
div.innerHTML = svgCode
bindFunctions(div)
}, div)
}
}
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
/*! /*!
@@ -30,7 +54,12 @@ if (typeof document !== 'undefined') {
window.addEventListener( window.addEventListener(
'load', 'load',
function () { function () {
contentLoaded() if (this.location.href.match('xss.html')) {
this.console.log('Using api')
contentLoadedApi()
} else {
contentLoaded()
}
}, },
false false
) )

44
e2e/platform/xss.html Normal file
View File

@@ -0,0 +1,44 @@
<html>
<head>
<script src="/e2e.js"></script>
<link
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
rel="stylesheet"
/>
<style>
.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>
<script>
function xssAttack(){
const div = document.createElement('div')
div.id = 'the-malware'
div.className = 'malware'
div.innerHTML = 'XSS Succeeded'
document.getElementsByTagName('body')[0].appendChild(div)
}
</script>
</head>
<body>
<script src="./mermaid.js"></script>
<script>
mermaid.initialize({
startOnLoad: false,
useMaxWidth: true,
});
</script>
</body>
</html>

BIN
e2e/platform/xss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

15
e2e/spec/xss.spec.js Normal file
View File

@@ -0,0 +1,15 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../helpers/util.js'
const { toMatchImageSnapshot } = require('jest-image-snapshot')
expect.extend({ toMatchImageSnapshot })
/* eslint-disable */
describe('XSS', () => {
it('should handle xss in tags', async () => {
// const str = 'graph LR;\nB-->D(<img onerror=location=`javascript\u003aalert\u0028document.domain\u0029` src=x>);'
const str = 'eyJjb2RlIjoiXG5ncmFwaCBMUlxuICAgICAgQi0tPkQoPGltZyBvbmVycm9yPWxvY2F0aW9uPWBqYXZhc2NyaXB0XFx1MDAzYXhzc0F0dGFja1xcdTAwMjhkb2N1bWVudC5kb21haW5cXHUwMDI5YCBzcmM9eD4pOyIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In19';
await imgSnapshotTest(page, str,
{}, true)
})
})

View File

@@ -1,10 +1,10 @@
import gulp from 'gulp' import gulp from 'gulp'
import jison from 'gulp-jison' import jison from 'gulp-jison'
import filelog from 'gulp-filelog' import print from 'gulp-print'
gulp.task('jison', function () { gulp.task('jison', function () {
return gulp.src('./src/**/*.jison') return gulp.src('./src/**/*.jison')
.pipe(filelog('Jison file:')) .pipe(print())
.pipe(jison({ 'token-stack': true })) .pipe(jison({ 'token-stack': true }))
.pipe(gulp.dest('./src/')) .pipe(gulp.dest('./src/'))
}) })

View File

@@ -14,6 +14,7 @@
], ],
"scripts": { "scripts": {
"build": "webpack --progress --colors", "build": "webpack --progress --colors",
"postbuild": "documentation build src/mermaidAPI.js --shallow -f md --markdown-toc false -o docs/mermaidAPI.md",
"build:watch": "yarn build --watch", "build:watch": "yarn build --watch",
"minify": "minify ./dist/mermaid.js > ./dist/mermaid.min.js", "minify": "minify ./dist/mermaid.js > ./dist/mermaid.min.js",
"release": "yarn build -p --config webpack.config.prod.babel.js", "release": "yarn build -p --config webpack.config.prod.babel.js",
@@ -45,11 +46,13 @@
"d3": "^5.7.0", "d3": "^5.7.0",
"dagre-d3-renderer": "^0.5.8", "dagre-d3-renderer": "^0.5.8",
"dagre-layout": "^0.8.8", "dagre-layout": "^0.8.8",
"documentation": "^12.0.1",
"graphlibrary": "^2.2.0", "graphlibrary": "^2.2.0",
"gulp-print": "^5.0.2",
"he": "^1.2.0", "he": "^1.2.0",
"moment-mini": "^2.22.1",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"minify": "^4.1.1", "minify": "^4.1.1",
"moment-mini": "^2.22.1",
"scope-css": "^1.2.1" "scope-css": "^1.2.1"
}, },
"devDependencies": { "devDependencies": {
@@ -63,7 +66,6 @@
"css-loader": "^2.0.1", "css-loader": "^2.0.1",
"css-to-string-loader": "^0.1.3", "css-to-string-loader": "^0.1.3",
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-filelog": "^0.4.1",
"gulp-jison": "^1.2.0", "gulp-jison": "^1.2.0",
"husky": "^1.2.1", "husky": "^1.2.1",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
@@ -91,4 +93,4 @@
"babel-core" "babel-core"
] ]
} }
} }

8
src/config.js Normal file
View File

@@ -0,0 +1,8 @@
let config = {
securityLevel: 'strict'
}
export const setConfig = conf => {
config = conf
}
export const getConfig = () => config

View File

@@ -2,7 +2,9 @@ import * as d3 from 'd3'
import { logger } from '../../logger' import { logger } from '../../logger'
import utils from '../../utils' import utils from '../../utils'
import { getConfig } from '../../config'
const config = getConfig()
let vertices = {} let vertices = {}
let edges = [] let edges = []
let classes = [] let classes = []
@@ -13,6 +15,19 @@ let subCount = 0
let direction let direction
// Functions to be run after graph rendering // Functions to be run after graph rendering
let funs = [] let funs = []
const sanitize = text => {
let txt = text
if (config.securityLevel === 'strict') {
txt = txt.replace(/<br>/g, '#br#')
txt = txt.replace(/<br\S*\/>/g, '#br#')
txt = txt.replace(/</g, '&lt;').replace(/>/g, '&gt;')
txt = txt.replace(/#br#/g, '<br/>')
}
return txt
}
/** /**
* Function called by parser when a node definition has been found * Function called by parser when a node definition has been found
* @param id * @param id
@@ -35,7 +50,7 @@ export const addVertex = function (id, text, type, style, classes) {
vertices[id] = { id: id, styles: [], classes: [] } vertices[id] = { id: id, styles: [], classes: [] }
} }
if (typeof text !== 'undefined') { if (typeof text !== 'undefined') {
txt = text.trim() txt = sanitize(text.trim())
// strip quotes if string starts and exnds with a quote // strip quotes if string starts and exnds with a quote
if (txt[0] === '"' && txt[txt.length - 1] === '"') { if (txt[0] === '"' && txt[txt.length - 1] === '"') {
@@ -76,7 +91,7 @@ export const addLink = function (start, end, type, linktext) {
linktext = type.text linktext = type.text
if (typeof linktext !== 'undefined') { if (typeof linktext !== 'undefined') {
edge.text = linktext.trim() edge.text = sanitize(linktext.trim())
// strip quotes if string starts and exnds with a quote // strip quotes if string starts and exnds with a quote
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') { if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
@@ -172,6 +187,9 @@ const setTooltip = function (ids, tooltip) {
} }
const setClickFun = function (id, functionName) { const setClickFun = function (id, functionName) {
if (config.securityLevel === 'strict') {
return
}
if (typeof functionName === 'undefined') { if (typeof functionName === 'undefined') {
return return
} }
@@ -335,6 +353,7 @@ export const addSubGraph = function (id, list, title) {
id = id || ('subGraph' + subCount) id = id || ('subGraph' + subCount)
title = title || '' title = title || ''
title = sanitize(title)
subCount = subCount + 1 subCount = subCount + 1
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] } const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] }
subGraphs.push(subGraph) subGraphs.push(subGraph)

View File

@@ -1,5 +1,10 @@
import flowDb from '../flowDb' import flowDb from '../flowDb'
import flow from './flow' import flow from './flow'
import { setConfig } from '../../../config'
setConfig({
securityLevel: 'strict',
})
describe('when parsing ', function () { describe('when parsing ', function () {
beforeEach(function () { beforeEach(function () {
@@ -1333,7 +1338,7 @@ describe('when parsing ', function () {
const edges = flow.parser.yy.getEdges() const edges = flow.parser.yy.getEdges()
expect(vert['C'].type).toBe('round') expect(vert['C'].type).toBe('round')
expect(vert['C'].text).toBe('Chimpansen hoppar åäö <br> - ÅÄÖ') expect(vert['C'].text).toBe('Chimpansen hoppar åäö <br/> - ÅÄÖ')
}) })
// xit('it should handle åäö, minus and space and br',function(){ // xit('it should handle åäö, minus and space and br',function(){
// const res = flow.parser.parse('graph TD; A[Object&#40;foo,bar&#41;]-->B(Thing);'); // const res = flow.parser.parse('graph TD; A[Object&#40;foo,bar&#41;]-->B(Thing);');
@@ -1460,7 +1465,7 @@ describe('when parsing ', function () {
expect(edges.length).toBe(0) expect(edges.length).toBe(0)
expect(vert['a'].type).toBe('diamond') expect(vert['a'].type).toBe('diamond')
expect(vert['a'].text).toBe('A <br> end') expect(vert['a'].text).toBe('A <br/> end')
}) })
it('should handle a single round node with html in it', function () { it('should handle a single round node with html in it', function () {
// Silly but syntactically correct // Silly but syntactically correct
@@ -1471,7 +1476,7 @@ describe('when parsing ', function () {
expect(edges.length).toBe(0) expect(edges.length).toBe(0)
expect(vert['a'].type).toBe('round') expect(vert['a'].type).toBe('round')
expect(vert['a'].text).toBe('A <br> end') expect(vert['a'].text).toBe('A <br/> end')
}) })
it('should handle a single node with alphanumerics starting on a char', function () { it('should handle a single node with alphanumerics starting on a char', function () {
// Silly but syntactically correct // Silly but syntactically correct
@@ -1573,7 +1578,7 @@ describe('when parsing ', function () {
}) })
describe('special characters should be be handled.', function () { describe('special characters should be be handled.', function () {
const charTest = function (char) { const charTest = function (char, result) {
const res = flow.parser.parse('graph TD;A(' + char + ')-->B;') const res = flow.parser.parse('graph TD;A(' + char + ')-->B;')
const vert = flow.parser.yy.getVertices() const vert = flow.parser.yy.getVertices()
@@ -1581,7 +1586,11 @@ describe('when parsing ', function () {
expect(vert['A'].id).toBe('A') expect(vert['A'].id).toBe('A')
expect(vert['B'].id).toBe('B') expect(vert['B'].id).toBe('B')
expect(vert['A'].text).toBe(char) if(result){
expect(vert['A'].text).toBe(result)
}else{
expect(vert['A'].text).toBe(char)
}
} }
it('it should be able to parse a \'.\'', function () { it('it should be able to parse a \'.\'', function () {
@@ -1614,16 +1623,19 @@ describe('when parsing ', function () {
}) })
it('it should be able to parse a \'<\'', function () { it('it should be able to parse a \'<\'', function () {
charTest('<') charTest('<','&lt;')
}) })
it('it should be able to parse a \'>\'', function () { it('it should be able to parse a \'>\'', function () {
charTest('>') charTest('>','&gt;')
}) })
it('it should be able to parse a \'=\'', function () { it('it should be able to parse a \'=\'', function () {
charTest('=') charTest('=')
}) })
it('it should be able to parse a \'&\'', function () {
charTest('&')
})
}) })
it('should be possible to declare a class', function () { it('should be possible to declare a class', function () {

View File

@@ -1,7 +1,9 @@
import moment from 'moment-mini' import moment from 'moment-mini'
import { logger } from '../../logger' import { logger } from '../../logger'
import * as d3 from 'd3' import * as d3 from 'd3'
import { getConfig } from '../../config'
const config = getConfig()
let dateFormat = '' let dateFormat = ''
let axisFormat = '' let axisFormat = ''
let excludes = [] let excludes = []
@@ -62,10 +64,12 @@ export const getExcludes = function () {
} }
export const setTitle = function (txt) { export const setTitle = function (txt) {
console.log('Setting title ', txt)
title = txt title = txt
} }
export const getTitle = function () { export const getTitle = function () {
console.log('Title is ', title)
return title return title
} }
@@ -451,6 +455,9 @@ export const setClass = function (ids, className) {
} }
const setClickFun = function (id, functionName, functionArgs) { const setClickFun = function (id, functionName, functionArgs) {
if (config.securityLevel === 'strict') {
return
}
if (typeof functionName === 'undefined') { if (typeof functionName === 'undefined') {
return return
} }

View File

@@ -71,7 +71,7 @@
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
} }
*/ */
var gantt = (function(){ var parser = (function(){
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15,16,18,20],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13],$V6=[1,14],$V7=[1,16],$V8=[1,17]; var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15,16,18,20],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13],$V6=[1,14],$V7=[1,16],$V8=[1,17];
var parser = {trace: function trace () { }, var parser = {trace: function trace () { },
yy: {}, yy: {},
@@ -191,15 +191,18 @@ parse: function parse(input) {
vstack.length = vstack.length - n; vstack.length = vstack.length - n;
lstack.length = lstack.length - n; lstack.length = lstack.length - n;
} }
_token_stack: function lex() {
var lex = function () {
var token; var token;
token = lexer.lex() || EOF; token = tstack.pop() || lexer.lex() || EOF;
if (typeof token !== 'number') { if (typeof token !== 'number') {
if (token instanceof Array) {
tstack = token;
token = tstack.pop();
}
token = self.symbols_[token] || token; token = self.symbols_[token] || token;
} }
return token; return token;
}; }
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
while (true) { while (true) {
state = stack[stack.length - 1]; state = stack[stack.length - 1];
@@ -211,27 +214,27 @@ parse: function parse(input) {
} }
action = table[state] && table[state][symbol]; action = table[state] && table[state][symbol];
} }
if (typeof action === 'undefined' || !action.length || !action[0]) { if (typeof action === 'undefined' || !action.length || !action[0]) {
var errStr = ''; var errStr = '';
expected = []; expected = [];
for (p in table[state]) { for (p in table[state]) {
if (this.terminals_[p] && p > TERROR) { if (this.terminals_[p] && p > TERROR) {
expected.push('\'' + this.terminals_[p] + '\''); expected.push('\'' + this.terminals_[p] + '\'');
}
} }
if (lexer.showPosition) {
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
} else {
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
}
this.parseError(errStr, {
text: lexer.match,
token: this.terminals_[symbol] || symbol,
line: lexer.yylineno,
loc: yyloc,
expected: expected
});
} }
if (lexer.showPosition) {
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
} else {
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
}
this.parseError(errStr, {
text: lexer.match,
token: this.terminals_[symbol] || symbol,
line: lexer.yylineno,
loc: yyloc,
expected: expected
});
}
if (action[0] instanceof Array && action.length > 1) { if (action[0] instanceof Array && action.length > 1) {
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
} }
@@ -703,9 +706,9 @@ return new Parser;
if (typeof require !== 'undefined' && typeof exports !== 'undefined') { if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = gantt; exports.parser = parser;
exports.Parser = gantt.Parser; exports.Parser = parser.Parser;
exports.parse = function () { return gantt.parse.apply(gantt, arguments); }; exports.parse = function () { return parser.parse.apply(parser, arguments); };
exports.main = function commonjsMain (args) { exports.main = function commonjsMain (args) {
if (!args[1]) { if (!args[1]) {
console.log('Usage: '+args[0]+' FILE'); console.log('Usage: '+args[0]+' FILE');

View File

@@ -1,20 +1,16 @@
/** /**
* ---
* title: mermaidAPI
* order: 5
* ---
* # mermaidAPI
* This is the api to be used when handling the integration with the web page instead of using the default integration * This is the api to be used when handling the integration with the web page instead of using the default integration
* (mermaid.js). * (mermaid.js).
* *
* The core of this api is the **render** function that given a graph definitionas text renders the graph/diagram and * The core of this api is the **render** function that given a graph definitionas text renders the graph/diagram and
* returns a svg element for the graph. It is is then up to the user of the API to make use of the svg, either insert it * returns a svg element for the graph. It is is then up to the user of the API to make use of the svg, either insert it
* somewhere in the page or something completely different. * somewhere in the page or something completely different.
* @name mermaidAPI
*/ */
import * as d3 from 'd3' import * as d3 from 'd3'
import scope from 'scope-css' import scope from 'scope-css'
import pkg from '../package.json' import pkg from '../package.json'
import { setConfig } from './config'
import { logger, setLogLevel } from './logger' import { logger, setLogLevel } from './logger'
import utils from './utils' import utils from './utils'
import flowRenderer from './diagrams/flowchart/flowRenderer' import flowRenderer from './diagrams/flowchart/flowRenderer'
@@ -42,7 +38,6 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
} }
/** /**
* ## Configuration
* These are the default options which can be overridden with the initialization call as in the example below: * These are the default options which can be overridden with the initialization call as in the example below:
* ``` * ```
* mermaid.initialize({ * mermaid.initialize({
@@ -51,6 +46,7 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
* } * }
* }); * });
* ``` * ```
* @name Configuration
*/ */
const config = { const config = {
@@ -68,170 +64,205 @@ const config = {
themeCSS: undefined, themeCSS: undefined,
/** /**
* logLevel , decides the amount of logging to be used. * This option decides the amount of logging to be used.
* * debug: 1 * * debug: 1
* * info: 2 * * info: 2
* * warn: 3 * * warn: 3
* * error: 4 * * error: 4
* * fatal: 5 * * fatal: (**default**) 5
*/ */
logLevel: 5, logLevel: 5,
/** /**
* **startOnLoad** - This options controls whether or mermaid starts when the page loads * Sets the level of trust to be used on the parsed diagrams.
* * **true**: (**default**) tags in text are encoded, click functionality is disabeled
* * **false**: tags in text are allowed, click functionality is enabled
*/
securityLevel: 'strict',
/**
* This options controls whether or mermaid starts when the page loads
* **Default value true**.
*/ */
startOnLoad: true, startOnLoad: true,
/** /**
* **arrowMarkerAbsolute** - This options controls whether or arrow markers in html code will be absolute paths or * This options controls whether or arrow markers in html code will be absolute paths or
* an anchor, #. This matters if you are using base tag settings. * an anchor, #. This matters if you are using base tag settings.
* **Default value false**.
*/ */
arrowMarkerAbsolute: false, arrowMarkerAbsolute: false,
/** /**
* ### flowchart * The object containing configurations specific for flowcharts
* *The object containing configurations specific for flowcharts*
*/ */
flowchart: { flowchart: {
/** /**
* **htmlLabels** - Flag for setting whether or not a html tag should be used for rendering labels * Flag for setting whether or not a html tag should be used for rendering labels
* on the edges * on the edges.
* **Default value true**.
*/ */
htmlLabels: true, htmlLabels: true,
/**
* **Default value linear**.
*/
curve: 'linear' curve: 'linear'
}, },
/** /**
* ### sequenceDiagram
* The object containing configurations specific for sequence diagrams * The object containing configurations specific for sequence diagrams
*/ */
sequence: { sequence: {
/** /**
* **diagramMarginX** - margin to the right and left of the sequence diagram * margin to the right and left of the sequence diagram
* **Default value 50**.
*/ */
diagramMarginX: 50, diagramMarginX: 50,
/** /**
* **diagramMarginY** - margin to the over and under the sequence diagram * margin to the over and under the sequence diagram.
* **Default value 10**.
*/ */
diagramMarginY: 10, diagramMarginY: 10,
/** /**
* **actorMargin** - Margin between actors * Margin between actors.
* **Default value 50**.
*/ */
actorMargin: 50, actorMargin: 50,
/** /**
* **width** - Width of actor boxes * Width of actor boxes
* **Default value 150**.
*/ */
width: 150, width: 150,
/** /**
* **height** - Height of actor boxes * Height of actor boxes
* **Default value 65**.
*/ */
height: 65, height: 65,
/** /**
* **boxMargin** - Margin around loop boxes * Margin around loop boxes
* **Default value 10**.
*/ */
boxMargin: 10, boxMargin: 10,
/** /**
* **boxTextMargin** - margin around the text in loop/alt/opt boxes * margin around the text in loop/alt/opt boxes
* **Default value 5**.
*/ */
boxTextMargin: 5, boxTextMargin: 5,
/** /**
* **noteMargin** - margin around notes * margin around notes.
* **Default value 10**.
*/ */
noteMargin: 10, noteMargin: 10,
/** /**
* **messageMargin** - Space between messages * Space between messages.
* **Default value 35**.
*/ */
messageMargin: 35, messageMargin: 35,
/** /**
* **mirrorActors** - mirror actors under diagram * mirror actors under diagram.
* **Default value true**.
*/ */
mirrorActors: true, mirrorActors: true,
/** /**
* **bottomMarginAdj** - Depending on css styling this might need adjustment. * Depending on css styling this might need adjustment.
* Prolongs the edge of the diagram downwards * Prolongs the edge of the diagram downwards.
* **Default value 1**.
*/ */
bottomMarginAdj: 1, bottomMarginAdj: 1,
/** /**
* **useMaxWidth** - when this flag is set the height and width is set to 100% and is then scaling with the * when this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used * available space if not the absolute space required is used.
* **Default value true**.
*/ */
useMaxWidth: true, useMaxWidth: true,
/** /**
* **rightAngles** - this will display arrows that start and begin at the same node as right angles, rather than a curve * This will display arrows that start and begin at the same node as right angles, rather than a curve
* **Default value false**.
*/ */
rightAngles: false, rightAngles: false,
/** /**
* **showSequenceNumbers** - this will show the node numbers * This will show the node numbers
* **Default value false**.
*/ */
showSequenceNumbers: false showSequenceNumbers: false
}, },
/** ### gantt /**
* The object containing configurations specific for gantt diagrams* * The object containing configurations specific for gantt diagrams*
*/ */
gantt: { gantt: {
/** /**
* **titleTopMargin** - margin top for the text over the gantt diagram * Margin top for the text over the gantt diagram
* **Default value 25**.
*/ */
titleTopMargin: 25, titleTopMargin: 25,
/** /**
* **barHeight** - the height of the bars in the graph * The height of the bars in the graph
* **Default value 20**.
*/ */
barHeight: 20, barHeight: 20,
/** /**
* **barGap** - the margin between the different activities in the gantt diagram * The margin between the different activities in the gantt diagram.
* **Default value 4**.
*/ */
barGap: 4, barGap: 4,
/** /**
* **topPadding** - margin between title and gantt diagram and between axis and gantt diagram. * Margin between title and gantt diagram and between axis and gantt diagram.
* **Default value 50**.
*/ */
topPadding: 50, topPadding: 50,
/** /**
* **leftPadding** - the space allocated for the section name to the left of the activities. * The space allocated for the section name to the left of the activities.
* **Default value 75**.
*/ */
leftPadding: 75, leftPadding: 75,
/** /**
* **gridLineStartPadding** - Vertical starting position of the grid lines * Vertical starting position of the grid lines.
* **Default value 35**.
*/ */
gridLineStartPadding: 35, gridLineStartPadding: 35,
/** /**
* **fontSize** - font size ... * Font size ...
* **Default value 11**.
*/ */
fontSize: 11, fontSize: 11,
/** /**
* **fontFamily** - font family ... * font family ...
* **Default value '"Open-Sans", "sans-serif"'**.
*/ */
fontFamily: '"Open-Sans", "sans-serif"', fontFamily: '"Open-Sans", "sans-serif"',
/** /**
* **numberSectionStyles** - the number of alternating section styles * The number of alternating section styles.
* **Default value 4**.
*/ */
numberSectionStyles: 4, numberSectionStyles: 4,
/** /**
* **axisFormat** - datetime format of the axis, this might need adjustment to match your locale and preferences * Datetime format of the axis, this might need adjustment to match your locale and preferences
* **Default value '%Y-%m-%d'**.
*/ */
axisFormat: '%Y-%m-%d' axisFormat: '%Y-%m-%d'
}, },
@@ -240,6 +271,7 @@ const config = {
} }
setLogLevel(config.logLevel) setLogLevel(config.logLevel)
setConfig(config)
function parse (text) { function parse (text) {
const graphType = utils.detectType(text) const graphType = utils.detectType(text)
@@ -324,10 +356,9 @@ export const decodeEntities = function (text) {
return txt return txt
} }
/** /**
* ##render
* Function that renders an svg with a graph from a chart definition. Usage example below. * Function that renders an svg with a graph from a chart definition. Usage example below.
* *
* ``` * ```js
* mermaidAPI.initialize({ * mermaidAPI.initialize({
* startOnLoad:true * startOnLoad:true
* }); * });

1895
yarn.lock

File diff suppressed because it is too large Load Diff