@blueking/xss-filter

蓝鲸 XSS 过滤工具
11,482
Install
npm install @blueking/xss-filter
Latest Version:0.0.11
License:MIT
Last Updated:Oct 12, 2026
Links: npm
Maintainer: blueking-magicbox

蓝鲸 XSS 过滤工具

一个基于 xss 库封装轻量级的工具,用于过滤字符串或 JSON 中 HTML 内容片段的 XSS(跨站脚本)攻击。支持 Vue(Vue 2 和 Vue 3)指令,或者独立使用过滤方法。

安装

npm install @blueking/xss-filter

使用

Vue 指令

注册指令
// Vue 3
import { createApp } from 'vue';
import { BkXssFilterDirective } from '@blueking/xss-filter';
import App from './app.vue';

createApp(App).use(BkXssFilterDirective).mount('#app');

// Vue 2
import Vue from 'vue';
import { BkXssFilterDirective } from '@blueking/xss-filter';

Vue.use(BkXssFilterDirective);

new Vue({
  render: h => h(App),
}).$mount('#app');
在模板中使用
<!-- 默认 -->
<div v-bk-xss-html="userContent"></div>

<!-- 纯文本模式,只保留html内容中标签内的纯文本 -->
<div v-bk-xss-html.plain="userContent"></div>

<!-- 传入对象模式(自定义选项) -->
<div v-bk-xss-html="{ content: userContent, options: { whiteList: { p: [] } } }"></div>

<!-- 黑名单:去掉 a 标签及其内容(对纯文本模式同样生效) -->
<div v-bk-xss-html="{ content: userContent, options: { blackList: ['a'] } }"></div>

whiteList 会与内置白名单深合并,只能新增标签或属性,无法移除内置标签。如需禁用内置白名单中的标签(如 a),请使用 blackList。

指令中单次传入的 options 与全局 defaultOptions 为浅合并,单次传入的 blackList 会整体覆盖全局配置中的 blackList。

配置全局选项

以 vue 3 项目为例:

import { createApp } from 'vue';
import App from './App.vue';
import { BkXssFilterDirective } from '@blueking/xss-filter';
import type { XssDirectiveConfig } from '@blueking/xss-filter';

const app = createApp(App);

// 安装指令并配置全局选项
app.use(BkXssFilterDirective, {
  defaultOptions: {
    // 全局 XSS 配置选项
    whiteList: {
      a: ['href', 'title', 'target', 'rel']
    }
  },
  plainModeByDefault: false // 默认使用富文本模式
} as XssDirectiveConfig);

app.mount('#app');

独立使用过滤方法

从模块中导入函数:

import { filterXss, deepFilterXss, filterPlainText } from '@blueking/xss-filter';

// 过滤掉所有非白名单内的 HTML 标签和属性
const safeString = filterXss('<script>alert("XSS")</script>');

// 过滤掉所有 HTML 标签和属性,只保留标签内的纯文本
const safeText = filterPlainText('<script>alert("XSS")</script>');

// 通过黑名单去掉 a 标签及其内容,其余白名单标签照常保留
const noLink = filterXss('<p>前<a href="https://x.com">链接</a>后</p>', { blackList: ['a'] }); // <p>前后</p>

// 过滤掉 JSON 中 HTML 片段,默认采用 filterPlainText 方法只保留 HTML 标签内的纯文本,可配置为filterXss并传入自定义过滤选项
const safeObject = deepFilterXss({ html: '<script>alert("XSS")</script>' });

API 参考

函数

  • filterXss(html: string, options?: XssOptions): string 过滤掉所有非白名单内的 HTML 标签和属性。

  • filterPlainText(text: string, options?: XssOptions): string 过滤掉所有 HTML 标签和属性,只保留标签内的纯文本。options 中仅 blackList 生效,命中的标签内容也会被去掉。

  • deepFilterXss(obj: T, filterFn?: FilterFunction = filterPlainText, options?: XssOptions): T 过滤掉 JSON 中 HTML 片段,默认采用filterPlainText方法只保留 HTML 标签内的纯文本,可配置为filterXss并传入自定义过滤选项。

interface XssOptions {
  // 白名单,不在白名单上的标签将被过滤,不在白名单上的属性也会被过滤
  whiteList?: Record<string, string[]>;

  // 黑名单,优先级高于白名单,命中的标签会连同标签内的内容一起被去掉
  blackList?: string[];

  // 去掉不在白名单上的标签,但保留标签里的纯文本内容
  stripIgnoreTag?: boolean;

