diff --git a/README.md b/README.md index 5ac0c6b..f71219c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ 白名单 -### 指纹嗅探页面(静态,开发中) +### 指纹嗅探页面
指纹嗅探
@@ -163,7 +163,10 @@ - 完善ID密钥的匹配,增加过滤集 ### 2025-01-27 - 完善一些过滤细节 - +### 2025-02-05 +- 完善指纹嗅探功能 +- 完善响应包处理 +- 增加更多的第三方库 --- **雪瞳——洞悉无形,守护无界。** \ No newline at end of file diff --git a/background/background.js b/background/background.js index ea1486c..d537ef1 100644 --- a/background/background.js +++ b/background/background.js @@ -109,6 +109,14 @@ function parseServerHeader(serverHeader) { name: name, version: part.includes('/') ? part.split('/')[1] : null }; + + // 如果是 IIS,直接判定为 Windows 系统 + if (serverName.includes('microsoft-iis') && !components.os) { + components.os = { + name: 'Windows', + version: 'windows' + }; + } } } // 处理其他组件 diff --git a/content/config/scanner.config.js b/content/config/scanner.config.js index 9c1fba8..f85b44c 100644 --- a/content/config/scanner.config.js +++ b/content/config/scanner.config.js @@ -14,24 +14,24 @@ const SCANNER_CONFIG = { // 需要跳过的第三方JS库正则匹配规则 SKIP_JS_PATTERNS: [ // jQuery相关 - /jquery([.-]?\d*\.?\d*\.?\d*)?(?:[\.-]cookie)?(?:[\.-]fancybox)?(?:[\.-]validate)?(?:[\.-]artDialog)?(?:[\.-]blockui)?(?:[\.-]pack)?(?:[\.-]base64)?(?:[\.-]md5)?(?:[\.-]min)?\.js$/i, - /(?:vue|vue-router|vuex)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^jquery([.-]?\d*\.?\d*\.?\d*)?(?:[\.-]cookie)?(?:[\.-]fancybox)?(?:[\.-]validate)?(?:[\.-]artDialog)?(?:[\.-]blockui)?(?:[\.-]pack)?(?:[\.-]base64)?(?:[\.-]md5)?(?:[\.-]min)?\.js$/i, + /^(?:vue|vue-router|vuex)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, // React相关 - /(react|react-dom)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^(react|react-dom)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, // Bootstrap相关 - /bootstrap(?:\.bundle)?[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^bootstrap(?:\.bundle)?[.-]?\d*\.?\d*\.?\d*(?:[\.-]datepicker|datetimepicker)?(?:[\.-]zh-CN)?(?:[\.-]min)?\.js$/i, // UI框架相关 - /(layui|lay|layer|liger|h-ui|element-ui|ueditor|kindeditor|ant-design)[.-]?\d*\.?\d*\.?\d*(?:[\.-]all)?(?:\.admin)?(?:[\.-]config)?(?:[\.-]min)?\.js$/i, + /^(layui|lay|layer|liger|h-ui|element-ui|ueditor|kindeditor|ant-design)[.-]?\d*\.?\d*\.?\d*(?:[\.-]all)?(?:\.admin)?(?:[\.-]config)?(?:[\.-]min)?\.js$/i, // 图表相关 - /(echarts|chart|highcharts)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^(echarts|chart|highcharts)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, // 工具库相关 - /(lodash|moment|tableexport|axios|plupload|pqgrid)[.-]?\d*\.?\d*\.?\d*(?:\.full)?(?:\.min)?\.js$/i, + /^(lodash|moment|tableexport|axios|plupload|pqgrid)[.-]?\d*\.?\d*\.?\d*(?:\.full)?(?:\.min)?\.js$/i, // 其他常用库 - /(polyfill|modernizr|device|less|isotope.pkgd|lhgdialog|kendo.web|seajs-style|seajs-text|tinymce|jsencrypt|backbone|select2|underscore|ext-all|ext-unigui-min|exporter|v5_float_4)[.-]?\d*\.?\d*\.?\d*(?:[\.-]dev)?(?:[\.-]min)?\.js$/i, + /^(polyfill|modernizr|device|less|isotope.pkgd|lhgdialog|kendo.web|dataTables|editor|seajs-style|seajs-text|tinymce|jsencrypt|backbone|select2|underscore|ext-all|ext-unigui-min|exporter|buttons|v5_float_4)[.-]?\d*\.?\d*\.?\d*(?:[\.-]dev)?(?:[\.-]html5|bootstrap|print|full)?(?:[\.-]min)?\.js$/i, // 日期选择器 - /(datepicker|datetimepicker|wdatepicker|laydate)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^(datepicker|datetimepicker|wdatepicker|laydate)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, // 语言包 - /(?:zh|en|zh-cn|zh-tw|ja|ko)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, + /^(?:zh|en|zh-cn|zh-tw|ja|ko)[.-]?\d*\.?\d*\.?\d*(?:\.min)?\.js$/i, ], // 需要过滤的内容类型 FILTERED_CONTENT_TYPES: [ @@ -139,6 +139,31 @@ const SCANNER_CONFIG = { } }, + // 统一的黑名单配置 + BLACKLIST: { + // 值黑名单 - 从 ID_KEY 配置中移出 + VALUES: new Set([ + 'input', 'about', 'video', 'null', 'top', 'gener', 'charlendar', 'met', + 'login', 'read', 'black', 'hand', 'next', 'nect', 'system', 'panel', 'body', + 'load', 'red', 'green', 'shift', 'item', 'init', 'print','button','left', + 'finger', 'mark', 'down', 'up', 'ctrl', 'play', 'row', 'ntal', 'header', + 'has', 'range', 'new', 'json', 'url', 'uri', 'long', 'big','right', 'slider', + 'html', 'tion', 'default', 'set', 'serve','select', 'view', 'false', 'expect', + 'broad', 'status', 'dom', 'use', 'rect', 'guard', 'index', 'link', + 'opera', 'main', 'tech','insert', 'char', 'core', 'turn', 'number', 'atom', + 'visual','get', 'type','write','error','array','parse','inter', 'size', + 'statis','card','extract','full','white','refer', 'wrap', 'emp', 'this', + 'crypt','normal','single','double','triple','more', 'message', 'profile', + 'tool','tance','popper', 'radio', 'area', 'tip', 'color', 'hid', + 'old', 'place', 'parm', 'axis', 'high', 'true', 'simple', 'bool', 'pick', + 'date', 'cancel', 'croll', 'late', 'note', 'length', 'class', 'light', + 'param','tend', 'title', 'work','lang', 'func', 'variable', 'dark', + 'data', 'object', 'brac', 'beare', 'string', 'self', 'void', + 'opts', 'token', 'result', 'text', 'con', 'stor', 'sup', 'pun', + 'key', 'com', 'ent', 'case', 'pare', 'dent', 'reg', 'invalid' + ]) + }, + // ID密钥相关配置 ID_KEY: { PATTERNS: [ @@ -158,25 +183,7 @@ const SCANNER_CONFIG = { ], // 关键词黑名单 KEY_BLACKLIST: new Set([ - 'size', 'row', 'dict', 'up', 'highlight', 'cabin' - ]), - // 值黑名单 - VALUE_BLACKLIST: new Set([ - 'input', 'about', 'video', 'null', 'top', 'gener', 'charlendar', 'content', - 'login', 'read', 'black', 'hand', 'next', 'nect', 'system', 'panel', 'body', - 'load', 'red', 'green', 'shift', 'item', 'init', 'print','button','left', - 'finger', 'mark', 'down', 'up', 'ctrl', 'play', 'row', 'ntal', 'header','contain', - 'has', 'range', 'new', 'json', 'url', 'uri', 'long', 'big','right', 'slider', - 'html', 'event', 'tion', 'comp', 'default', 'set', 'serve','select', 'view', - 'broad', 'status', 'dom', 'use', 'rect', 'ment', 'guard', 'index', 'link', - 'dentified', 'opera', 'main', 'tech','insert', 'char','command', 'core', - 'visual','get', 'type','write','error','array','parse','inter', 'size', - 'statis','discard','extract','full','white','refer','parent', 'wrap', - 'convert','crypt','normal','single','double','triple','more', 'message', - 'tool','tance','popper','region', 'radio', 'area', 'tip', 'color', 'hid', - 'old', 'place', 'parm', 'axis', 'high', 'ture', 'simple', 'bool', 'pick', - 'date', 'cancel', 'croll', 'late', 'note', 'confirm', 'length', 'class', - 'param','tend', 'title', 'work','lang' + 'size', 'row', 'dict', 'up', 'highlight', 'cabin', 'cross' ]) } }; diff --git a/content/content.js b/content/content.js index e788aa3..520a69c 100644 --- a/content/content.js +++ b/content/content.js @@ -1,3 +1,11 @@ +// 在文件开头添加动态扫描状态变量 +let dynamicScanEnabled = true; + +// 在初始化时获取设置 +chrome.storage.local.get(['dynamicScan'], (result) => { + dynamicScanEnabled = result.dynamicScan !== false; +}); + // 等待依赖加载 - 简化检查 const waitForDependencies = () => { const deps = [ @@ -280,6 +288,9 @@ async function initScan() { // 使用 MutationObserver 监听 DOM 变化 const observer = new MutationObserver((mutations) => { + // 如果动态扫描被禁用,直接返回 + if (!dynamicScanEnabled) return; + // 过滤掉不重要的变化 const significantChanges = mutations.filter(mutation => { // 忽略文本内容的微小变化 @@ -344,6 +355,7 @@ async function collectAndScanResources() { // 检查是否是第三方库 const isThirdPartyLib = (url) => { const fileName = url.split('/').pop()?.split('?')[0]?.toLowerCase() || ''; + // console.log('fileName', fileName); return SCANNER_CONFIG.API.SKIP_JS_PATTERNS.some(pattern => pattern.test(fileName)); }; @@ -507,6 +519,9 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { WHITELIST: SCANNER_CONFIG.DOMAIN.WHITELIST } }); + } else if (request.type === 'UPDATE_DYNAMIC_SCAN') { + dynamicScanEnabled = request.enabled; + sendResponse({ success: true }); } } catch (e) { // 扩展上下文失效时忽略错误 diff --git a/content/filters/scanner.filter.js b/content/filters/scanner.filter.js index ccee10c..387f915 100644 --- a/content/filters/scanner.filter.js +++ b/content/filters/scanner.filter.js @@ -276,19 +276,22 @@ const SCANNER_FILTER = { if (!value.length||key==value) { return false; } - if (regexCache.keywordPattern.test(value)) return false; + + for (const blackWord of SCANNER_CONFIG.BLACKLIST.VALUES) { + if (value.includes(blackWord)) { + return false; + } + } resultsSet?.cookies?.add(match); return true; }, id_key: (match, resultsSet) => { if (match.match(/[:=]/)) { - // 检查是否是空值 const valueMatch = match.replace(/\s+/g,'').split(/[:=]/); var key = valueMatch[0].replace(/['"]/g,''); var value = valueMatch[1].replace(/['"]/g,''); - // 转换为小写进行检查 const keyLower = key.toLowerCase(); const valueLower = value.toLowerCase(); @@ -303,8 +306,8 @@ const SCANNER_FILTER = { } } - // 检查value是否包含黑名单词 - for (const blackWord of SCANNER_CONFIG.ID_KEY.VALUE_BLACKLIST) { + // 检查value是否在统一黑名单中 + for (const blackWord of SCANNER_CONFIG.BLACKLIST.VALUES) { if (valueLower.includes(blackWord)) { return false; } diff --git a/icons/xiutan.png b/icons/xiutan.png index 5ee00d1..6a4bf84 100644 Binary files a/icons/xiutan.png and b/icons/xiutan.png differ diff --git a/popup/highlight.css b/popup/highlight.css index 331e7b9..f364fca 100644 --- a/popup/highlight.css +++ b/popup/highlight.css @@ -676,4 +676,59 @@ body { font-family: monospace; word-break: break-all; } + +/* 开关按钮样式 */ +.switch-wrapper { + display: flex; + align-items: center; + cursor: pointer; + margin-bottom: 8px; +} + +.switch-input { + position: relative; + width: 40px; + height: 20px; + margin: 0; + margin-right: 8px; + background: #e9ecef; + border-radius: 20px; + border: none; + outline: none; + cursor: pointer; + -webkit-appearance: none; + transition: all 0.3s; +} + +.switch-input:checked { + background: #228be6; +} + +.switch-input::before { + content: ''; + position: absolute; + top: 2px; + left: 2px; + width: 16px; + height: 16px; + background: white; + border-radius: 50%; + transition: all 0.3s; +} + +.switch-input:checked::before { + transform: translateX(20px); +} + +.switch-label { + font-size: 14px; + color: #495057; +} + +.config-desc { + font-size: 12px; + color: #868e96; + margin-top: 4px; + margin-left: 48px; +} \ No newline at end of file diff --git a/popup/popup.html b/popup/popup.html index 13f56f8..79478f5 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -68,7 +68,33 @@
-
+
+
+
+

