packages/mermaid/src/utils/sanitizeDirective.ts
import { configKeys } from '../defaultConfig.js';
import { log } from '../logger.js';
/**
* Sanitizes directive objects
*
* @param args - Directive's JSON
*/
export const sanitizeDirective = (args: any): void => {
log.debug('sanitizeDirective called with', args);
// Return if not an object
if (typeof args !== 'object' || args == null) {
return;
}
// Sanitize each element if an array
if (Array.isArray(args)) {
args.forEach((arg) => sanitizeDirective(arg));
return;
}
// Sanitize each key if an object
for (const key of Object.keys(args)) {
log.debug('Checking key', key);
if (
key.startsWith('__') ||
key.includes('proto') ||
key.includes('constr') ||
!configKeys.has(key) ||
args[key] == null
) {
log.debug('sanitize deleting key: ', key);
delete args[key];
continue;
}
// Recurse if an object
if (typeof args[key] === 'object') {
log.debug('sanitizing object', key);
sanitizeDirective(args[key]);
continue;
}
const cssMatchers = ['themeCSS', 'fontFamily', 'altFontFamily'];
for (const cssKey of cssMatchers) {
if (key.includes(cssKey)) {
log.debug('sanitizing css option', key);
args[key] = sanitizeCss(args[key]);
}
}
}
if (args.themeVariables) {
for (const k of Object.keys(args.themeVariables)) {
const val = args.themeVariables[k];
if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
args.themeVariables[k] = '';
}
}
}
log.debug('After sanitization', args);
};
export const sanitizeCss = (str: string): string => {
let startCnt = 0;
let endCnt = 0;
for (const element of str) {
if (startCnt < endCnt) {
return '{ /* ERROR: Unbalanced CSS */ }';
}
if (element === '{') {
startCnt++;
} else if (element === '}') {
endCnt++;
}
}
if (startCnt !== endCnt) {
return '{ /* ERROR: Unbalanced CSS */ }';
}
// Todo add more checks here
return str;
};