Как я могу получить доступ к встроенным методам Объекта, который переопределен? - PullRequest
0 голосов
/ 30 декабря 2018

Веб-страница устанавливает для встроенного метода javascript значение null, и я пытаюсь найти способ вызова переопределенных методов в сценарии пользователя.

Рассмотрим следующий код:

// Overriding the native method to something else
document.querySelectorAll = null;

Теперь, если я попытаюсь выполнить document.querySelectorAll('#an-example'), я получу исключение Uncaught TypeError: null is not a function.Причина в том, что метод был изменен на null и больше не доступен.

Я ищу способ каким-то образом восстановить ссылку на метод в моем скрипте пользователя.Проблема в том, что веб-сайт может переопределить ссылку на что угодно (даже включая конструкторы Document, Element и Object).

Поскольку веб-сайт также может легко установить ссылку на null,Мне нужен способ найти способ доступа к querySelectorAll методу, который веб-сайт не сможет переопределить .

Проблема в том, что любой метод такие как createElement и getElementsByTagName (в дополнение к их prototype s) могут быть переопределены на null в момент, когда мой пользовательский скрипт выполняется на странице.

Мой вопрос, как мнеполучить доступ к методам конструктора Document или HTMLDocument, если они также были переопределены?

Примечание:

Поскольку Tampermonkey из-за ограничений браузера не может запустить мой скрипт в начале документа, яЯ не могу сохранить ссылку на метод, который я хотел бы использовать, с чем-то вроде этого:

// the following code cannot be run at the beginning of the document
var _originalQuerySelectorAll = document.querySelectorAll;

Ответы [ 2 ]

0 голосов
/ 02 января 2019

Дополнительным решением в Tampermonkey является восстановление оригинала с помощью iframe - при условии, что CSP сайта разрешает это, как обычно, AFAIK.

function restoreOriginal(name) {
  let f = restoreOriginal.iframe;
  if (f) {
    let o = f.contentWindow;
    for (const prop of name.split('.'))
      o = o[prop];
    return Promise.resolve(o);
  } else {
    return new Promise((resolve, reject) => {
      f = restoreOriginal.iframe = document.createElement('iframe');
      f.style.cssText = 'display:none !important';
      f.onload = () => {
        f.onload = null;
        resolve(restoreOriginal(name));
      };
      f.onerror = reject;
      document.documentElement.appendChild(f);
    });
  }
}

Использование:

(async () => {
  const dqsa = await restoreOriginal('document.querySelectorAll');
  // restore
  document.querySelectorAll = dqsa;
  // or just call it directly
  console.log(dqsa.call(document, '*'));
})();

PS Если страница не полная, вы можете получить доступ к оригиналу через прототип:

Document.prototype.querySelectorAll.call(document, '*')
Element.prototype.querySelectorAll.call(normalElements, '*')
0 голосов
/ 31 декабря 2018

Существует как минимум 3 подхода:

  1. Используйте пользовательскую песочницу .Увы, в настоящее время это работает только на Greasemonkey (включая версию 4+) из-за недостатков / ошибок в дизайне Tampermonkey и Violentmonkey.Подробнее ниже.
  2. Использование @run-at document-start.За исключением того, что это также не будет работать на быстрых страницах.
  3. Удалить переопределение функции .Это обычно работает, но подвержено большему вмешательству с / с целевой страницы.и может быть заблокировано, если страница изменяет prototype функции.


См. также Остановите выполнение функции Javascript (на стороне клиента) или настройте ее


Обратите внимание, что все приведенные ниже примеры сценариев и расширений представляют собой полный рабочий код .
И вы можете проверить их по эту страницу корзины JS , изменив:
*://YOUR_SERVER.COM/YOUR_PATH/*
на:
https://output.jsbin.com/kobegen*



Пользовательская песочница:

Это предпочтительный метод, который работает в Firefox + Greasemonkey (включая Greasemonkey 4).

Если для @grant установлено значение, отличное от none, обработчик сценариев должен запускатьсяскрипт в песочнице, который браузеры специально предоставляют для этой цели.

