Greasy Fork镜像 还支持 简体中文。

DeepQuery Secure Client (GF-safe, no hardcoded key)

沙箱内 DeepQuery API;密钥仅存本机 GM 存储,公开代码不含密钥;与 Secure Core 协作完成签名校验

目前為 2025-09-05 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/548365/1654982/DeepQuery%20Secure%20Client%20%28GF-safe%2C%20no%20hardcoded%20key%29.js

// ==UserScript==
// @name         DeepQuery Secure Client (GF-safe, no hardcoded key)
// @namespace    dq.secure.v2.client
// @version      3.0.0
// @description  沙箱内 DeepQuery API;密钥仅存本机 GM 存储,公开代码不含密钥;与 Secure Core 协作完成签名校验
// @author       you
// @match        http://*/*
// @match        https://*/*
// @include      about:blank
// @run-at       document-start
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// ==/UserScript==
(function () {
  'use strict';

  const CHANNEL = '__DQ_SECURE_V2__';
  const KEY_NAME = 'dq_key_b64';       // 本机密钥保存的键名
  const EXPOSE_TOP_PROXY = false;      // 设为 true 可零改动继续用 top.DeepQuery(仅对本沙箱可见)

  // ==== 基础工具 ====
  const te = new TextEncoder();

  function b64ToU8(b64) {
    if (!b64 || typeof b64 !== 'string') return new Uint8Array();
    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;
  }
  function u8ToB64(u8) {
    let s = '';
    for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]);
    return btoa(s).replace(/\=+$/,'');
  }
  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);
  }

  // ==== 密钥管理(仅本机) ====
  async function ensureKeyInteractive() {
    // 如果没有密钥:询问用户是“自动生成”还是“粘贴已有(与 Core 一致)”
    const has = await GM_getValue(KEY_NAME, '');
    if (has) return has;

    const gen = confirm('[DeepQuery] 未检测到本机密钥。\n确定后将自动生成一个强随机密钥(推荐)。\n取消则提示你手动粘贴已有密钥。');
    let b64;
    if (gen) {
      const u8 = crypto.getRandomValues(new Uint8Array(32));
      b64 = u8ToB64(u8);
      alert('[DeepQuery] 已生成新密钥(base64)。\n请在“Core”脚本里配置相同密钥后使用。\n接下来会提供菜单方便你复制。');
    } else {
      b64 = prompt('[DeepQuery] 请输入与你的 Core 相同的密钥(base64,建议≥32字节):', '');
      if (!b64) throw new Error('NO_KEY_PROVIDED');
    }
    await GM_setValue(KEY_NAME, b64);
    return b64;
  }

  async function getKeyU8() {
    let b64 = await GM_getValue(KEY_NAME, '');
    if (!b64) b64 = await ensureKeyInteractive();
    return b64ToU8(b64);
  }

  // 提供菜单:设置/重置/复制密钥
  try {
    GM_registerMenuCommand('DeepQuery:设置/重置密钥', async () => {
      const b64 = prompt('[DeepQuery] 请输入新密钥(base64,留空自动生成):', '');
      let val = b64;
      if (!b64) {
        const u8 = crypto.getRandomValues(new Uint8Array(32));
        val = u8ToB64(u8);
        alert('[DeepQuery] 已自动生成并保存新密钥。');
      }
      await GM_setValue(KEY_NAME, val);
      alert('[DeepQuery] 密钥已更新。请保证 Core 使用相同密钥!');
    });
    GM_registerMenuCommand('DeepQuery:复制当前密钥', async () => {
      const b64 = await GM_getValue(KEY_NAME, '');
      if (!b64) { alert('当前未设置密钥'); return; }
      try {
        await navigator.clipboard.writeText(b64);
        alert('已复制到剪贴板。');
      } catch {
        prompt('复制失败,请手动复制:', b64);
      }
    });
  } catch {}

  // ==== 与 Core 的签名通信 ====
  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 KEY_U8 = await getKeyU8();
    const sigU8 = await hmacSignRaw(KEY_U8, toSign);
    const sigB64 = u8ToB64(sigU8);

    return new Promise((resolve) => {
      pending.set(id, { resolve });
      send({ cmd: 'REQ', id, ts, nonce, sigB64, spec });
      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.1.0-client'
  };

  // 推荐:在本沙箱全局暴露 DeepQuery
  try { window.DeepQuery = DeepQuery; } catch {}

  // 可选:零改动代理(仅当前沙箱可见,不暴露给页面)
  if (EXPOSE_TOP_PROXY) {
    try {
      Object.defineProperty(window.top, 'DeepQuery', {
        configurable: true,
        get() { return DeepQuery; }
      });
    } catch {}
  }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址