Fix for issue with backticks in ids in classDiagrams

This commit is contained in:
Knut Sveidqvist
2023-09-29 13:38:00 +02:00
parent f96d351fdc
commit 47acc1e423
21 changed files with 305 additions and 24 deletions

View File

@@ -132,4 +132,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 backticks in class names properly', () => {
cy.visit('http://localhost:9000/xss24.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
}); });

View File

@@ -58,12 +58,10 @@
</head> </head>
<body> <body>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
flowchart classDiagram
classDef mainCategories fill:#f9d5e5, stroke:#233d4d,stroke-width:2px, font-weight:bold; `Class<img src=x onerror=alert(1)>` <|-- `Class2<img src=x onerror=alert(2)>`
CS(Customer Awareness Journey):::mainCategories </pre>
</pre <pre id="diagram" class="mermaid2">
>
<pre id="diagram" class="mermaid">
flowchart flowchart
Node1:::class1 --> Node2:::class2 Node1:::class1 --> Node2:::class2
Node1:::class1 --> Node3:::class2 Node1:::class1 --> Node3:::class2

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

109
cypress/platform/xss24.html Normal file
View File

@@ -0,0 +1,109 @@
<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>
<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);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>
<div class="flex">
<div id="diagram" class="mermaid"></div>
<div id="res" class=""></div>
</div>
<script type="module">
import mermaid from './mermaid.esm.mjs';
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-wrapper',
},
flowchart: {
// defaultRenderer: 'dagre-wrapper',
nodeSpacing: 10,
curve: 'cardinal',
htmlLabels: true,
},
htmlLabels: false,
// 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');
}
let diagram = 'classDiagram\n';
diagram += '`Class<img src=x on';
diagram += 'error=xssAttack()>` <|-- `Class2<img src=x on';
diagram += 'error=xssAttack()>`';
console.log(diagram);
// document.querySelector('#diagram').innerHTML = diagram;
const { svg } = await mermaid.render('diagram', diagram);
document.querySelector('#res').innerHTML = svg;
</script>
</body>
</html>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>
@@ -84,14 +94,6 @@
function callback() { function callback() {
alert('It worked'); 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 Succeeded');
}
let diagram = 'graph LR\n'; let diagram = 'graph LR\n';
diagram += 'B-->D("<img onerror=location=`java'; diagram += 'B-->D("<img onerror=location=`java';
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n"; // diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px; font-size: 72px;
} }
</style> </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);
throw new Error('XSS Succeeded');
}
</script>
</head> </head>
<body> <body>
<div>Security check</div> <div>Security check</div>

View File

@@ -36,7 +36,8 @@ let functions: any[] = [];
const sanitizeText = (txt: string) => common.sanitizeText(txt, configApi.getConfig()); const sanitizeText = (txt: string) => common.sanitizeText(txt, configApi.getConfig());
const splitClassNameAndType = function (id: string) { const splitClassNameAndType = function (_id: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
let genericType = ''; let genericType = '';
let className = id; let className = id;
@@ -49,7 +50,8 @@ const splitClassNameAndType = function (id: string) {
return { className: className, type: genericType }; return { className: className, type: genericType };
}; };
export const setClassLabel = function (id: string, label: string) { export const setClassLabel = function (_id: string, label: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
if (label) { if (label) {
label = sanitizeText(label); label = sanitizeText(label);
} }
@@ -64,22 +66,25 @@ export const setClassLabel = function (id: string, label: string) {
* @param id - Id of the class to add * @param id - Id of the class to add
* @public * @public
*/ */
export const addClass = function (id: string) { export const addClass = function (_id: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
const { className, type } = splitClassNameAndType(id); const { className, type } = splitClassNameAndType(id);
// Only add class if not exists // Only add class if not exists
if (Object.hasOwn(classes, className)) { if (Object.hasOwn(classes, className)) {
return; return;
} }
// alert('Adding class: ' + className);
classes[className] = { const name = common.sanitizeText(className, configApi.getConfig());
id: className, // alert('Adding class after: ' + name);
classes[name] = {
id: name,
type: type, type: type,
label: className, label: name,
cssClasses: [], cssClasses: [],
methods: [], methods: [],
members: [], members: [],
annotations: [], annotations: [],
domId: MERMAID_DOM_ID_PREFIX + className + '-' + classCounter, domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
} as ClassNode; } as ClassNode;
classCounter++; classCounter++;
@@ -91,7 +96,8 @@ export const addClass = function (id: string) {
* @param id - class ID to lookup * @param id - class ID to lookup
* @public * @public
*/ */
export const lookUpDomId = function (id: string): string { export const lookUpDomId = function (_id: string): string {
const id = common.sanitizeText(_id, configApi.getConfig());
if (id in classes) { if (id in classes) {
return classes[id].domId; return classes[id].domId;
} }
@@ -296,7 +302,8 @@ export const setClickEvent = function (ids: string, functionName: string, functi
setCssClass(ids, 'clickable'); setCssClass(ids, 'clickable');
}; };
const setClickFunc = function (domId: string, functionName: string, functionArgs: string) { const setClickFunc = function (_domId: string, functionName: string, functionArgs: string) {
const domId = common.sanitizeText(_domId, configApi.getConfig());
const config = configApi.getConfig(); const config = configApi.getConfig();
if (config.securityLevel !== 'loose') { if (config.securityLevel !== 'loose') {
return; return;