|
@@ -2,7 +2,7 @@ import growiDirective from '@growi/remark-growi-directive';
|
|
|
import type { Schema as SanitizeOption } from 'hast-util-sanitize';
|
|
import type { Schema as SanitizeOption } from 'hast-util-sanitize';
|
|
|
import katex from 'rehype-katex';
|
|
import katex from 'rehype-katex';
|
|
|
import raw from 'rehype-raw';
|
|
import raw from 'rehype-raw';
|
|
|
-import sanitize, { defaultSchema as rehypeSanitizeDefaultSchema } from 'rehype-sanitize';
|
|
|
|
|
|
|
+import sanitize from 'rehype-sanitize';
|
|
|
import slug from 'rehype-slug';
|
|
import slug from 'rehype-slug';
|
|
|
import breaks from 'remark-breaks';
|
|
import breaks from 'remark-breaks';
|
|
|
import emoji from 'remark-emoji';
|
|
import emoji from 'remark-emoji';
|
|
@@ -16,17 +16,19 @@ import type { Pluggable, PluginTuple } from 'unified';
|
|
|
|
|
|
|
|
import { CodeBlock } from '~/components/ReactMarkdownComponents/CodeBlock';
|
|
import { CodeBlock } from '~/components/ReactMarkdownComponents/CodeBlock';
|
|
|
import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
|
|
import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
|
|
|
-import { RehypeSanitizeOption } from '~/interfaces/rehype';
|
|
|
|
|
import type { RendererOptions } from '~/interfaces/renderer-options';
|
|
import type { RendererOptions } from '~/interfaces/renderer-options';
|
|
|
|
|
+import { RehypeSanitizeType } from '~/interfaces/services/rehype-sanitize';
|
|
|
import type { RendererConfig } from '~/interfaces/services/renderer';
|
|
import type { RendererConfig } from '~/interfaces/services/renderer';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
|
|
|
|
+import { tagNames as recommendedTagNames, attributes as recommendedAttributes } from './recommended-whitelist';
|
|
|
import * as addClass from './rehype-plugins/add-class';
|
|
import * as addClass from './rehype-plugins/add-class';
|
|
|
import { relativeLinks } from './rehype-plugins/relative-links';
|
|
import { relativeLinks } from './rehype-plugins/relative-links';
|
|
|
import { relativeLinksByPukiwikiLikeLinker } from './rehype-plugins/relative-links-by-pukiwiki-like-linker';
|
|
import { relativeLinksByPukiwikiLikeLinker } from './rehype-plugins/relative-links-by-pukiwiki-like-linker';
|
|
|
import { pukiwikiLikeLinker } from './remark-plugins/pukiwiki-like-linker';
|
|
import { pukiwikiLikeLinker } from './remark-plugins/pukiwiki-like-linker';
|
|
|
import * as xsvToTable from './remark-plugins/xsv-to-table';
|
|
import * as xsvToTable from './remark-plugins/xsv-to-table';
|
|
|
|
|
|
|
|
|
|
+
|
|
|
// import EasyGrid from './PreProcessor/EasyGrid';
|
|
// import EasyGrid from './PreProcessor/EasyGrid';
|
|
|
|
|
|
|
|
|
|
|
|
@@ -36,31 +38,18 @@ const logger = loggerFactory('growi:services:renderer');
|
|
|
|
|
|
|
|
type SanitizePlugin = PluginTuple<[SanitizeOption]>;
|
|
type SanitizePlugin = PluginTuple<[SanitizeOption]>;
|
|
|
|
|
|
|
|
-const baseSanitizeSchema = {
|
|
|
|
|
- tagNames: ['iframe', 'section', 'video'],
|
|
|
|
|
- attributes: {
|
|
|
|
|
- iframe: ['allow', 'referrerpolicy', 'sandbox', 'src', 'srcdoc'],
|
|
|
|
|
- video: ['controls', 'src', 'muted', 'preload', 'width', 'height', 'autoplay'],
|
|
|
|
|
- // The special value 'data*' as a property name can be used to allow all data properties.
|
|
|
|
|
- // see: https://github.com/syntax-tree/hast-util-sanitize/
|
|
|
|
|
- '*': ['key', 'class', 'className', 'style', 'data*'],
|
|
|
|
|
- },
|
|
|
|
|
|
|
+export const commonSanitizeOption: SanitizeOption = {
|
|
|
|
|
+ tagNames: recommendedTagNames,
|
|
|
|
|
+ attributes: recommendedAttributes,
|
|
|
|
|
+ clobberPrefix: '', // remove clobber prefix
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-export const commonSanitizeOption: SanitizeOption = deepmerge(
|
|
|
|
|
- rehypeSanitizeDefaultSchema,
|
|
|
|
|
- baseSanitizeSchema,
|
|
|
|
|
- {
|
|
|
|
|
- clobberPrefix: '', // remove clobber prefix
|
|
|
|
|
- },
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
let isInjectedCustomSanitaizeOption = false;
|
|
let isInjectedCustomSanitaizeOption = false;
|
|
|
|
|
|
|
|
export const injectCustomSanitizeOption = (config: RendererConfig): void => {
|
|
export const injectCustomSanitizeOption = (config: RendererConfig): void => {
|
|
|
- if (!isInjectedCustomSanitaizeOption && config.isEnabledXssPrevention && config.xssOption === RehypeSanitizeOption.CUSTOM) {
|
|
|
|
|
- commonSanitizeOption.tagNames = baseSanitizeSchema.tagNames.concat(config.tagWhitelist ?? []);
|
|
|
|
|
- commonSanitizeOption.attributes = deepmerge(baseSanitizeSchema.attributes, config.attrWhitelist ?? {});
|
|
|
|
|
|
|
+ if (!isInjectedCustomSanitaizeOption && config.isEnabledXssPrevention && config.sanitizeType === RehypeSanitizeType.CUSTOM) {
|
|
|
|
|
+ commonSanitizeOption.tagNames = config.customTagWhitelist ?? recommendedTagNames;
|
|
|
|
|
+ commonSanitizeOption.attributes = config.customAttrWhitelist ?? recommendedAttributes;
|
|
|
isInjectedCustomSanitaizeOption = true;
|
|
isInjectedCustomSanitaizeOption = true;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -142,7 +131,7 @@ export const generateSSRViewOptions = (
|
|
|
remarkPlugins.push(breaks);
|
|
remarkPlugins.push(breaks);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (config.xssOption === RehypeSanitizeOption.CUSTOM) {
|
|
|
|
|
|
|
+ if (config.sanitizeType === RehypeSanitizeType.CUSTOM) {
|
|
|
injectCustomSanitizeOption(config);
|
|
injectCustomSanitizeOption(config);
|
|
|
}
|
|
}
|
|
|
|
|
|