Review policy of inserting first party DOM - trying to port userscript to webextension

I’m trying to port an userscript to webextension since the script won’t work on GM4+ due to the changing of API. I had read some policies of webextensions on MDN. And I want to make sure how to port one of its functionality:

The function is about generating the feed list (timeline). The userscript would fetch feed lists of what user had followed from multiple first party source, and try to merge all the feeds in lists by the time it posted. While doing so, the userscript would use xhr to fetch the feed list pages, find out its feeds, sort them by time, and insert to current page. And the url fetched by xhr would be first party page (same domain).

I’m trying to port this piece of userscript to webextension’s page script (or content script).

To me, I think it is secure enough since: It only load first party content. It won’t making the website gained any privileges. It won’t be tracked by any third party. (First party may always track the usage of these extensions which had modified there pages’ content.) It just make the page works as what it should be.

But I wonder whether it will be rejected simply due to inserting remote contents. If so, what’s the best alternative to me?


For reference purposes, the userscript may be found here if you want more details.

You shouldn’t insert remote content (html/scripts) in privileged pages (i.e. the background page and your extensions pages). In content pages it is generally acceptable.

You still shouldn’t eval anything you XHRed that wasn’t meant as a script and you should sanatize everything you insert into the pages DOM (better don’t rely on the server doing that).
Depending on the HTML content you want to process, that can be pretty simple:

/**
 * Sanitizes untrusted HTML by escaping everything that is not recognized as a whitelisted tag or entity.
 * @param  {string}  html  Untrusted HTML input.
 * @return {string}        Sanitized HTML that won't contain any tags but those whitelisted by `rTag` below.
 * @author Niklas Gollenstede
 * @license MIT
 */
