{"id":13716877,"url":"https://github.com/syntax-tree/hast-util-sanitize","last_synced_at":"2025-07-12T07:37:53.888Z","repository":{"id":57261716,"uuid":"61429819","full_name":"syntax-tree/hast-util-sanitize","owner":"syntax-tree","description":"utility to sanitize hast nodes","archived":false,"fork":false,"pushed_at":"2024-10-25T13:36:16.000Z","size":209,"stargazers_count":55,"open_issues_count":1,"forks_count":21,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-06-02T12:16:29.990Z","etag":null,"topics":["clean","hast","hast-util","html","sanitize","security","syntax-tree","unist","util","xss"],"latest_commit_sha":null,"homepage":"https://unifiedjs.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/syntax-tree.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"unifiedjs","open_collective":"unified","thanks_dev":"u/gh/syntax-tree"}},"created_at":"2016-06-18T11:11:15.000Z","updated_at":"2025-06-01T19:59:17.000Z","dependencies_parsed_at":"2023-02-01T06:31:00.811Z","dependency_job_id":"f966147e-03c9-4ba2-a40e-13c2a4c5dbb6","html_url":"https://github.com/syntax-tree/hast-util-sanitize","commit_stats":{"total_commits":125,"total_committers":6,"mean_commits":"20.833333333333332","dds":0.05600000000000005,"last_synced_commit":"69fcfa33a820ec34df790728b3e0fabbcf6ee066"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/syntax-tree/hast-util-sanitize","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-sanitize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-sanitize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-sanitize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-sanitize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/syntax-tree","download_url":"https://codeload.github.com/syntax-tree/hast-util-sanitize/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-sanitize/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264011006,"owners_count":23543727,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean","hast","hast-util","html","sanitize","security","syntax-tree","unist","util","xss"],"created_at":"2024-08-03T00:01:15.260Z","updated_at":"2025-07-12T07:37:53.850Z","avatar_url":"https://github.com/syntax-tree.png","language":"JavaScript","funding_links":["https://github.com/sponsors/unifiedjs","https://opencollective.com/unified","https://thanks.dev/u/gh/syntax-tree"],"categories":["hast utilities"],"sub_categories":[],"readme":"# hast-util-sanitize\n\n[![Build][build-badge]][build]\n[![Coverage][coverage-badge]][coverage]\n[![Downloads][downloads-badge]][downloads]\n[![Size][size-badge]][size]\n[![Sponsors][sponsors-badge]][collective]\n[![Backers][backers-badge]][collective]\n[![Chat][chat-badge]][chat]\n\n[hast][] utility to make trees safe.\n\n## Contents\n\n* [What is this?](#what-is-this)\n* [When should I use this?](#when-should-i-use-this)\n* [Install](#install)\n* [Use](#use)\n* [API](#api)\n  * [`defaultSchema`](#defaultschema)\n  * [`sanitize(tree[, options])`](#sanitizetree-options)\n  * [`Schema`](#schema)\n* [Types](#types)\n* [Compatibility](#compatibility)\n* [Security](#security)\n* [Related](#related)\n* [Contribute](#contribute)\n* [License](#license)\n\n## What is this?\n\nThis package is a utility that can make a tree that potentially contains\ndangerous user content safe for use.\nIt defaults to what GitHub does to clean unsafe markup, but you can change that.\n\n## When should I use this?\n\nThis package is needed whenever you deal with potentially dangerous user\ncontent.\n\nThe plugin [`rehype-sanitize`][rehype-sanitize] wraps this utility to also\nsanitize HTML at a higher-level (easier) abstraction.\n\n## Install\n\nThis package is [ESM only][esm].\nIn Node.js (version 16+), install with [npm][]:\n\n```sh\nnpm install hast-util-sanitize\n```\n\nIn Deno with [`esm.sh`][esmsh]:\n\n```js\nimport {sanitize} from 'https://esm.sh/hast-util-sanitize@5'\n```\n\nIn browsers with [`esm.sh`][esmsh]:\n\n```html\n\u003cscript type=\"module\"\u003e\n  import {sanitize} from 'https://esm.sh/hast-util-sanitize@5?bundle'\n\u003c/script\u003e\n```\n\n## Use\n\n```js\nimport {h} from 'hastscript'\nimport {sanitize} from 'hast-util-sanitize'\nimport {toHtml} from 'hast-util-to-html'\nimport {u} from 'unist-builder'\n\nconst unsafe = h('div', {onmouseover: 'alert(\"alpha\")'}, [\n  h(\n    'a',\n    {href: 'jAva script:alert(\"bravo\")', onclick: 'alert(\"charlie\")'},\n    'delta'\n  ),\n  u('text', '\\n'),\n  h('script', 'alert(\"charlie\")'),\n  u('text', '\\n'),\n  h('img', {src: 'x', onerror: 'alert(\"delta\")'}),\n  u('text', '\\n'),\n  h('iframe', {src: 'javascript:alert(\"echo\")'}),\n  u('text', '\\n'),\n  h('math', h('mi', {'xlink:href': 'data:x,\u003cscript\u003ealert(\"foxtrot\")\u003c/script\u003e'}))\n])\n\nconst safe = sanitize(unsafe)\n\nconsole.log(toHtml(unsafe))\nconsole.log(toHtml(safe))\n```\n\nUnsafe:\n\n```html\n\u003cdiv onmouseover=\"alert(\u0026#x22;alpha\u0026#x22;)\"\u003e\u003ca href=\"jAva script:alert(\u0026#x22;bravo\u0026#x22;)\" onclick=\"alert(\u0026#x22;charlie\u0026#x22;)\"\u003edelta\u003c/a\u003e\n\u003cscript\u003ealert(\"charlie\")\u003c/script\u003e\n\u003cimg src=\"x\" onerror=\"alert(\u0026#x22;delta\u0026#x22;)\"\u003e\n\u003ciframe src=\"javascript:alert(\u0026#x22;echo\u0026#x22;)\"\u003e\u003c/iframe\u003e\n\u003cmath\u003e\u003cmi xlink:href=\"data:x,\u003cscript\u003ealert(\u0026#x22;foxtrot\u0026#x22;)\u003c/script\u003e\"\u003e\u003c/mi\u003e\u003c/math\u003e\u003c/div\u003e\n```\n\nSafe:\n\n```html\n\u003cdiv\u003e\u003ca\u003edelta\u003c/a\u003e\n\n\u003cimg src=\"x\"\u003e\n\n\u003c/div\u003e\n```\n\n## API\n\nThis package exports the identifiers [`defaultSchema`][api-default-schema] and\n[`sanitize`][api-sanitize].\nThere is no default export.\n\n### `defaultSchema`\n\nDefault schema ([`Schema`][api-schema]).\n\nFollows [GitHub][] style sanitation.\n\n### `sanitize(tree[, options])`\n\nSanitize a tree.\n\n###### Parameters\n\n* `tree` ([`Node`][node])\n  — unsafe tree\n* `options` ([`Schema`][api-schema], default:\n  [`defaultSchema`][api-default-schema])\n  — configuration\n\n###### Returns\n\nNew, safe tree ([`Node`][node]).\n\n### `Schema`\n\nSchema that defines what nodes and properties are allowed.\n\nThe default schema is [`defaultSchema`][api-default-schema], which follows how\nGitHub cleans.\nIf any top-level key is missing in the given schema, the corresponding\nvalue of the default schema is used.\n\nTo extend the standard schema with a few changes, clone `defaultSchema`\nlike so:\n\n```js\nimport deepmerge from 'deepmerge'\nimport {h} from 'hastscript'\nimport {defaultSchema, sanitize} from 'hast-util-sanitize'\n\n// This allows `className` on all elements.\nconst schema = deepmerge(defaultSchema, {attributes: {'*': ['className']}})\n\nconst tree = sanitize(h('div', {className: ['foo']}), schema)\n\n// `tree` still has `className`.\nconsole.log(tree)\n// {\n//   type: 'element',\n//   tagName: 'div',\n//   properties: {className: ['foo']},\n//   children: []\n// }\n```\n\n##### Fields\n\n###### `allowComments`\n\nWhether to allow comment nodes (`boolean`, default: `false`).\n\nFor example:\n\n```js\nallowComments: true\n```\n\n###### `allowDoctypes`\n\nWhether to allow doctype nodes (`boolean`, default: `false`).\n\nFor example:\n\n```js\nallowDoctypes: true\n```\n\n###### `ancestors`\n\nMap of tag names to a list of tag names which are required ancestors\n(`Record\u003cstring, Array\u003cstring\u003e\u003e`, default: `defaultSchema.ancestors`).\n\nElements with these tag names will be ignored if they occur outside of one\nof their allowed parents.\n\nFor example:\n\n```js\nancestors: {\n  tbody: ['table'],\n  // …\n  tr: ['table']\n}\n```\n\n###### `attributes`\n\nMap of tag names to allowed [property names][name]\n(`Record\u003cstring, Array\u003c[string, ...Array\u003cRegExp | boolean | number | string\u003e] | string\u003e`,\ndefault: `defaultSchema.attributes`).\n\nThe special key `'*'` as a tag name defines property names allowed on all\nelements.\n\nThe special value `'data*'` as a property name can be used to allow all `data`\nproperties.\n\nFor example:\n\n```js\nattributes: {\n  a: [\n    'ariaDescribedBy', 'ariaLabel', 'ariaLabelledBy', /* … */, 'href'\n  ],\n  // …\n  '*': [\n    'abbr',\n    'accept',\n    'acceptCharset',\n    // …\n    'vAlign',\n    'value',\n    'width'\n  ]\n}\n```\n\nInstead of a single string in the array, which allows any property value for\nthe field, you can use an array to allow several values.\nFor example, `input: ['type']` allows `type` set to any value on `input`s.\nBut `input: [['type', 'checkbox', 'radio']]` allows `type` when set to\n`'checkbox'` or `'radio'`.\n\nYou can use regexes, so for example `span: [['className', /^hljs-/]]` allows\nany class that starts with `hljs-` on `span`s.\n\nWhen comma- or space-separated values are used (such as `className`), each\nvalue in is checked individually.\nFor example, to allow certain classes on `span`s for syntax highlighting, use\n`span: [['className', 'number', 'operator', 'token']]`.\nThis will allow `'number'`, `'operator'`, and `'token'` classes, but drop\nothers.\n\n###### `clobber`\n\nList of [*property names*][name] that clobber (`Array\u003cstring\u003e`, default:\n`defaultSchema.clobber`).\n\nFor example:\n\n```js\nclobber: ['ariaDescribedBy', 'ariaLabelledBy', 'id', 'name']\n```\n\n###### `clobberPrefix`\n\nPrefix to use before clobbering properties (`string`, default:\n`defaultSchema.clobberPrefix`).\n\nFor example:\n\n```js\nclobberPrefix: 'user-content-'\n```\n\n###### `protocols`\n\nMap of [*property names*][name] to allowed protocols\n(`Record\u003cstring, Array\u003cstring\u003e\u003e`, default: `defaultSchema.protocols`).\n\nThis defines URLs that are always allowed to have local URLs (relative to\nthe current website, such as `this`, `#this`, `/this`, or `?this`), and\nonly allowed to have remote URLs (such as `https://example.com`) if they\nuse a known protocol.\n\nFor example:\n\n```js\nprotocols: {\n  cite: ['http', 'https'],\n  // …\n  src: ['http', 'https']\n}\n```\n\n###### `required`\n\nMap of tag names to required [*property names*][name] with a default value\n(`Record\u003cstring, Record\u003cstring, unknown\u003e\u003e`, default: `defaultSchema.required`).\n\nThis defines properties that must be set.\nIf a field does not exist (after the element was made safe), these will be\nadded with the given value.\n\nFor example:\n\n```js\nrequired: {\n  input: {disabled: true, type: 'checkbox'}\n}\n```\n\n\u003e 👉 **Note**: properties are first checked based on `schema.attributes`,\n\u003e then on `schema.required`.\n\u003e That means properties could be removed by `attributes` and then added\n\u003e again with `required`.\n\n###### `strip`\n\nList of tag names to strip from the tree (`Array\u003cstring\u003e`, default:\n`defaultSchema.strip`).\n\nBy default, unsafe elements (those not in `schema.tagNames`) are replaced by\nwhat they contain.\nThis option can drop their contents.\n\nFor example:\n\n```js\nstrip: ['script']\n```\n\n###### `tagNames`\n\nList of allowed tag names (`Array\u003cstring\u003e`, default: `defaultSchema.tagNames`).\n\nFor example:\n\n```js\ntagNames: [\n  'a',\n  'b',\n  // …\n  'ul',\n  'var'\n]\n```\n\n## Types\n\nThis package is fully typed with [TypeScript][].\nIt exports the additional type [`Schema`][api-schema].\n\n## Compatibility\n\nProjects maintained by the unified collective are compatible with maintained\nversions of Node.js.\n\nWhen we cut a new major release, we drop support for unmaintained versions of\nNode.\nThis means we try to keep the current release line, `hast-util-sanitize@^5`,\ncompatible with Node.js 16.\n\n## Security\n\nBy default, `hast-util-sanitize` will make everything safe to use.\nAssuming you understand that certain attributes (including a limited set of\nclasses) can be generated by users, and you write your CSS (and JS)\naccordingly.\nWhen used incorrectly, deviating from the defaults can open you up to a\n[cross-site scripting (XSS)][xss] attack.\n\nUse `hast-util-sanitize` after the last unsafe thing: everything after it could\nbe unsafe (but is fine if you do trust it).\n\n## Related\n\n* [`rehype-sanitize`](https://github.com/rehypejs/rehype-sanitize)\n  — rehype plugin\n\n## Contribute\n\nSee [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for\nways to get started.\nSee [`support.md`][support] for ways to get help.\n\nThis project has a [code of conduct][coc].\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.\n\n## License\n\n[MIT][license] © [Titus Wormer][author]\n\n\u003c!-- Definitions --\u003e\n\n[build-badge]: https://github.com/syntax-tree/hast-util-sanitize/workflows/main/badge.svg\n\n[build]: https://github.com/syntax-tree/hast-util-sanitize/actions\n\n[coverage-badge]: https://img.shields.io/codecov/c/github/syntax-tree/hast-util-sanitize.svg\n\n[coverage]: https://codecov.io/github/syntax-tree/hast-util-sanitize\n\n[downloads-badge]: https://img.shields.io/npm/dm/hast-util-sanitize.svg\n\n[downloads]: https://www.npmjs.com/package/hast-util-sanitize\n\n[size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size\u0026query=$.size.compressedSize\u0026url=https://deno.bundlejs.com/?q=hast-util-sanitize\n\n[size]: https://bundlejs.com/?q=hast-util-sanitize\n\n[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg\n\n[backers-badge]: https://opencollective.com/unified/backers/badge.svg\n\n[collective]: https://opencollective.com/unified\n\n[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg\n\n[chat]: https://github.com/syntax-tree/unist/discussions\n\n[npm]: https://docs.npmjs.com/cli/install\n\n[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n\n[esmsh]: https://esm.sh\n\n[typescript]: https://www.typescriptlang.org\n\n[license]: license\n\n[author]: https://wooorm.com\n\n[health]: https://github.com/syntax-tree/.github\n\n[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md\n\n[support]: https://github.com/syntax-tree/.github/blob/main/support.md\n\n[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md\n\n[hast]: https://github.com/syntax-tree/hast\n\n[node]: https://github.com/syntax-tree/hast#nodes\n\n[name]: https://github.com/syntax-tree/hast#propertyname\n\n[github]: https://github.com/gjtorikian/html-pipeline/blob/a2e02ac/lib/html_pipeline/sanitization_filter.rb\n\n[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting\n\n[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize\n\n[api-default-schema]: #defaultschema\n\n[api-sanitize]: #sanitizetree-options\n\n[api-schema]: #schema\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsyntax-tree%2Fhast-util-sanitize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsyntax-tree%2Fhast-util-sanitize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsyntax-tree%2Fhast-util-sanitize/lists"}