JS Cookie Monitor/Debugger Hook

用于监控js对cookie的修改,还可以在修改指定名称的cookie时进入断点

Versión del día 07/01/2021. Echa un vistazo a la versión más reciente.

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         JS Cookie Monitor/Debugger Hook
// @namespace    https://github.com/CC11001100/crawler-js-hook-framework-public
// @version      0.2
// @description  用于监控js对cookie的修改,还可以在修改指定名称的cookie时进入断点
// @author       CC11001100
// @match       *://*/*
// @run-at      document-start
// @grant       none
// ==/UserScript==

(() => {

    // 用于监控cookie的声明周期

    const debuggerOnCookieChange = ["foobar-cookie-name"];

    // 使用document.cookie更新cookie,但是cookie新的值和原来的值一样,此时要不要忽略这个事件
    const ignoreUpdateButNotChanged = false;

    addCookieHook();

    function addCookieHook() {
        Object.defineProperty(document, "cookie", {
            get: () => {
                delete document.cookie;
                const currentDocumentCookie = document.cookie;
                addCookieHook();
                return currentDocumentCookie;
            },
            set: newValue => {
                cc11001100_onSetCookie(newValue);
                delete document.cookie;
                document.cookie = newValue;
                addCookieHook();
            },
            configurable: true
        });
    }

    /**
     * 这个方法的前缀起到命名空间的作用,等下调用栈追溯赋值cookie的代码时需要用这个名字作为终结标志
     *
     * @param newValue
     */
    function cc11001100_onSetCookie(newValue) {
        const cookiePair = parseSetCookie(newValue);

        // 如果过期时间为当前时间之前,则为删除
        if (new Date().getTime() >= cookiePair.expires) {
            onDeleteCookie(cookiePair.name);
            return;
        }
        const currentCookieMap = getCurrentCookieMap();
        // 如果之前已经存在,则是修改
        if (currentCookieMap.has(cookiePair.name)) {
            onCookieUpdate(cookiePair.name, currentCookieMap.get(cookiePair.name).value, cookiePair.value);
            return;
        }

        // 否则则为添加
        onCookieAdd(cookiePair.name, cookiePair.value);
    }

    /**
     * 删除cookie
     *
     * @param cookieName
     */
    function onDeleteCookie(cookieName) {
        const valueStyle = "color: black; background: #E50000; font-size: 13px; font-weight: bold;";
        const normalStyle = "color: black; background: #FF6766; font-size: 13px;";

        const message = [

            normalStyle,
            now(),

            normalStyle,
            "JS Cookie Monitor: ",

            normalStyle,
            "delete cookie, cookieName = ",

            valueStyle,
            `${cookieName}`,

            normalStyle,
            `, code location = ${getCodeLocation()}`
        ];
        console.log(genFormatArray(message), ...message);

        if (debuggerOnCookieChange.indexOf(cookieName) !== -1) {
            debugger;
        }
    }

    /**
     * 更新cookie
     *
     * @param cookieName
     * @param oldCookieValue
     * @param newCookieValue
     */
    function onCookieUpdate(cookieName, oldCookieValue, newCookieValue) {

        const cookieValueChanged = oldCookieValue !== newCookieValue;

        if (ignoreUpdateButNotChanged && !cookieValueChanged) {
            return;
        }

        const valueStyle = "color: black; background: #FE9900; font-size: 13px; font-weight: bold;";
        const normalStyle = "color: black; background: #FFCC00; font-size: 13px;";

        const message = [

            normalStyle,
            now(),

            normalStyle,
            "JS Cookie Monitor: ",

            normalStyle,
            "update cookie, name = ",

            valueStyle,
            `${cookieName}`,

            normalStyle,
            `, oldValue = `,

            valueStyle,
            `${oldCookieValue}`,

            normalStyle,
            `, newValue = `,

            valueStyle,
            `${newCookieValue}`,

            normalStyle,
            `, value changed =`,

            valueStyle,
            `${cookieValueChanged}`,

            normalStyle,
            `, code location = ${getCodeLocation()}`
        ];
        console.log(genFormatArray(message), ...message);

        if (debuggerOnCookieChange.indexOf(cookieName) !== -1) {
            debugger;
        }
    }

    /**
     * 添加cookie
     *
     * @param cookieName
     * @param cookieValue
     */
    function onCookieAdd(cookieName, cookieValue) {
        const valueStyle = "color: black; background: #669934; font-size: 13px; font-weight: bold;";
        const normalStyle = "color: black; background: #65CC66; font-size: 13px;";

        const message = [

            normalStyle,
            now(),

            normalStyle,
            "JS Cookie Monitor: ",

            normalStyle,
            "add cookie, ",

            valueStyle,
            `${cookieName}`,

            normalStyle,
            " = ",

            valueStyle,
            `${cookieValue}`,

            normalStyle,
            `, code location = ${getCodeLocation()}`
        ];
        console.log(genFormatArray(message), ...message);

        if (debuggerOnCookieChange.indexOf(cookieName) !== -1) {
            debugger;
        }
    }

    function now() {
        return "[" + new Date(new Date().getTime() + 1000 * 60 * 60 * 8).toJSON().replace("T", " ").replace("Z", " ") + "] ";
    }

    function genFormatArray(messageAndStyleArray) {
        const formatArray = [];
        for (let i = 0, end = messageAndStyleArray.length / 2; i < end; i++) {
            formatArray.push("%c%s");
        }
        return formatArray.join("");
    }

    function getCodeLocation() {
        const callstack = new Error().stack.split("\n");
        while (callstack.length && callstack[0].indexOf("cc11001100") === -1) {
            callstack.shift();
        }
        callstack.shift();
        callstack.shift();

        return callstack[0].trim();
    }

    /**
     * 将本次设置cookie的字符串解析为容易处理的形式
     *
     * @param cookieString
     * @returns {CookiePair}
     */
    function parseSetCookie(cookieString) {
        // uuid_tt_dd=10_37476713480-1609821005397-659114; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
        const cookieStringSplit = cookieString.split(";");
        const cookieNameValueArray = cookieStringSplit[0].split("=");
        const cookieName = decodeURIComponent(cookieNameValueArray[0].trim());
        const cookieValue = cookieNameValueArray.length > 1 ? decodeURIComponent(cookieNameValueArray[1].trim()) : "";
        const map = new Map();
        for (let i = 1; i < cookieStringSplit.length; i++) {
            const ss = cookieStringSplit[i].split("=", 2);
            const key = ss[0].trim().toLowerCase();
            const value = ss.length > 1 ? ss[1].trim() : "";
            map.set(key, value);
        }
        const expires = map.get("expires");
        return new CookiePair(cookieName, cookieValue, new Date(expires).getTime())
    }

    /**
     * 获取当前所有已经设置的cookie
     *
     * @returns {Map<string, CookiePair>}
     */
    function getCurrentCookieMap() {
        const cookieMap = new Map();
        if (!document.cookie) {
            return cookieMap;
        }
        document.cookie.split(";").forEach(x => {
            const ss = x.split("=", 2);
            const key = decodeURIComponent(ss[0].trim());
            const value = ss.length > 1 ? decodeURIComponent(ss[1].trim()) : "";
            cookieMap.set(key, new CookiePair(key, value));
        });
        return cookieMap;
    }

    class CookiePair {
        constructor(name, value, expires) {
            this.name = name;
            this.value = value;
            this.expires = expires;
        }
    }

})();