  // 去掉不在白名单上的标签及里面的纯文本内容
  stripIgnoreTagBody?: boolean | string[];

  // 去掉 HTML 备注
  allowCommentTag?: boolean;

  // 自定义匹配到标签时的处理方法
  // tag是当前的标签名称,html是该标签的的html。如果返回字符串,则当前标签将被替换为该字符串;如果不返回任何值,则使用默认处理方法(在白名单上:通过onTagAttr来过滤属性;不在白名单上:通过onIgnoreTag指定)
  onTag?: (tag: string, html: string, options: Record<string, boolean | number>) => string | undefined;

  // 自定义匹配到标签的属性时的处理方法
  // tag是当前的标签名称,name是当前属性名称,value是属性值,isWhiteAttr表示是否为白名单上属性
  // 如果返回字符串,则当前属性值会被替换为该字符串;如果不返回任何值,则使用默认处理方法(在白名单上:调用safeAttrValue来过滤属性值,并输出该属性;不在白名单上:通过onIgnoreTagAttr指定)
  onTagAttr?: (tag: string, name: string, value: string, isWhiteAttr: boolean) => string | undefined;

  // 自定义匹配到不在白名单上的标签时的处理方法
  // 参数与onTag相同。如果返回字符串,则当前标签将被替换为该字符串;如果不返回任何值,则使用默认处理方法(通过escape指定)
  onIgnoreTag?: (tag: string, html: string, options: Record<string, boolean | number>) => string | undefined;

  // 自定义匹配到不在白名单上的标签时的处理方法
  // 参数与onTagAttr相同。如果返回字符串,则当前属性值会被替换为该字符串;如果不返回任何值,则使用默认处理方法(删除该属性)
  onIgnoreTagAttr?: (tag: string, name: string, value: string, isWhiteAttr: boolean) => string | undefined;

  // 自定义 HTML 转义函数
  // 默认返回 html.replace(/</g, "&lt;").replace(/>/g, "&gt;");,不建议修改
  escapeHtml?: (html: string) => string;

  // 自定义标签属性值的转义函数
  // 参数与onTagAttr相同。返回一个值表示该属性值
  safeAttrValue?: (tag: string, name: string, value: string) => string | undefined;

  css?: boolean | CssFilterOptions;

  imgSrcMode?: 'base64' | string[] | 'none'; // 支持三种模式:base64、信任域名、不限制
}

// style属性值过滤接口
interface CssFilterOptions {
  // css属性白名单
  whiteList?: {
    [key: string]: boolean | ((value: string) => boolean) | RegExp;
  };

  // 自定义匹配到在白名单上的属性时的处理方法
  // 返回字符串表示覆盖此段CSS,不返回任何值表示使用默认生成方法,即name:value
  onAttr?: (name: string, value: string, options) => string | undefined;

  // 自定义匹配到不在白名单上的属性时的处理方法
  // 返回字符串表示覆盖此段CSS,不返回任何值表示使用默认生成方法,即去掉此段CSS
  onIgnoreAttr?: (name: string, value: string, options) => string | undefined;
}

内置默认的白名单配置