扫描白名单

+
+ 以下域名将不会被扫描: +
+
+ +
+
+ + +
+

扫描设置

+
+ +
+ 启用后将自动扫描页面动态变化的内容,适用于单页应用等动态渲染的页面 +
+
+
+
+
diff --git a/popup/popup.js b/popup/popup.js index 003df24..7f17228 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -22,31 +22,18 @@ function switchPage(pageName) { // 显示配置信息 function displayConfig(config) { - const container = document.querySelector('.config-page .container'); + const whitelistDomains = document.querySelector('.whitelist-domains'); if (!config) { - container.innerHTML = '
无法获取配置信息
'; + whitelistDomains.innerHTML = '
无法获取配置信息
'; return; } - let html = ` -
-
-

扫描白名单

-
- 以下域名将不会被扫描: -
-
- ${config.WHITELIST ? config.WHITELIST.map(domain => ` -
- ${domain} -
- `).join('') : ''} -
-
+ // 显示白名单域名 + whitelistDomains.innerHTML = config.WHITELIST ? config.WHITELIST.map(domain => ` +
+ ${domain}
- `; - - container.innerHTML = html; + `).join('') : ''; } // 显示扫描结果的函数 @@ -322,6 +309,7 @@ function handleLongText() { function initConfigPage() { chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { if (tabs[0]) { + // 获取配置信息 chrome.tabs.sendMessage(tabs[0].id, {type: 'GET_CONFIG'}, response => { if (chrome.runtime.lastError || !response || !response.config) { const container = document.querySelector('.config-page .container'); @@ -329,6 +317,26 @@ function initConfigPage() { return; } displayConfig(response.config); + + // 获取动态扫描设置 + chrome.storage.local.get(['dynamicScan'], (result) => { + const dynamicScanCheckbox = document.getElementById('dynamicScan'); + if (dynamicScanCheckbox) { + dynamicScanCheckbox.checked = result.dynamicScan !== false; // 默认开启 + + // 添加变更监听 + dynamicScanCheckbox.addEventListener('change', (e) => { + const enabled = e.target.checked; + chrome.storage.local.set({ dynamicScan: enabled }); + + // 通知 content script 更新设置 + chrome.tabs.sendMessage(tabs[0].id, { + type: 'UPDATE_DYNAMIC_SCAN', + enabled: enabled + }); + }); + } + }); }); } });