From 7f9beb27809da15774b21736b227f72d9e877371 Mon Sep 17 00:00:00 2001 From: mutoe Date: Fri, 18 Jan 2019 18:29:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(SPA):=20=E8=BF=87=E6=BB=A4=20XSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spa/src/components/FeedCard/FeedCard.vue | 12 +++------ resources/spa/src/filters.js | 25 +++++++++++++++++++ resources/spa/src/page/feed/FeedDetail.vue | 11 +++----- resources/spa/src/style/base.less | 3 +++ 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/resources/spa/src/components/FeedCard/FeedCard.vue b/resources/spa/src/components/FeedCard/FeedCard.vue index 4ef639d44..e15f245e4 100644 --- a/resources/spa/src/components/FeedCard/FeedCard.vue +++ b/resources/spa/src/components/FeedCard/FeedCard.vue @@ -20,7 +20,7 @@

@@ -94,7 +94,7 @@ import { mapState } from 'vuex' import FeedImage from './FeedImage.vue' import FeedVideo from './FeedVideo.vue' import CommentItem from './CommentItem.vue' -import { time2txt } from '@/filters.js' +import { time2txt, escapeHTML } from '@/filters.js' import * as api from '@/api/feeds.js' export default { @@ -212,12 +212,8 @@ export default { }, methods: { replaceURI (str) { - // 脚本内容以纯文本方式显示 - const scriptRegex = /<\s*script\s*>(.*?)<\s*\/\s*script\s*>/i - str = str.replace(scriptRegex, '<script>$1</script>') - - // 换行符转换 - str = str.replace(/\n/g, '
') + // XSS filter + str = escapeHTML(str) const reg = /(https?|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g return str diff --git a/resources/spa/src/filters.js b/resources/spa/src/filters.js index 666e46872..a697033d5 100644 --- a/resources/spa/src/filters.js +++ b/resources/spa/src/filters.js @@ -12,6 +12,31 @@ export function plusMessageFirst (message, defaultMessage) { return plueMessageBundle(message, defaultMessage).getMessage() } +/** + * 过滤 XSS + * + * @author mutoe + * @export + * @param {string} value + * @returns {string} + */ +export function escapeHTML (value) { + if (typeof value !== 'string') { + return value + } + return value.replace(/[&<>`"'/]/g, function (result) { + return { + '&': '&', + '<': '<', + '>': '>', + '`': '`', + '"': '"', + "'": ''', + '/': '/', + }[result] + }) +} + /** * ThinkSNS Plus 消息解析器,获取顶部消息. * diff --git a/resources/spa/src/page/feed/FeedDetail.vue b/resources/spa/src/page/feed/FeedDetail.vue index 5d6f319b0..663abe3f0 100644 --- a/resources/spa/src/page/feed/FeedDetail.vue +++ b/resources/spa/src/page/feed/FeedDetail.vue @@ -50,7 +50,7 @@ @click="onFileClick(img)" > -

+

@@ -124,6 +124,7 @@ import ArticleCard from '@/page/article/ArticleCard.vue' import CommentItem from '@/page/article/ArticleComment.vue' import ArticleLikeBadge from '@/components/common/ArticleLikeBadge.vue' import ArticleRewardBadge from '@/components/common/ArticleRewardBadge.vue' +import { escapeHTML } from '@/filters' export default { name: 'FeedDetail', @@ -293,12 +294,8 @@ export default { }, methods: { formatBody (str) { - // 脚本内容以纯文本方式显示 - const scriptRegex = /<\s*script\s*>(.*?)<\s*\/\s*script\s*>/i - str = str.replace(scriptRegex, '<script>$1</script>') - - // 换行符转换 - str = str.replace(/\n/g, '
') + // XSS filter + str = escapeHTML(str) const reg = /(https?|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g return str diff --git a/resources/spa/src/style/base.less b/resources/spa/src/style/base.less index 8616d479f..774383317 100644 --- a/resources/spa/src/style/base.less +++ b/resources/spa/src/style/base.less @@ -231,6 +231,9 @@ body, word-wrap: break-word; word-break: break-all; } +.m-text-pre { + white-space: pre-wrap; +} .m-text-box > * { margin: 0 0 0.375 * 16px 0;