npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@blueking/xss-filter

v0.0.10

Published

蓝鲸 XSS 过滤工具

Downloads

1,271

Readme

蓝鲸 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>

配置全局选项

以 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>');

// 过滤掉 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 标签和属性,只保留标签内的纯文本。

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

interface XssOptions {
  // 白名单,不在白名单上的标签将被过滤,不在白名单上的属性也会被过滤
  whiteList?: Record<string, 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,
}