function sanitize(html) {
	const parts = (html ? html +'' : '').split(rTag);
	return parts.map((s, i) => i % 2 ? s : s.replace(rEsc, c => oEsc[c])).join('');
} // `rTag` must not contain any capturing groups except the one wrapping the entire expression
const rTag = /(&(?:[A-Za-z]+|#\d+|#x[0-9A-Ea-e]+);|<\/?(?:a|abbr|b|br|code|details|em|i|p|pre|kbd|li|ol|ul|small|spam|span|strong|summary|sup|sub|tt|var)(?: download(?:="[^"]*")?)?(?: href="(?!(?:javascript|data):)[^\s"]*?")?(?: title="[^"]*")?>)/;
const oEsc = { '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;', '/': '&#47;', }, rEsc = new RegExp('['+ Object.keys(oEsc).join('') +']', 'g');

Not so easy to my situation. I cannot sanitize the content, otherwise it will just break. I’m only sure the server response me a list of feeds. But I have no idea of the dom details of feeds. And I want merge feeds from other pages to the current one. What I currently doing is simply insert it into the webpage. Any sanitize tool would just break it and make it unusable.

What the userscript currently do (simplified):

const listInCurrentPage = document.querySelector('[node-type="feed_list"]');
const requestUrl = new URL(location.href);
requestUrl.searchParams.set('gid', 123456)
const serverResponse = await fetch(requestUrl, { foo: 'bar' });
const listFromAnotherPage = new DOMParser().parseFromString(serverResponse, 'text/html').querySelectorAll('.WB_feed_type');
const feedToInsert = listFromAnotherPage.shift();
listInCurrentPage.appendChild(feedToInsert);
A sample response from server
<div class="WB_feed WB_feed_v3 WB_feed_v4" pagenum="1" node-type="feed_list">


<div mrid="rid=5_0_202_2633229399672539576_0_0" tbinfo="ouid=1935392370" diss-data="group_source=group_gid&amp;rid=5_0_202_2633229399672539576_0_0" class="WB_cardwrap WB_feed_type S_bg2 WB_feed_vipcover WB_feed_like" mid="4280506537884612" action-type="feed_list_item">
            <div class="WB_feed_detail clearfix" node-type="feed_content" style="background-image:url(//img.t.sinajs.cn/t6/skin/public/feed_cover/star_095_pc_x2.png?version=201809041345)">
                    <div class="WB_starcover">
                                    <a href="javascript:void(0)" action-data="id=star_095&amp;isvip=0" action-type="fl_cardCover" suda-uatrack="key=vip_background&amp;value=feed_background_click"></a>
                            </div>
                
            <div class="WB_screen W_fr" node-type="fl_screen_box">
    
                            <div class="screen_box">
                                            <a href="javascript:void(0);" action-type="fl_menu"><i class="W_ficon ficon_arrow_down S_ficon">c</i></a>
<div class="layer_menu_list" node-type="fl_menu_right" style="display:none; position: absolute; z-index: 999;">
    <ul>
                                           <li><a action-type="thrid_rend_iframe" href="javascript:void(0)" suda-data="" action-data="width=660&amp;height=556&amp;mid=4280506537884612&amp;src=%2F%2Fpromote.vip.weibo.com%2Fpromoteadvance%3Fdai_tou%3Dpc_group_01%26touid%3D1935392370%26mid%3D4280506537884612&amp;title=推广" title="帮上头条">帮上头条</a></li>
                                                <li><a action-type="feed_list_shield_by_rootmid" href="javascript:void(0)" suda-data="key=smart_feed&amp;value=hidden_feed" action-data="filter_type=0&amp;mid=4280506537884612&amp;justhide=0" title="屏蔽这条微博">屏蔽这条微博</a></li>
            <li><a action-type="feed_list_shield_by_user" href="javascript:void(0)" suda-data="key=smart_feed&amp;value=block_sbsfeed" action-data="filter_type=1&amp;uid=1935392370&amp;nickname=FPS罗兹&amp;gender=m" title="屏蔽 FPS罗兹">屏蔽 FPS罗兹</a></li>                                                                            <li><a href="javascript:void(0)" action-data="id=star_095&amp;isvip=0" action-type="fl_cardCover" suda-uatrack="key=vip_background&amp;value=feed_background_click" title="用此卡片背景">用此卡片背景</a></li>
                        
                                    <li><a href="javascript:void(0);" onclick="javascript:window.open('http://service.account.weibo.com/reportspam?rid=4280506537884612&amp;type=1&amp;from=10101&amp;url=&amp;bottomnav=1&amp;wvr=6', 'newwindow', 'height=700, width=550, toolbar =yes, menubar=no, scrollbars=yes, resizable=yes, location=no, status=no');">举报</a></li>
                            </ul>
            <ul style="display:none;" node-type="hide">
            	    		                                    
    </ul>
             </div>
                                    </div>
                    </div>
        <div class="WB_face W_fl">
                <div class="face"><a target="_blank" class="W_face_radius" suda-uatrack="key=feed_headnick&amp;value=pubuser_head:4280506537884612" href="/fpsluozi?refer_flag=0000015012_&amp;from=feed&amp;loc=avatar" title="FPS罗兹"><img usercard="id=1935392370&amp;refer_flag=0000015012_" title="FPS罗兹" alt="" src="//tva3.sinaimg.cn/crop.0.0.249.249.180/735bbe72jw1e7sgy47t36j206y06ydgj.jpg" class="W_face_radius" width="50" height="50"></a></div>
                                                            </div>
        <div class="WB_detail">
            <div class="WB_info">
                <a suda-uatrack="key=feed_headnick&amp;value=pubuser_nick:4280506537884612" target="_blank" class="W_f14 W_fb S_txt1" nick-name="FPS罗兹" title="FPS罗兹" href="/fpsluozi?refer_flag=0000015012_&amp;from=feed&amp;loc=nickname" usercard="id=1935392370&amp;refer_flag=0000015012_">FPS罗兹</a><a target="_blank" suda-data="key=pc_apply_entry&amp;value=feed_icon" href="http://club.weibo.com/intro"><i title="微博达人" class="W_icon icon_club" node-type="daren"></i></a><a action-type="ignore_list" suda-uatrack="key=home_vip&amp;value=home_feed_vip" title="微博会员" target="_blank" href="http://vip.weibo.com/personal?from=main"><em class="W_icon icon_member7"></em></a>            </div>
            <div class="WB_from S_txt2">
                <!-- minzheng add part 2 -->
                                                            <a target="_blank" href="/1935392370/GxMlfx59a?ref=home&amp;rid=5_0_202_2633229399672539576_0_0" title="2018-09-04 12:35" date="1536035749000" class="S_txt2" node-type="feed_list_item_date" suda-data="key=tblog_home_new&amp;value=feed_time:4280506537884612:fromfzfeed">今天 12:35</a> 来自 <a class="S_txt2" suda-data="key=tblog_home_new&amp;value=feed_come_from" action-type="app_source" target="_blank" href="http://app.weibo.com/t/feed/6vtZb0" rel="nofollow">微博 weibo.com</a>                                                    <!-- minzheng add part 2 -->
                            </div>
                        <div class="PCD_user_b S_bg1" node-type="follow_recommend_box" style="display:none"></div>
            <div class="WB_text W_f14" node-type="feed_list_content">
                                                        肝俩晚上都没肝完 明天又要上课了咋整<img class="W_img_face" render="ext" src="//img.t.sinajs.cn/t4/appstyle/expression/ext/normal/83/2018new_kuxiao_org.png" title="[允悲]" alt="[允悲]" type="face" style="visibility: visible;"> ​​​​                            </div>
                                                <div class="WB_expand_media_box " style="display: none;" node-type="feed_list_media_disp"></div>
                                                                    <!-- 引用文件时,必须对midia_info赋值 -->
<!-- 微博心情,独立于标准的ul节点 -->

                    <div class="WB_media_wrap clearfix" node-type="feed_list_media_prev">
        <div class="media_box">
                                                <!--图片个数等于1,只显示图片-->
                    							    <!--picture_count == 1-->
    <ul class="WB_media_a  WB_media_a_m1 clearfix" action-data="isPrivate=0&amp;relation=0&amp;clear_picSrc=%2F%2Fwx4.sinaimg.cn%2Fmw690%2F735bbe72ly1fuxehj9dzsj20lq09nwie.jpg">
                        <li class="WB_pic li_1 S_bg1 S_line2 bigcursor " style="width:296px;height:222px" action-data="isPrivate=0&amp;relation=0&amp;pid=735bbe72ly1fuxehj9dzsj20lq09nwie&amp;object_ids=1042018%3A4980cb5898ac9a5a6a25d22d5b9445ea&amp;photo_tag_pids=&amp;uid=1935392370&amp;mid=4280506537884612&amp;pic_ids=735bbe72ly1fuxehj9dzsj20lq09nwie&amp;pic_objects=" action-type="feed_list_media_img" suda-uatrack="key=tblog_newimage_feed&amp;value=image_feed_unfold:4280506537884612:735bbe72ly1fuxehj9dzsj20lq09nwie:1935392370">
                             <img src="//wx4.sinaimg.cn/orj360/735bbe72ly1fuxehj9dzsj20lq09nwie.jpg">
            <i class="W_loading" style="display:none;"></i>
                  
            <i class="W_loading" style="display:none;"></i>
                    </li>
    </ul>
                                    </div>
    </div>
         
<!-- super topic card -->
                                                                                                                <!-- feed区 大数据tag -->
<!-- /feed区 大数据tag -->                    </div>
        <div class="WB_like" node-type="templeLike_ani" action-data="parise_id=p_0000" style="display:none;">
            <div class="anibox UI_ani" style="background-image:url(//img.t.sinajs.cn/t6/skin/public/like/p_0000_pc.png?version=201809041345);"></div>
        </div>
    </div>
        <!-- minzheng add part 3 -->
            <div class="WB_feed_handle">
            <div class="WB_handle">
                <ul class="WB_row_line WB_row_r4 clearfix S_line2">
                    <li>
                                            <a class="S_txt2" suda-data="key=smart_feed&amp;value=time_sort_collect" href="javascript:void(0);" diss-data="fuid=1935392370" action-type="fl_favorite"><span class="pos"><span class="line S_line1" node-type="favorite_btn_text"><span><em class="W_ficon ficon_favorite S_ficon">û</em><em>收藏</em></span></span></span></a>
                                        </li>
                                            <li>
                            <a class="S_txt2" suda-data="key=smart_feed&amp;value=time_sort_tran:4280506537884612" href="javascript:void(0);" action-history="rec=1" action-type="fl_forward" action-data="allowForward=1&amp;url=https://weibo.com/1935392370/GxMlfx59a&amp;mid=4280506537884612&amp;name=FPS罗兹&amp;uid=1935392370&amp;domain=fpsluozi&amp;pid=735bbe72ly1fuxehj9dzsj20lq09nwie"><span class="pos"><span class="line S_line1" node-type="forward_btn_text"><span><em class="W_ficon ficon_forward S_ficon"></em><em>转发</em></span></span></span></a>
                        </li>
                                        <li>
                                                <a class="S_txt2" suda-data="key=smart_feed&amp;value=time_sort_comm:4280506537884612" href="javascript:void(0);" action-type="fl_comment" action-data="ouid=1935392370&amp;location=home"><span class="pos"><span class="line S_line1" node-type="comment_btn_text"><span><em class="W_ficon ficon_repeat S_ficon"></em><em>4</em></span></span></span></a>
                        <span class="arrow" style="display: none;" node-type="cmtarrow"><span class="W_arrow_bor W_arrow_bor_t"><i class="S_line1"></i><em class="S_bg1_br"></em></span></span>
                                                                </li>
                    <li class="">
                        <!--cuslike用于前端判断是否显示个性赞,1:显示 -->
                        <a href="javascript:void(0);" class="S_txt2" action-type="fl_like" action-data="version=mini&amp;qid=heart&amp;mid=4280506537884612&amp;like_src=1&amp;cuslike=1" title="赞"><span class="pos"><span class="line S_line1">
                                                                                                                                                                                            <span node-type="like_status" class=""><em class="W_ficon ficon_praised S_txt2">ñ</em><em>15</em></span>                    </span></span></a>
                    <span class="arrow" node-type="cmtarrow"><span class="W_arrow_bor W_arrow_bor_t"><i class="S_line1"></i><em class="S_bg1_br"></em></span></span>
                    </li>
                </ul>
            </div>
        </div>
        <div node-type="feed_list_repeat" class="WB_feed_repeat S_bg1" style="display:none;"></div>
            </div>

</div>

cannot sanitize the content, otherwise it will just break

Oh certainly you can. Everything can be sanitized. I once did it on entire websites. It involved an entire HTML parser and an about 300 line JSON object whitelisting tags and attributes. But it made sure the code couldn’t contain any but the (many) whitelisted tags and attributes.

Anyway. from your code it is pretty obvious that the stuff you are inserting is really meant as HTML markup. I’d additionally check the Content-Type of the response to be text/html (or whatever exactly they serve).

If their own markup contains javascript: URLs, they really can’t complain about XSS -.-

It not depend on how many lines of sanitized I should use. There isn’t a list of what may be there and what may not. There are many special types of feeds form post with picture, post with video, post with article, post with whatever they “invented”. And the codes for feed list update very frequently. I don’t think trying to sanitize could / should be a possible idea. That will be more complex than pack whole ViolentMonkey in my extension. (Or may i chose this solution?)

Btw, there did be a XSS attract some years ago for this site.

But what I mainly argued is that: There is no benefit to doing so (sanitize). There isn’t any security issues here at all. Am i wrong here?

Btw, there did be a XSS attract some years ago for this site.

Well, apparently that did not worry them enough to build a site that can have a CSP, which is one of the best defenses against XSS.

[Sanitizing] will be more complex than pack whole ViolentMonkey in my extension.

Possibly, yes (but I am always very careful with saying that things are “impossible”).

But what I mainly argued is that: There is no benefit to doing so (sanitize). There isn’t any security issues here at all.

I agree in so far as that there are no additional security issues introduced by what you do.
You are basically just moving content (which may include code) from on visitable page to another.
If the site was using CSPs that might make a difference if the pages in question use different rules, but I don’t think they do (see above).
But as I said I’d check the Content-Type, to make sure what you fetched is actually meant to be HTML (otherwise its a XSS risk).

1 Like