// 标签属性白名单
{
  "a": ["href", "title", "target", "rel", "id", "class", "style"],
  "aside": ["id", "class", "style"],
  "audio": ["src", "autoplay", "controls", "loop", "muted"],
  "b": [],
  "blockquote": ["id", "class", "style"],
  "body": ["id", "class", "style"],
  "br": [],
  "button": ["type", "id", "class", "style"],
  "canvas": ["width", "height"],
  "code": ["id", "class"],
  "div": ["id", "class", "style"],
  "em": [],
  "h1": ["id", "class", "style"],
  "h2": ["id", "class", "style"],
  "h3": ["id", "class", "style"],
  "h4": ["id", "class", "style"],
  "h5": ["id", "class", "style"],
  "h6": ["id", "class", "style"],
  "hr": [],
  "i": [],
  "img": ["src", "alt", "title", "width", "height", "id", "class", "style"],
  "li": ["id", "class", "style"],
  "nav": ["id", "class", "style"],
  "ol": ["id", "class", "style"],
  "p": ["id", "class", "style"],
  "pre": ["class"],
  "s": [],
  "section": ["id", "class", "style"],
  "span": ["id", "class", "style"],
  "strong": [],
  "style": [],
  "table": ["id", "class", "style", "border", "cellspacing", "cellpadding"],
  "textarea": ["id", "class", "style"],
  "tbody": ["id", "class", "style"],
  "td": ["id", "class", "style", "colspan", "rowspan"],
  "th": ["id", "class", "style", "colspan", "rowspan"],
  "thead": ["id", "class", "style"],
  "tr": ["id", "class", "style"],
  "u": [],
  "ul": ["id", "class", "style"],
  "video": ["src", "autoplay", "controls", "loop", "muted"]
}
// style属性值白名单
{
  'background': true,
  'background-attachment': true,
  'background-clip': true,
  'background-color': true,
  'background-image': true,
  'background-origin': true,
  'background-position': true,
  'background-repeat': true,
  'background-size': true,
  'border': true,
  'border-bottom': true,
  'border-bottom-color': true,
  'border-bottom-left-radius': true,
  'border-bottom-right-radius': true,
  'border-bottom-style': true,
  'border-bottom-width': true,
  'border-collapse': true,
  'border-color': true,
  'border-image': true,
  'border-image-outset': true,
  'border-image-repeat': true,
  'border-image-slice': true,
  'border-image-source': true,
  'border-image-width': true,
  'border-left': true,
  'border-left-color': true,
  'border-left-style': true,
  'border-left-width': true,
  'border-radius': true,
  'border-right': true,
  'border-right-color': true,
  'border-right-style': true,
  'border-right-width': true,
  'border-spacing': true,
  'border-style': true,
  'border-top': true,
  'border-top-color': true,
  'border-top-left-radius': true,
  'border-top-right-radius': true,
  'border-top-style': true,
  'border-top-width': true,
  'border-width': true,
  'box-decoration-break': true,
  'box-shadow': true,
  'box-sizing': true,
  'box-snap': true,
  'box-suppress': true,
  'break-after': true,
  'break-before': true,
  'break-inside': true,
  'clear': true,
  'color': true,
  'color-interpolation-filters': true,
  'display': true,
  'display-inside': true,
  'display-list': true,
  'display-outside': true,
  'font': true,
  'font-family': true,
  'font-feature-settings': true,
  'font-kerning': true,
  'font-language-override': true,
  'font-size': true,
  'font-size-adjust': true,
  'font-stretch': true,
  'font-style': true,
  'font-synthesis': true,
  'font-variant': true,
  'font-variant-alternates': true,
  'font-variant-caps': true,
  'font-variant-east-asian': true,
  'font-variant-ligatures': true,
  'font-variant-numeric': true,
  'font-variant-position': true,
  'font-weight': true,
  'height': true,
  'letter-spacing': true,
  'lighting-color': true,
  'list-style': true,
  'list-style-image': true,
  'list-style-position': true,
  'list-style-type': true,
  'margin': true,
  'margin-bottom': true,
  'margin-left': true,
  'margin-right': true,
  'margin-top': true,
  'max-height': true,
  'max-width': true,
  'min-height': true,
  'min-width': true,
  'padding': true,
  'padding-bottom': true,
  'padding-left': true,
  'padding-right': true,
  'padding-top': true,
  'text-align': true,
  'text-align-last': true,
  'text-combine-upright': true,
  'text-decoration': true,
  'text-decoration-color': true,
  'text-decoration-line': true,
  'text-decoration-skip': true,
  'text-decoration-style': true,
  'text-emphasis': true,
  'text-emphasis-color': true,
  'text-emphasis-position': true,
  'text-emphasis-style': true,
  'text-height': true,
  'text-indent': true,
  'text-justify': true,
  'text-orientation': true,
  'text-overflow': true,
  'text-shadow': true,
  'text-space-collapse': true,
  'text-transform': true,
  'text-underline-position': true,
  'text-wrap': true,
  'width': true,
  'word-break': true,
  'word-spacing': true,
  'word-wrap': true,
}

Related Packages

@benedictleejh/nuxt-sanitise-html

A Nuxt module to sanitise HTML to protect against XSS attacks

1,732 1
@radya/nuxt-dompurify

A Nuxt 3 module that integrates DOMPurify for sanitizing HTML content and protec...

28,438 8
purrrify

A Nuxt module that integrates sanitize-html for sanitizing HTML content and prot...

2,066 4
vue-dompurify-html

Safe replacement for the v-html directive

6,276,753 334
vue-sanitize-escape

Vue.js plugin for sanitizing and escaping user input to protect against XSS, SQL...

8,913 0

Version History

Version Released License
0.0.11 MIT
0.0.10 MIT
0.0.9 MIT
0.0.8 MIT
0.0.7 MIT