CVE-2021-21278 (published 2021-01-26T21:15Z)

RSSHub is an open source, easy to use, and extensible RSS feed generator. In RSSHub before version 7f1c430 (non-semantic versioning) there is a risk of code injection. Some routes use `eval` or `Function constructor`, which may be injected by the target site with unsafe code, causing server-side security issues The fix in version 7f1c430 is to temporarily remove the problematic route and added a `no-new-func` rule to eslint.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-74
cwe_name: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
description: The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
extended_description: Software or other automated logic has certain assumptions about what constitutes data and control respectively. It is the lack of verification of these assumptions for user-controlled input that leads to injection problems. Injection problems encompass a wide variety of issues -- all mitigated in very different ways and usually attempted in order to alter the control flow of the process. For this reason, the most effective way to discuss these weaknesses is to note the distinct features that classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed.
         

Fix information:
repo_url: https://github.com/DIYgod/RSSHub
commit_details: repo_url: https://github.com/DIYgod/RSSHub
author: DIYgod
author_date: 2021-01-25 18:43:47+08:00
msg: feat: remove some routes for security reasons, !!! no-eval no-new-func !!!
repository: repo_url: https://github.com/DIYgod/RSSHub
repo_name: DIYgod/RSSHub
description: 🧡 Everything is RSSible
repo_language: TypeScript
owner: DIYgod
file_changes: filename: .eslintrc
change_type: ModificationType.MODIFY
diff: @@ -53,6 +53,7 @@
         "prettier/prettier": 0,
         "no-await-in-loop": 2,
         "require-atomic-updates": 0,
-        "no-prototype-builtins": 0
+        "no-prototype-builtins": 0,
+        "no-new-func": 2
     }
 }

programming_language: Jupyter Notebook
filename: game.md
change_type: ModificationType.MODIFY
diff: @@ -432,22 +432,6 @@ Example: `https://store.steampowered.com/search/?specials=1&term=atelier` 中的
 
 <Route author="KotoriK" example="/pcr/news-cn" path="/pcr/news-cn"/>
 
-## 篝火营地
-
-### 游戏资讯
-
-<Route author="sintak" example="/gouhuo/news/switch" path="/gouhuo/news/:category" :paramsDesc="['资讯类型']">
-
-| 精选       | 海外     | 原创    | PS4 | Xboxone | PC | Switch | 掌机     | 手游       | 新闻 | 评测   | 文化    | 视频  | 音频  | 折扣     |
-| ---------- | -------- | ------- | --- | ------- | -- | ------ | -------- | ---------- | ---- | ------ | ------- | ----- | ----- | -------- |
-| choiceness | overseas | orignal | ps4 | xboxone | pc | switch | handheld | mobilegame | news | review | culture | video | audio | discount |
-
-</Route>
-
-### 游戏攻略
-
-<Route author="sintak" example="/gouhuo/strategy" path="/gouhuo/strategy"/>
-
 ## 怪物猎人世界
 
 ### 更新情报

programming_language: Markdown
filename: live.md
change_type: ModificationType.MODIFY
diff: @@ -38,12 +38,6 @@ pageClass: routes
 
 <Route author="DIYgod" example="/douyu/room/24422" path="/douyu/room/:id" :paramsDesc="['直播间 id, 可在主播直播间页 URL 中找到']"/>
 
-## 黑白直播
-
-### 直播间开播
-
-<Route author="laampui" example="/heibaizhibo/room/195976" path="/heibaizhibo/room/:id" :paramsDesc="['直播间 id, 可在主播直播间页 URL 中找到']"/>
-
 ## 虎牙直播
 
 ### 直播间开播

programming_language: Markdown
filename: new-media.md
change_type: ModificationType.MODIFY
diff: @@ -1936,10 +1936,6 @@ column 为 third 时可选的 category:
 
 </Route>
 
-### 数读
-
-<Route author="laampui" example="/netease/news/data" path="/netease/news/data"/>
-
 ## 维基百科
 
 ### 中国大陆新闻动态

programming_language: Markdown
filename: program-update.md
change_type: ModificationType.MODIFY
diff: @@ -418,12 +418,6 @@ pageClass: routes
 
 <Route author="mrbruce516" example="/tesla" path="/tesla/update"/>
 
-## 腾讯柠檬 Lab
-
-### 柠檬精选 Mac Apps
-
-<Route author="HenryQW" example="/tencent/lemon" path="/tencent/lemon"/>
-
 ## 腾讯云移动直播 SDK
 
 ### 更新日志

programming_language: Markdown
filename: programming.md
change_type: ModificationType.MODIFY
diff: @@ -678,11 +678,6 @@ GitHub 官方也提供了一些 RSS:
 <Route author="hellodword" example="/galaxylab" path="/galaxylab">
 </Route>
 
