recommended-whitelist.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import { defaultSchema } from 'hast-util-sanitize';
  2. import deepmerge from 'ts-deepmerge';
  3. type Attributes = typeof defaultSchema.attributes;
  4. type ExtractPropertyDefinition<T> =
  5. T extends Record<string, (infer U)[]> ? U : never;
  6. type PropertyDefinition = ExtractPropertyDefinition<NonNullable<Attributes>>;
  7. const excludeRestrictedClassAttributes = (
  8. propertyDefinitions: PropertyDefinition[],
  9. ): PropertyDefinition[] => {
  10. if (propertyDefinitions == null) {
  11. return propertyDefinitions;
  12. }
  13. return propertyDefinitions.filter((propertyDefinition) => {
  14. if (!Array.isArray(propertyDefinition)) {
  15. return true;
  16. }
  17. return (
  18. propertyDefinition[0] !== 'class' && propertyDefinition[0] !== 'className'
  19. );
  20. });
  21. };
  22. // generate relaxed schema
  23. const relaxedSchemaAttributes: Record<string, PropertyDefinition[]> =
  24. structuredClone(defaultSchema.attributes) ?? {};
  25. relaxedSchemaAttributes.a = excludeRestrictedClassAttributes(
  26. relaxedSchemaAttributes.a,
  27. );
  28. relaxedSchemaAttributes.ul = excludeRestrictedClassAttributes(
  29. relaxedSchemaAttributes.ul,
  30. );
  31. relaxedSchemaAttributes.li = excludeRestrictedClassAttributes(
  32. relaxedSchemaAttributes.li,
  33. );
  34. /**
  35. * reference: https://meta.stackexchange.com/questions/1777/what-html-tags-are-allowed-on-stack-exchange-sites,
  36. * https://github.com/jch/html-pipeline/blob/70b6903b025c668ff3c02a6fa382031661182147/lib/html/pipeline/sanitization_filter.rb#L41
  37. */
  38. export const tagNames: Array<string> = [
  39. ...(defaultSchema.tagNames ?? []),
  40. '-',
  41. 'abbr',
  42. 'bdo',
  43. 'bdi',
  44. 'button',
  45. 'caption',
  46. 'cite',
  47. 'col',
  48. 'colgroup',
  49. 'data',
  50. 'dfn',
  51. 'figure',
  52. 'figcaption',
  53. 'iframe',
  54. 'mark',
  55. 'rb',
  56. 'small',
  57. 'time',
  58. 'u',
  59. 'video',
  60. 'wbr',
  61. ];
  62. export const attributes: Attributes = deepmerge(relaxedSchemaAttributes, {
  63. a: ['target'],
  64. abbr: ['title'],
  65. bdo: ['dir'],
  66. caption: [],
  67. cite: [],
  68. dfn: ['title'],
  69. figure: [],
  70. figcaption: [],
  71. iframe: ['allow', 'referrerpolicy', 'sandbox', 'src'],
  72. mark: [],
  73. small: [],
  74. time: ['datetime'],
  75. video: ['controls', 'src', 'muted', 'preload', 'width', 'height', 'autoplay'],
  76. wbr: [],
  77. // The special value 'data*' as a property name can be used to allow all data properties.
  78. // see: https://github.com/syntax-tree/hast-util-sanitize/
  79. '*': ['key', 'class', 'className', 'style', 'role', 'data*'],
  80. });