В нужной песочнице целевая страница может переопределять document.querySelectorAll или другие собственные функции, какие только пожелает, и пользовательский скрипт будет видеть свои собственные, полностью нетронутые экземпляры , независимо от того.

Это должно всегда работать:

// ==UserScript==
// @name     _Unoverride built in functions
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant    GM_addStyle
// @grant    GM.getValue
// ==/UserScript==
//- The @grant directives are needed to restore the proper sandbox.

console.log ("document.querySelectorAll: ", document.querySelectorAll);

и давать:

document.querySelectorAll: function querySelectorAll () {[собственный код]}

Тем не менее, и Tampermonkey, и Violentmonkey не работают в «песочнице» должным образом , ни в Chrome, ни в Firefox.
Целевая страница может вмешиваться в собственные функции, которые видит скрипт Tampermonkey, даже еслиВерсия песочницы Tampermonkey или Violentmonkey.
Это не просто недостаток дизайна, это недостаток безопасности и вектор для потенциальных эксплойтов.

Мы знаем, что Firefox и Chromeне являются виновниками, поскольку (1) Greasemonkey-4 правильно устанавливает песочницу и (2) расширение Chrome правильно устанавливает «Изолированный мир».То есть это расширение:

manifest.json:

{
    "manifest_version": 2,
    "content_scripts": [ {
        "js":               [ "Unoverride.js" ],
        "matches":          [ "*://YOUR_SERVER.COM/YOUR_PATH/*" ]
    } ],
    "description":  "Unbuggers native function",
    "name":         "Native function restore slash use",
    "version":      "1"
}

Unoverride.js:

console.log ("document.querySelectorAll: ", document.querySelectorAll);

Выходные данные:

document.querySelectorAll: функция querySelectorAll () {[собственный код]}

в порядке.



Использование @run-at document-start:

Теоретически, запуск сценария на document-start должен позволить сценарию перехватить нативную функцию до ее изменения.
EG:

// ==UserScript==
// @name     _Unoverride built in functions
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant    none
// @run-at   document-start
// ==/UserScript==

console.log ("document.querySelectorAll: ", document.querySelectorAll);

И это иногда работает на достаточно медленных страницах и / или сетях.

Но, как уже отмечалось в OP, ни Tampermonkey, ни Violentmonkey фактически не вводят и не запускают перед любым другим кодом страницы , поэтому этот метод не работает на быстрых страницах.скрипт содержимого с расширением Chrome, установленный в манифесте с "run_at": "document_start", работает в правильное время и / или достаточно быстро.



Удалите переопределение функции:

Если страница (мягко) переопределяет такую ​​функцию, как document.querySelectorAll, вы можете отменить переопределение, используя delete, например:

// ==UserScript==
// @name     _Unoverride built in functions
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant    none
// ==/UserScript==

delete document.querySelectorAll;

console.log ("document.querySelectorAll: ", document.querySelectorAll);

, что приводит к:

document.querySelectorAll: функция querySelectorAll () {[собственный код]}

Недостатки:

  1. Не будет работатьесли страница изменяет прототип.Например:
    Document.prototype.querySelectorAll = null;
  2. На странице могут отображаться или переделываться такие изменения, особенно если ваш сценарий тоже запускается скоро .

Смягчить элемент 2 с помощьюсоздание личной копии:

// ==UserScript==
// @name     _Unoverride built in functions
// @match    *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant    none
// ==/UserScript==

var foobarFunc = document.querySelectorAll;

delete document.querySelectorAll;

var _goodfunc = document.querySelectorAll;
var goodfunc  = function (params) {return _goodfunc.call (document, params); };

console.log (`goodfunc ("body"): `, goodfunc("body") );

, что приводит к:

goodfunc ("body"): NodeList 1 0: тело, длина: 1 ,...

И goodfunc() продолжит работать (для вашего скрипта), даже если страница будет удалена document.querySelectorAll.

...