-## 前端技术文章
-
-<Route author="laampui" example="/front-end-rss" path="/front-end-rss" :paramsDesc="['分类']">
-</Route>
-
 ## 前端艺术家 && 飞冰早报
 
 ### 列表

programming_language: Markdown
filename: router.js
change_type: ModificationType.MODIFY
diff: @@ -2072,10 +2072,6 @@ router.get('/pingwest/user/:uid/:type?', require('./routes/pingwest/user'));
 // Hanime
 router.get('/hanime/video', require('./routes/hanime/video'));
 
-// 篝火营地
-router.get('/gouhuo/news/:category', require('./routes/gouhuo'));
-router.get('/gouhuo/strategy', require('./routes/gouhuo/strategy'));
-
 // Soul
 router.get('/soul/:id', require('./routes/soul'));
 router.get('/soul/posts/hot', require('./routes/soul/hot'));
@@ -2141,9 +2137,6 @@ router.get('/szse/rule', require('./routes/szse/rule'));
 // 前端艺术家每日整理&&飞冰早报
 router.get('/jskou/:type?', require('./routes/jskou/index'));
 
-// 前端
-router.get('/front-end-rss', require('./routes/frontend/index'));
-
 // 国家应急广播
 router.get('/cneb/yjxx', require('./routes/cneb/yjxx'));
 router.get('/cneb/guoneinews', require('./routes/cneb/guoneinews'));
@@ -2178,9 +2171,6 @@ router.get('/sohu/mp/:id', require('./routes/sohu/mp'));
 // 腾讯企鹅号
 router.get('/tencent/news/author/:mid', require('./routes/tencent/news/author'));
 
-// 腾讯柠檬精选
-router.get('/tencent/lemon', require('./routes/tencent/lemon/index'));
-
 // 奈菲影视
 router.get('/nfmovies/:id?', require('./routes/nfmovies/index'));
 
@@ -3056,7 +3046,6 @@ router.get('/jike/user/:id', require('./routes/jike/user'));
 // 网易新闻
 router.get('/netease/news/rank/:category?/:type?/:time?', require('./routes/netease/news/rank'));
 router.get('/netease/news/special/:type?', require('./routes/netease/news/special'));
-router.get('/netease/news/data', require('./routes/netease/news/data'));
 
 // 网易 - 网易号
 router.get('/netease/dy/:id', require('./routes/netease/dy'));
