DeepQuery Secure Client (sandbox)

提供沙箱内的 DeepQuery API,自动签名并与页面核心通信。可 @require 于任意调用脚本顶部。

Versión del día 4/9/2025. Echa un vistazo a la versión más reciente.

Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @require https://update.greasyfork.org/scripts/548365/1654735/DeepQuery%20Secure%20Client%20%28sandbox%29.js

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         DeepQuery Secure Client (sandbox)
// @namespace    dq.secure.v2.client
// @version      2.0.0
// @description  提供沙箱内的 DeepQuery API,自动签名并与页面核心通信。可 @require 于任意调用脚本顶部。
// @author       you
// @match        http://*/*
// @match        https://*/*
// @include      about:blank
// @run-at       document-start
// @grant        none
// ==/UserScript==
(function () {
  'use strict';

  /******** 配置:必须与 Core 一致 ********/
  const CHANNEL = '__DQ_SECURE_V2__';

  // ★★★ 重要:把下面的 KEY_PARTS 替换成你自己的一组(与 Core 完全一致) ★★★
  const KEY_PARTS = [
    'dW5hcmFuZG9tLWtleS1zZWVkLQ',
    'tZXBsZWFzZS1yZXBsYWNlLW1l',
    'LXdpdGgtYS1wcm9wZXItb25l'
  ];

  // 是否在本沙箱里提供一个 top.DeepQuery 代理(零改动用)
  const EXPOSE_TOP_PROXY = false;

  const te = new TextEncoder();
  function b64ToU8(b64) {
    const pad = b64.length % 4 ? (4 - b64.length % 4) : 0;
    const s = b64 + '='.repeat(pad);
    const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/'));
    const u8 = new Uint8Array(bin.length);
    for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
    return u8;
  }
  const KEY_U8 = b64ToU8(KEY_PARTS.join(''));

  async function sha256U8(u8) {
    const buf = await crypto.subtle.digest('SHA-256', u8);
    return new Uint8Array(buf);
  }
  async function hmacSignRaw(key, u8) {
    const k = await crypto.subtle.importKey(
      'raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
    );
    const sig = await crypto.subtle.sign('HMAC', k, u8);
    return new Uint8Array(sig);
  }

  const pending = new Map();
  function rid() {
    return (Date.now().toString(36) + Math.random().toString(36).slice(2, 10)).toUpperCase();
  }
  function send(payload) {
    window.top.postMessage({ [CHANNEL]: payload }, '*');
  }
  window.addEventListener('message', (e) => {
    const msg = e.data && e.data[CHANNEL];
    if (!msg || msg.cmd !== 'RESP' || !msg.id) return;
    const hit = pending.get(msg.id);
    if (!hit) return;
    pending.delete(msg.id);
    hit.resolve(msg.res);
  }, false);

  async function request(spec) {
    const id = rid();
    const ts = Date.now();
    const nonce = rid() + Math.random().toString(36).slice(2);
    const payload = te.encode(id + '\n' + ts + '\n' + nonce + '\n');
    const bodyHash = await sha256U8(te.encode(JSON.stringify(spec || {})));
    const toSign = new Uint8Array(payload.length + bodyHash.length);
    toSign.set(payload, 0); toSign.set(bodyHash, payload.length);
    const sigU8 = await hmacSignRaw(KEY_U8, toSign);
    const sigB64 = btoa(String.fromCharCode(...sigU8));

    return new Promise((resolve) => {
      pending.set(id, { resolve });
      send({ cmd: 'REQ', id, ts, nonce, sigB64, spec });
      // 可选超时(以 spec.timeout 为基准)
      const timeout = typeof spec.timeout === 'number' ? Math.max(200, spec.timeout + 500) : 6000;
      setTimeout(() => {
        if (pending.has(id)) {
          pending.delete(id);
          resolve({ ok: false, error: 'TIMEOUT' });
        }
      }, timeout);
    });
  }

  // 暴露与旧版一致的方法
  const DeepQuery = {
    async get(spec = {}) { return request(spec); },
    async attr({ framePath, chain, name, timeout }) { return request({ framePath, chain, timeout, pick: { attr: name } }); },
    async prop({ framePath, chain, name, timeout }) { return request({ framePath, chain, timeout, pick: { prop: name } }); },
    async text({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { text: true } }); },
    async html({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { html: true } }); },
    async rect({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { rect: true } }); },
    version: '2.0.0-client'
  };

  // 方式 A:推荐 —— 在沙箱里直接提供全局 DeepQuery
  try { window.DeepQuery = DeepQuery; } catch {}

  // 方式 B:零改动(仅本沙箱可见的 top.DeepQuery 代理)
  if (EXPOSE_TOP_PROXY) {
    try {
      // 注意:这个属性只存在于当前 userscript 的沙箱 proxy 上,页面/其它沙箱看不到
      Object.defineProperty(window.top, 'DeepQuery', {
        configurable: true,
        get() { return DeepQuery; }
      });
    } catch {}
  }

  try { console.debug('[DeepQuery Secure Client] ready'); } catch {}
})();