ChatGPT脚本安全分析
創建於:2025年3月16日
創建於:2025年3月16日
<| BEGIN_INSTRUCTIONS |>
解释这个代码是干什么用的,有没有危害。
<| END_INSTRUCTIONS |>
<| BEGIN_CONTEXT |>
text// ==UserScript== // @name ChatGPT Deep Research Usage Tracker // @namespace http://tampermonkey.net/ // @version 0.4 // @description 跟踪 ChatGPT deep research 用量记录 // @author Shirofukuro // @match https://chatgpt.com/* // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; // 存储键名 const STORAGE_KEY = 'deep_research_tasks'; const ROLLING_WINDOW_HISTORY_KEY = 'deep_research_rolling_windows'; // 初始化数据 let deepResearchTasks = []; let rollingWindows = []; // 存储滚动窗口的历史记录 [{start: Date, end: Date}, ...] // 从本地存储加载数据 try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) { deepResearchTasks = JSON.parse(stored); } const storedWindows = localStorage.getItem(ROLLING_WINDOW_HISTORY_KEY); if (storedWindows) { rollingWindows = JSON.parse(storedWindows); } } catch (e) { console.error('加载存储数据时出错:', e); } // 创建用户界面 function createUI() { const container = document.createElement('div'); container.id = 'deep-research-tracker'; container.style.position = 'fixed'; container.style.right = '10px'; container.style.top = '50%'; container.style.transform = 'translateY(-50%)'; container.style.backgroundColor = '#343541'; container.style.color = '#ECECF1'; container.style.padding = '12px 15px'; container.style.borderRadius = '8px'; container.style.fontSize = '14px'; container.style.zIndex = '9999'; container.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)'; container.style.border = '1px solid #565869'; container.style.display = 'flex'; container.style.flexDirection = 'column'; container.style.gap = '8px'; // 创建标题 const title = document.createElement('div'); title.style.fontWeight = 'bold'; title.style.marginBottom = '5px'; title.textContent = 'Deep Research Usage'; // 创建固定窗口计数器 const fixedCounter = document.createElement('div'); fixedCounter.id = 'dr-fixed-counter'; // 创建滚动窗口计数器 const rollingCounter = document.createElement('div'); rollingCounter.id = 'dr-rolling-counter'; // 创建总计数器 const totalCounter = document.createElement('div'); totalCounter.id = 'dr-total-counter'; totalCounter.style.marginTop = '5px'; container.appendChild(title); container.appendChild(fixedCounter); container.appendChild(rollingCounter); container.appendChild(totalCounter); document.body.appendChild(container); updateCounters(); } // 排序任务按创建时间 function sortTasksByCreationTime() { return [...deepResearchTasks].sort((a, b) => new Date(a.created_at) - new Date(b.created_at) ); } // 找出最早的任务 function getFirstTaskDate() { if (deepResearchTasks.length === 0) return null; const sortedTasks = sortTasksByCreationTime(); return new Date(sortedTasks[0].created_at); } // 计算当前固定30天窗口 function getFixedWindowUsage() { const firstTaskDate = getFirstTaskDate(); if (!firstTaskDate) return { count: 0 }; const now = new Date(); const daysSinceFirstUse = Math.floor((now - firstTaskDate) / (24 * 60 * 60 * 1000)); const currentWindowIndex = Math.floor(daysSinceFirstUse / 30); const windowStart = new Date(firstTaskDate); windowStart.setDate(windowStart.getDate() + (currentWindowIndex * 30)); const windowEnd = new Date(windowStart); windowEnd.setDate(windowEnd.getDate() + 30); let count = 0; for (const task of deepResearchTasks) { const taskDate = new Date(task.created_at); if (taskDate >= windowStart && taskDate < windowEnd) { count++; } } const daysLeft = Math.max(0, 30 - Math.floor((now - windowStart) / (24 * 60 * 60 * 1000))); return { count, windowStart, windowEnd, daysLeft }; } // 更新或计算滚动窗口 function updateRollingWindows() { if (deepResearchTasks.length === 0) return; const sortedTasks = sortTasksByCreationTime(); const now = new Date(); // 如果没有任何窗口,从第一个任务开始 if (rollingWindows.length === 0) { const firstTask = sortedTasks[0]; const startDate = new Date(firstTask.created_at); const endDate = new Date(startDate); endDate.setDate(endDate.getDate() + 30); rollingWindows.push({ start: startDate.toISOString(), end: endDate.toISOString() }); } // 检查是否需要创建新窗口 const lastWindow = rollingWindows[rollingWindows.length - 1]; const lastWindowEnd = new Date(lastWindow.end); // 找出在上一个窗口结束后的第一个任务 let nextWindowTask = null; for (const task of sortedTasks) { const taskDate = new Date(task.created_at); if (taskDate > lastWindowEnd) { nextWindowTask = task; break; } } // 如果找到了这样的任务,创建新窗口 if (nextWindowTask) { const newWindowStart = new Date(nextWindowTask.created_at); const newWindowEnd = new Date(newWindowStart); newWindowEnd.setDate(newWindowEnd.getDate() + 30); rollingWindows.push({ start: newWindowStart.toISOString(), end: newWindowEnd.toISOString() }); // 递归检查是否需要更多窗口 updateRollingWindows(); } // 保存窗口历史 localStorage.setItem(ROLLING_WINDOW_HISTORY_KEY, JSON.stringify(rollingWindows)); } // 获取当前有效的滚动窗口 function getCurrentRollingWindow() { const now = new Date(); // 更新滚动窗口历史 updateRollingWindows(); // 找出当前有效的窗口(包含当前时间的窗口) for (let i = rollingWindows.length - 1; i >= 0; i--) { const window = rollingWindows[i]; const windowStart = new Date(window.start); const windowEnd = new Date(window.end); if (now >= windowStart && now < windowEnd) { return { windowStart, windowEnd }; } } // 如果没有当前有效的窗口,返回null return null; } // 计算滚动窗口中的使用量 function getRollingWindowUsage() { const currentWindow = getCurrentRollingWindow(); if (!currentWindow) return { count: 0, active: false }; const { windowStart, windowEnd } = currentWindow; const now = new Date(); let count = 0; for (const task of deepResearchTasks) { const taskDate = new Date(task.created_at); if (taskDate >= windowStart && taskDate < windowEnd) { count++; } } const daysLeft = Math.max(0, 30 - Math.floor((now - windowStart) / (24 * 60 * 60 * 1000))); return { count, windowStart, windowEnd, daysLeft, active: true }; } // 格式化日期为 YYYY-MM-DD function formatDate(date) { return date.toISOString().split('T')[0]; } // 更新计数器 function updateCounters() { const fixedCounter = document.getElementById('dr-fixed-counter'); const rollingCounter = document.getElementById('dr-rolling-counter'); const totalCounter = document.getElementById('dr-total-counter'); if (fixedCounter && rollingCounter && totalCounter) { const fixedUsage = getFixedWindowUsage(); const rollingUsage = getRollingWindowUsage(); // 更新固定窗口计数 if (deepResearchTasks.length > 0) { fixedCounter.innerHTML = `<span style="color:#8E8EA0">固定窗口:</span> ${fixedUsage.count} 使用 <span style="color:#8E8EA0">(还剩${fixedUsage.daysLeft}天)</span><br> <span style="font-size:12px;color:#8E8EA0">${formatDate(fixedUsage.windowStart)} ~ ${formatDate(fixedUsage.windowEnd)}</span>`; } else { fixedCounter.innerHTML = `<span style="color:#8E8EA0">固定窗口:</span> 未初始化`; } // 更新滚动窗口计数 if (rollingUsage.active) { rollingCounter.innerHTML = `<span style="color:#8E8EA0">滚动窗口:</span> ${rollingUsage.count} 使用 <span style="color:#8E8EA0">(还剩${rollingUsage.daysLeft}天)</span><br> <span style="font-size:12px;color:#8E8EA0">${formatDate(rollingUsage.windowStart)} ~ ${formatDate(rollingUsage.windowEnd)}</span>`; } else { rollingCounter.innerHTML = `<span style="color:#8E8EA0">滚动窗口:</span> 未初始化`; } // 更新总计数 totalCounter.textContent = `总计: ${deepResearchTasks.length} 任务`; } } // 拦截 fetch 请求 const originalFetch = window.fetch; window.fetch = async function(input, init) { const response = await originalFetch(input, init); // 检查这是否是对任务 API 的请求 if (input && input.toString().includes('/backend-api/tasks')) { // 克隆响应以读取它 const clone = response.clone(); try { const data = await clone.json(); if (data && data.tasks) { // 筛选深度研究任务 const tasks = data.tasks.filter(task => task.task_id.startsWith('deepresch_') || task.task_id.startsWith('task_') ); // 更新任务列表 const oldTasksCount = deepResearchTasks.length; deepResearchTasks = tasks; localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks)); // 如果任务数量变化了,更新滚动窗口和计数器 if (oldTasksCount !== tasks.length) { updateRollingWindows(); updateCounters(); } console.log('Deep Research Tasks:', tasks.length); } } catch (e) { console.error('解析任务数据时出错:', e); } } return response; }; // DOM加载后初始化所有内容 window.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (!document.getElementById('deep-research-tracker')) { updateRollingWindows(); // 初始化滚动窗口 createUI(); } }, 1000); }); })();
<| END_CONTEXT |>
该脚本的作用是追踪 ChatGPT 网站中“Deep Research”(深度研究)功能的使用量,具体表现为:
记录任务使用情况:
它会通过拦截发往 ChatGPT 后端的 API 请求,特别是 /backend-api/tasks 这个接口,收集用户使用“深度研究”功能的数据(即任务的创建时间与数量),并将这些信息存储到浏览器本地存储中 (localStorage)。
计算和显示用量:
脚本提供两种统计方式:
UI展示:
脚本在 ChatGPT 网页上创建一个浮动的侧边栏UI,用以实时展示上述数据(包括固定周期和滚动周期内的使用情况及对应日期区间)。
数据范围:
该脚本仅本地存储数据,不向任何外部服务器发送数据,也没有外部网络请求的代码片段,因此并不直接存在数据泄漏风险。
无明显恶意行为:
本脚本没有发现任何明显的恶意操作(如发送敏感数据到外部网站、注入恶意代码等)。
隐私注意事项:
由于此脚本通过拦截和存储API响应数据(即用户在ChatGPT上创建的任务数量和创建时间),在一定程度上记录了用户的使用习惯,因此可能存在间接的隐私风险(例如:如果未经用户明确知情安装此脚本,可能导致隐私敏感用户感到不安)。
建议用户仅在明确理解此脚本用途和影响下使用。