@@ -3169,9 +3158,6 @@ router.get('/touhougarakuta/:language/:type', require('./routes/touhougarakuta')
 // 猎趣TV
 router.get('/liequtv/room/:id', require('./routes/liequtv/room'));
 
-// 黑白直播
-router.get('/heibaizhibo/room/:id', require('./routes/heibaizhibo/room'));
-
 // Behance
 router.get('/behance/:user/:type?', require('./routes/behance/index'));
 

programming_language: JavaScript
filename: index.js
change_type: ModificationType.DELETE
diff: @@ -1,27 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-
-module.exports = async (ctx) => {
-    const response = await got.get(`https://front-end-rss.now.sh/`);
-    const $ = cheerio.load(response.data);
-    const fn = Function($($('script')[1]).html() + 'return Array.isArray(LINKS_DATA) ? LINKS_DATA : []');
-    const items = fn().reduce(
-        (acc, category) => [
-            ...acc,
-            ...category.items.map((item) => ({
-                link: item.link,
-                pubDate: item.date,
-                title: item.title,
-                author: category.title,
-                description: category.title,
-            })),
-        ],
-        []
-    );
-
-    ctx.state.data = {
-        title: '前端技术文章',
-        link: 'https://front-end-rss.now.sh/',
-        item: items,
-    };
-};

programming_language: unknown
filename: cache.js
change_type: ModificationType.DELETE
diff: @@ -1,44 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-
-const self = {
-    _getProfile: async (ctx) => {
-        const key = 'gouhuo-profile';
-        let profile = await ctx.cache.get(key);
-        if (!profile) {
-            const response = await got({
-                method: 'get',
-                url: `https://gouhuo.qq.com/`,
-                headers: {
-                    Referer: `https://gouhuo.qq.com/`,
-                },
-            });
-            const body = response.data;
-            const $ = cheerio.load(body);
-
-            // eslint-disable-next-line no-eval
-            const obj = eval(body.match(/(?<=<script>\s*window\.__NUXT__=)[\s\S]*?(?=<\/script>)/g)[0]);
-            const tabNameMapping = {};
-            for (const item of obj.data[0].tabList) {
-                tabNameMapping[item.tab_id] = item.tab_name;
-            }
-            const title = $('title').text().split('-')[0].trim();
-            const description = $('title').text().split('-')[1].trim();
-
-            profile = JSON.stringify({
-                title: title,
-                description: description,
-                tabNameMapping: tabNameMapping,
-            });
-
-            ctx.cache.set(key, profile);
-        }
-        return JSON.parse(profile);
-    },
-    getStaffs: async (ctx, tabId) => {
-        const profile = await self._getProfile(ctx);
-        return [profile.title, profile.description, profile.tabNameMapping[tabId]];
-    },
-};
-
-module.exports = self;

programming_language: unknown
filename: index.js
change_type: ModificationType.DELETE
diff: @@ -1,62 +0,0 @@
-const got = require('@/utils/got');
-const cache = require('./cache');
-
-module.exports = async (ctx) => {
-    const category = ctx.params.category;
-
-    const tabIdMapping = {
-        choiceness: '1_110',
-        overseas: '1_108',
-        orignal: '1_109',
-        ps4: '2_5',
-        xboxone: '2_13',
-        pc: '2_1',
-        switch: '2_30',
-        handheld: '3_1',
-        mobilegame: '3_2',
-        news: '1_101',
-        review: '1_104',
-        culture: '1_103',
-        video: '1_102',
-        audio: '1_107',
-        discount: '1_120',
-    };
-    const tabId = (() => {
-        if (tabIdMapping[category]) {
-            return tabIdMapping[category];
-        } else {
-            throw Error('Unknow route.');
-        }
-    })();
-
-    const response = await got({
-        method: 'get',
-        url: `https://gouhuo.qq.com/content/getContentByTab?tabId=${tabId}`,
-        headers: {
-            Referer: `https://gouhuo.qq.com/`,
-        },
-    });
-    const list = response.data.data.list;
-
-    const staffs = await cache.getStaffs(ctx, tabId);
-    const title = staffs[0];
-    const description = staffs[1];
-    const tabName = staffs[2];
-
-    ctx.state.data = {
-        title: `${title} ${tabName}`,
-        link: `https://gouhuo.qq.com/`,
-        description: description,
-        item:
-            list &&
-            list.map((item) => {
-                const single = {
-                    title: item.title,
-                    description: `${item.title}<br><img src="${item.img_url}">`, // Get fulltext manually if needs be. Snippet like: $('.widget-article').first().html();
-                    pubDate: new Date(item.publish_ts * 1000).toUTCString(),
-                    link: item.link,
-                };
-                return single;
-            }),
-    };
-};

programming_language: unknown
filename: strategy.js
change_type: ModificationType.DELETE
diff: @@ -1,35 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-
-module.exports = async (ctx) => {
-    const baseUrl = 'https://gouhuo.qq.com';
-    const url = `${baseUrl}/guide`;
-    const response = await got({
-        method: 'get',
-        url: url,
-        headers: {
-            Referer: `https://gouhuo.qq.com/`,
-        },
-    });
-    const body = response.data;
-    const $ = cheerio.load(body);
-
-    // eslint-disable-next-line no-eval
-    const obj = eval(body.match(/(?<=<script>\s*window\.__NUXT__=)[\s\S]*?(?=<\/script>)/g)[0]);
-    const items = obj.data[0].newGuides.map((item) => {
-        const img_url = item.img_url;
-        const single = {
-            title: item.title,
-            description: `${item.title}<br><img src="${img_url}">`,
-            pubDate: new Date(item.publish_ts * 1000).toUTCString(),
-            link: `${baseUrl}${item.link}`,
-        };
-        return single;
-    });
-
-    ctx.state.data = {
-        title: $('title').text().split('_')[0],
-        link: url,
-        item: items,
-    };
-};

programming_language: unknown
filename: room.js
change_type: ModificationType.DELETE
diff: @@ -1,31 +0,0 @@
-const got = require('@/utils/got');
-
-module.exports = async (ctx) => {
-    const id = ctx.params.id;
-    const response = await got.get(`https://www.heibaizhibo.com/anchorLive/${id}`);
-
-    const liveInfo = Function('const window={};' + response.data.match(/window\.__NUXT__=.*;/gm)[0] + 'return window.__NUXT__')();
-    const gtvId = liveInfo.data[0].gtvId;
-    const res = await got.get(`https://sig.heibaizhibo.com/signal-front/live/matchLiveInfo?gtvId=${gtvId}&source=2&liveType=3&defi=hd`);
-    const offline = res.data.data[0].score === -1;
-
-    const item = offline
-        ? []
-        : [
-              {
-                  title: liveInfo.data[0].text,
-                  author: liveInfo.data[0].anchorInfo.nickname,
-                  link: response.url,
-                  guid: response.url + liveInfo.data[0].live_info.startDate,
-              },
-          ];
-
-    ctx.state.data = {
-        title: `${liveInfo.data[0].anchorInfo.nickname} - 黑白直播`,
-        image: liveInfo.data[0].anchorInfo.portrait,
-        description: liveInfo.data[0].anchorInfo.notice,
-        link: response.url,
-        item,
-        allowEmpty: true,
-    };
-};

programming_language: unknown
filename: utils.js
change_type: ModificationType.MODIFY
diff: @@ -15,13 +15,13 @@ const ProcessFeed = async (list, cache) => {
 
                 const $ = cheerio.load(response.data);
 
-                const __INITIAL_STATE__ = Function(
-                    'let window={};' +
-                        $('*')
-                            .html()
-                            .match(/window\.__INITIAL_STATE__.*};/gm)[0] +
-                        'return window.__INITIAL_STATE__'
-                )();
+                // const __INITIAL_STATE__ = Function(
+                //     'let window={};' +
+                //         $('*')
+                //             .html()
+                //             .match(/window\.__INITIAL_STATE__.*};/gm)[0] +
+                //         'return window.__INITIAL_STATE__'
+                // )();
 
                 $('.article__content')
                     .find('.lazyImg')
@@ -42,9 +42,9 @@ const ProcessFeed = async (list, cache) => {
 
                 let description = '';
 
-                if ('video_info' in __INITIAL_STATE__.articleDetail.articleDetail) {
-                    description += `<video height="100%" poster="${__INITIAL_STATE__.articleDetail.articleDetail.video_info.cover}" controls src="${__INITIAL_STATE__.articleDetail.articleDetail.video_info.hd_link}"></video>`;
-                }
+                // if ('video_info' in __INITIAL_STATE__.articleDetail.articleDetail) {
+                //     description += `<video height="100%" poster="${__INITIAL_STATE__.articleDetail.articleDetail.video_info.cover}" controls src="${__INITIAL_STATE__.articleDetail.articleDetail.video_info.hd_link}"></video>`;
+                // }
                 if ($('.top-img').html() !== null) {
                     description += $('.top-img').html();
                 }

programming_language: JavaScript
filename: data.js
change_type: ModificationType.DELETE
diff: @@ -1,39 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const iconv = require('iconv-lite');
-
-module.exports = async (ctx) => {
-    const response = await got({
-        url: 'https://data.163.com',
-        method: 'get',
-        responseType: 'buffer',
-    });
-    const $ = cheerio.load(iconv.decode(response.data, 'gbk'));
-    const dataStr = $('*')
-        .html()
-        .match(/var\sohnofuchlist=\[(.|\n)*"a"\];$/gm)[0];
-    const arr = new Function(dataStr + 'ohnofuchlist.pop();return ohnofuchlist;')();
-
-    const item = await Promise.all(
-        arr.slice(0, 20).map(async (item) => {
-            const single = await ctx.cache.tryGet(item.url, async () => {
-                const res = await got.get(item.url);
-                const doc = cheerio.load(res.data);
-                const description = doc('#main-content').html() || doc('.post_body').html();
-                return {
-                    title: item.title,
-                    link: item.url,
-                    pubDate: new Date(item.time),
-                    description,
-                };
-            });
-            return Promise.resolve(single);
-        })
-    );
-
-    ctx.state.data = {
-        title: '数读 - 网易新闻',
-        link: 'https://data.163.com/',
-        item,
-    };
-};

programming_language: unknown
filename: index.js
change_type: ModificationType.DELETE
diff: @@ -1,29 +0,0 @@
-const got = require('@/utils/got');
-const date = require('@/utils/date');
-
-module.exports = async (ctx) => {
-    const url = 'https://lemon.qq.com/lab/js/source.js';
-
-    let data = (await got.get(url)).data.match(/(?<=(const list = ))(.*)(\])/gs)[0];
-
-    // eslint-disable-next-line no-eval
-    data = eval('(' + data + ')').slice(0, 10);
-
-    const items = data.map((i) => ({
-        title: i.name,
-        description: `
-        ${i.comment}</br>
-        <img src="${i.logo}"></br>
-        <a href="${i.downloadlink}">下载链接</a>
-        `,
-        link: `https://lemon.qq.com/lab/app/${i.shortname}.html`,
-        author: i.referrer,
-        pubDate: date(i.date),
-    }));
-
-    ctx.state.data = {
-        title: '腾讯柠檬精选',
-        link: 'https://lemon.qq.com/lab/',
-        item: items,
-    };
-};

programming_language: unknown