新增深度扫描

This commit is contained in:
sickle committed 2025-02-14 18:34:17 +08:00
1 parent b5c8ec0aa5
commit d3a2712e20
3 files changed
+141 -110

No files matched your search

+94 -110
View File
@@ -1,9 +1,11 @@
// 在文件开头添加动态扫描状态变量
let dynamicScanEnabled = true;
let deepScanEnabled = false;
// 在初始化时获取设置
chrome.storage.local.get(['dynamicScan'], (result) => {
chrome.storage.local.get(['dynamicScan', 'deepScan'], (result) => {
dynamicScanEnabled = result.dynamicScan !== false;
deepScanEnabled = result.deepScan === true;
});
// 等待依赖加载 - 简化检查
@@ -44,6 +46,8 @@ const latestResults = {
fingers: new Set(), // 指纹结果集
};
// 添加一个Set用于记录已扫描过的JS URL,避免重复扫描
const scannedJsUrls = new Set();
// 优化扫描函数
async function scanSources(sources, isHtmlContent = false) {
@@ -362,135 +366,110 @@ async function initScan() {
}
}
// 增量收集和扫描资源
async function collectAndScanResources() {
// 修改收集和扫描资源的函数
async function collectAndScanResources(depth = 0, maxDepth = 3) {
try {
// 如果未启用深度扫描,则只扫描第一层
if (!deepScanEnabled) {
maxDepth = 1; // 未启用深度扫描时,将最大深度设为1
}
// 如果超过最大深度,则停止递归
if (depth >= maxDepth) {
return;
}
// 检查是否是第三方库
const isThirdPartyLib = (url) => {
const fileName = url.split('/').pop()?.split('?')[0]?.toLowerCase() || '';
return SCANNER_CONFIG.API.SKIP_JS_PATTERNS.some(pattern => pattern.test(fileName));
};
// 1. 收集并扫描关键资源的URL
const criticalSelectors = [
['a[href^="http"]', 'href'],
['script[src]', 'src'],
['link[href]', 'href']
];
for (const [selector, attr] of criticalSelectors) {
try {
const elements = document.querySelectorAll(selector);
const urls = [];
for (const el of elements) {
const value = el[attr];
if (value) urls.push(value);
}
if (urls.length > 0) {
scanSources(urls);
}
} catch (e) {
console.error('Error collecting resources:', e);
}
}
// 2. 收集并扫描meta标签内容
try {
const metas = document.querySelectorAll('meta[content]');
const contents = [];
for (const meta of metas) {
const content = meta.content;
if (content) contents.push(content);
}
if (contents.length > 0) {
scanSources(contents);
}
} catch (e) {
console.error('Error collecting meta content:', e);
}
// 3. 收集并扫描数据属性
try {
const sensitiveDataAttrs = ['api', 'url', 'endpoint', 'server', 'config'];
for (const attr of sensitiveDataAttrs) {
const elements = document.querySelectorAll(`[data-${attr}]`);
const values = [];
for (const el of elements) {
const value = el.dataset[attr];
if (value) values.push(value);
}
if (values.length > 0) {
scanSources(values);
}
}
} catch (e) {
console.error('Error collecting data attributes:', e);
}
// 4. 异步获取并扫描脚本内容
const baseUrl = window.location.origin;
// 从document.scripts获取
const scripts = Array.from(document.scripts)
.filter(script => script?.src && typeof script.src === 'string' && script.src.trim());
// 从页面内容匹配获取
const pageContent = document.documentElement.outerHTML;
const jsPattern = /['"](?:[^'"]+\.(?:js)(?:\?[^\s'"]*)?)['"]/g;
const jsMatches = Array.from(pageContent.matchAll(jsPattern))
.map(match => {
const path = match[0].slice(1, -1);
try {
if (path.startsWith('http')) {
return path;
} else if (path.startsWith('//')) {
return window.location.protocol + path;
} else if (path.startsWith('/')) {
return baseUrl + path;
} else {
return new URL(path, baseUrl).href;
}
} catch (e) {
console.error('Error processing JS path:', e);
return null;
}
})
.filter(url => url !== null);
// 合并所有JS文件URL并去重
const allJsUrls = new Set([
...scripts.map(script => script.src),
...jsMatches
]);
// 并行获取脚本内容,但每个脚本获取后立即扫描
const jsContents = new Set();
const fetchPromises = Array.from(allJsUrls).map(async url => {
if (jsContents.has(url)) return;
jsContents.add(url);
// 收集当前页面的所有JS URL
const collectJsUrls = (content) => {
const jsUrls = new Set();
const baseUrl = window.location.origin;
// 检查是否是第三方库,如果是则跳过
if (isThirdPartyLib(url)) {
logger.info('跳过第三方库:', url);
// 从script标签收集
if (typeof document !== 'undefined') {
Array.from(document.scripts)
.filter(script => script?.src && typeof script.src === 'string' && script.src.trim())
.forEach(script => jsUrls.add(script.src));
}
// 从内容中匹配JS URL
const jsPattern = /['"](?:[^'"]+\.(?:js)(?:\?[^\s'"]*)?)['"]/g;
const matches = Array.from(content.matchAll(jsPattern))
.map(match => {
const path = match[0].slice(1, -1);
try {
if (path.startsWith('http')) {
return path;
} else if (path.startsWith('//')) {
return window.location.protocol + path;
} else if (path.startsWith('/')) {
return baseUrl + path;
} else {
return new URL(path, baseUrl).href;
}
} catch (e) {
console.error('Error processing JS path:', e);
return null;
}
})
.filter(url => url !== null);
matches.forEach(url => jsUrls.add(url));
return jsUrls;
};
// 扫描单个JS文件
const scanJsFile = async (url, currentDepth) => {
// 如果已经扫描过或是第三方库,则跳过
if (scannedJsUrls.has(url) || isThirdPartyLib(url)) {
return;
}
// 如果超过最大深度,则停止递归
if (currentDepth >= maxDepth) {
return;
}
try {
const response = await new Promise(resolve => {
chrome.runtime.sendMessage({type: 'FETCH_JS', url: url}, resolve);
});
if (response?.content) {
// 立即扫描获取到的脚本内容
// 记录已扫描
scannedJsUrls.add(url);
// 扫描当前JS内容
scanSources([response.content]);
// 只在启用深度扫描时进行递归
if (deepScanEnabled) {
// 收集并递归扫描该JS中引用的其他JS
const newJsUrls = collectJsUrls(response.content);
for (const newUrl of newJsUrls) {
if (!scannedJsUrls.has(newUrl)) {
await scanJsFile(newUrl, currentDepth + 1);
}
}
}
}
} catch (e) {
console.error('Error fetching script:', url, e);
console.error('Error scanning JS:', url, e);
}
});
};
// 收集当前页面的JS URL
const currentJsUrls = collectJsUrls(document.documentElement.outerHTML);
// 并行扫描所有JS文件
const scanPromises = Array.from(currentJsUrls).map(url => scanJsFile(url, depth));
await Promise.all(scanPromises);
// 等待所有脚本获取完成
await Promise.all(fetchPromises);
} catch (e) {
console.error('Error in collectAndScanResources:', e);
}
@@ -534,6 +513,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
} else if (request.type === 'UPDATE_DYNAMIC_SCAN') {
dynamicScanEnabled = request.enabled;
sendResponse({ success: true });
} else if (request.type === 'UPDATE_DEEP_SCAN') {
deepScanEnabled = request.enabled;
// 清空已扫描的JS URL集合,这样切换设置后可以重新扫描
scannedJsUrls.clear();
sendResponse({ success: true });
}
} catch (e) {
// 扩展上下文失效时忽略错误
+9
View File
@@ -90,6 +90,15 @@
启用后将自动扫描页面动态变化的内容,适用于单页应用等动态渲染的页面
</div>
</div>
<div class="config-item">
<label class="switch-wrapper">
<input type="checkbox" id="deepScan" class="switch-input">
<span class="switch-label">深度扫描</span>
</label>
<div class="config-desc">
启用后将递归扫描JS文件中引用的其他JS文件,可能会增加扫描时间
</div>
</div>
</div>
</div>
</div>
+38
View File
@@ -265,6 +265,44 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
});
// 获取动态扫描开关状态
chrome.storage.local.get(['dynamicScan', 'deepScan'], (result) => {
document.getElementById('dynamicScan').checked = result.dynamicScan !== false;
document.getElementById('deepScan').checked = result.deepScan === true;
});
// 监听动态扫描开关变化
document.getElementById('dynamicScan').addEventListener('change', (e) => {
const enabled = e.target.checked;
chrome.storage.local.set({ dynamicScan: enabled });
// 通知content script更新设置
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
if (tabs[0]) {
chrome.tabs.sendMessage(tabs[0].id, {
type: 'UPDATE_DYNAMIC_SCAN',
enabled: enabled
});
}
});
});
// 监听深度扫描开关变化
document.getElementById('deepScan').addEventListener('change', (e) => {
const enabled = e.target.checked;
chrome.storage.local.set({ deepScan: enabled });
// 通知content script更新设置
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
if (tabs[0]) {
chrome.tabs.sendMessage(tabs[0].id, {
type: 'UPDATE_DEEP_SCAN',
enabled: enabled
});
}
});
});
});
// 监听来自 content script 的消息