Skip to content

Commit 8f37e1f

Browse files
committed
fix: 跳过浏览器内置页面检测
统一页面支持判断,浏览器内置页和扩展页不再触发后台检测、缓存写入和徽标显示。 补充 WebSocket 子请求观察与通用识别规则,HTTP/HTTPS 页面中的 ws/wss 握手会作为 API 线索合并展示。 插件版本升级到 1.2.2。
1 parent ecaf4a7 commit 8f37e1f

14 files changed

Lines changed: 221 additions & 39 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "stackprism",
33
"private": true,
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"type": "module",
66
"description": "StackPrism 用于检测网页前端、后端、CDN、SaaS、广告营销、统计、登录、支付、网站程序和主题模板线索。",
77
"scripts": {

public/rules/headers/header-patterns.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,19 @@
445445
}
446446
]
447447
},
448+
{
449+
"defaults": {
450+
"category": "安全与协议",
451+
"confidence": ""
452+
},
453+
"rules": [
454+
{
455+
"name": "WebSocket",
456+
"patterns": ["url:\\s*wss?://|(?:^|\\n)upgrade:\\s*websocket|(?:^|\\n)sec-websocket-accept:"],
457+
"evidence": "WebSocket 握手或 ws/wss 请求 URL"
458+
}
459+
]
460+
},
448461
{
449462
"category": "CMS / 电商平台",
450463
"name": "Shopify",

public/rules/headers/interesting-headers.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
"link",
3535
"set-cookie",
3636
"content-type",
37+
"upgrade",
38+
"sec-websocket-accept",
39+
"sec-websocket-protocol",
3740
"server-timing",
3841
"x-cdn",
3942
"x-cdn-provider",

public/rules/page/build-runtime.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@
5353
"confidence": "",
5454
"selectors": ["link[rel=\"manifest\"]"]
5555
},
56+
{
57+
"category": "安全与协议",
58+
"name": "WebSocket",
59+
"confidence": "",
60+
"resourceOnly": true,
61+
"resourceHints": ["ws://", "wss://"],
62+
"patterns": ["^wss?://"]
63+
},
5664
{
5765
"name": "Zone.js",
5866
"confidence": "",

public/tech-links.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"links": {
44
"965": "https://www.drupal.org/project/ninesixtyfive",
55
"1140": "http://cssgrid.net",
6+
"WebSocket": "https://developer.mozilla.org/docs/Web/API/WebSockets_API",
67
"Apache Solr Admin": "https://solr.apache.org",
78
"Elasticvue": "https://elasticvue.com",
89
"Dejavu": "https://github.com/appbaseio/dejavu",

src/background/content-injector.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
const canInjectContentObserver = (tab: chrome.tabs.Tab): boolean =>
2-
typeof tab?.id === 'number' && /^https?:\/\//i.test(String(tab.url || ''))
1+
import { isDetectablePageUrl } from '@/utils/page-support'
2+
3+
const canInjectContentObserver = (tab: chrome.tabs.Tab): boolean => typeof tab?.id === 'number' && isDetectablePageUrl(tab.url)
34

45
export const injectContentObserver = async (tabId: number): Promise<void> => {
56
const observerFile = chrome.runtime.getManifest().content_scripts?.[0]?.js?.[0]

src/background/detection.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { augmentPageWithWordPressThemeStyles } from './wordpress'
22
import { buildPopupCacheRecord, cleanPageDetectionRecord } from './popup-cache'
33
import { fetchMainHeadersFallback } from './headers'
4-
import { clearBadge, getTabData, getTabSnapshot, updateBadgeForTab, writeTabData } from './tab-store'
4+
import { clearBadge, clearTabSession, getTabData, getTabSnapshot, updateBadgeForTab, writeTabData } from './tab-store'
55
import { buildEffectivePageRules, loadDetectorSettings, loadTechRules } from './detector-settings'
6+
import { isDetectablePageUrl } from '@/utils/page-support'
67

78
const activeDetectionTimers = new Map<number, ReturnType<typeof setTimeout>>()
89

910
export const saveTabDataAndBadge = async (tabId: number, data: any, settings: any) => {
10-
const popup = buildPopupCacheRecord(data, settings, await getTabSnapshot(tabId))
11+
const tab = await getTabSnapshot(tabId)
12+
if (!isDetectablePageUrl(tab.url)) {
13+
await clearTabSession(tabId)
14+
clearBadge(tabId)
15+
return
16+
}
17+
const popup = buildPopupCacheRecord(data, settings, tab)
1118
const { popup: _legacyPopup, ...tabData } = data || {}
1219
await writeTabData(tabId, tabData, popup)
1320
await updateBadgeForTab(tabId, popup)
@@ -18,6 +25,11 @@ export const refreshAllBadges = async () => {
1825
const [tabs, settings] = await Promise.all([chrome.tabs.query({}), loadDetectorSettings()])
1926
for (const tab of tabs) {
2027
if (typeof tab.id !== 'number' || tab.id < 0) continue
28+
if (!isDetectablePageUrl(tab.url)) {
29+
await clearTabSession(tab.id)
30+
clearBadge(tab.id)
31+
continue
32+
}
2133
const data = await getTabData(tab.id)
2234
if (data && Object.keys(data).length) {
2335
await saveTabDataAndBadge(tab.id, data, settings)
@@ -34,6 +46,12 @@ export const runActivePageDetection = async (tabId: number) => {
3446
if (typeof tabId !== 'number' || tabId < 0) return
3547

3648
try {
49+
const tab = await getTabSnapshot(tabId)
50+
if (!isDetectablePageUrl(tab.url)) {
51+
await clearTabSession(tabId)
52+
clearBadge(tabId)
53+
return
54+
}
3755
const [data, rules, settings] = await Promise.all([getTabData(tabId), loadTechRules(), loadDetectorSettings()])
3856
const pageRules = buildEffectivePageRules(rules.page || {}, settings)
3957
await chrome.scripting.executeScript({

src/background/dynamic-snapshot.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-nocheck
22
import { safeDecodeURIComponent } from '@/utils/url'
3+
import { isDetectablePageUrl } from '@/utils/page-support'
34
import { mergeTechnologyRecords, normalizeDynamicFallbackTechName, shortHeaderUrl } from './merge'
45
import {
56
createCollector,
@@ -8,7 +9,7 @@ import {
89
matchesRuleTextHints,
910
passesRulePrefilter
1011
} from './rule-matcher'
11-
import { getTabData } from './tab-store'
12+
import { clearBadge, clearTabSession, getTabData, getTabSnapshot } from './tab-store'
1213
import { saveTabDataAndBadge, scheduleActivePageDetection } from './detection'
1314
import { buildEffectivePageRules, loadDetectorSettings, loadTechRules } from './detector-settings'
1415

@@ -631,6 +632,13 @@ const processQueuedDynamicSnapshot = async tabId => {
631632
return
632633
}
633634

635+
const tab = await getTabSnapshot(tabId)
636+
if (!isDetectablePageUrl(tab.url)) {
637+
await clearTabSession(tabId)
638+
clearBadge(tabId)
639+
return
640+
}
641+
634642
const [data, rules, settings] = await Promise.all([getTabData(tabId), loadTechRules(), loadDetectorSettings()])
635643
data.dynamic = normalizeDynamicSnapshot(snapshot, buildEffectivePageRules(rules.page || {}, settings), data.dynamic)
636644
data.updatedAt = Date.now()

src/background/index.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { clearBadge, clearTabSession } from './tab-store'
33
import { clearDynamicSnapshotTimer, clearPendingDynamicSnapshot } from './dynamic-snapshot'
44
import { buildHeaderRecord, dedupeApiRecords } from './headers'
55
import { clearActiveDetectionTimer, refreshAllBadges, saveTabDataAndBadge, scheduleActivePageDetection } from './detection'
6-
import { getTabData } from './tab-store'
6+
import { getTabData, getTabSnapshot } from './tab-store'
77
import { SETTINGS_STORAGE_KEY, applyDetectorSettingsUpdate, loadDetectorSettings, loadTechRules } from './detector-settings'
88
import { registerMessageRouter } from './message-router'
9+
import { isDetectablePageUrl, isObservableRequestUrl } from '@/utils/page-support'
910

1011
registerMessageRouter()
1112

@@ -24,7 +25,21 @@ chrome.tabs.onRemoved.addListener(tabId => {
2425
clearTabSession(tabId)
2526
})
2627

27-
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
28+
const clearTabDetectionState = (tabId: number) => {
29+
clearActiveDetectionTimer(tabId)
30+
clearDynamicSnapshotTimer(tabId)
31+
clearPendingDynamicSnapshot(tabId)
32+
clearBadge(tabId)
33+
clearTabSession(tabId).catch(() => {})
34+
}
35+
36+
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
37+
const url = changeInfo.url || tab.url || ''
38+
if (url && !isDetectablePageUrl(url)) {
39+
clearTabDetectionState(tabId)
40+
return
41+
}
42+
2843
if (changeInfo.status === 'loading') {
2944
clearActiveDetectionTimer(tabId)
3045
clearDynamicSnapshotTimer(tabId)
@@ -34,7 +49,11 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
3449
}
3550

3651
if (changeInfo.status === 'complete') {
37-
scheduleActivePageDetection(tabId, 600)
52+
if (isDetectablePageUrl(url)) {
53+
scheduleActivePageDetection(tabId, 600)
54+
} else {
55+
clearTabDetectionState(tabId)
56+
}
3857
}
3958
})
4059

@@ -48,17 +67,26 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
4867
chrome.webRequest.onHeadersReceived.addListener(
4968
details => {
5069
if (details.tabId < 0 || !details.responseHeaders) return
70+
if (!isObservableRequestUrl(details.url)) return
5171

52-
Promise.all([getTabData(details.tabId), loadTechRules(), loadDetectorSettings()])
53-
.then(([data, rules, settings]) => {
72+
Promise.all([getTabData(details.tabId), loadTechRules(), loadDetectorSettings(), getTabSnapshot(details.tabId)])
73+
.then(([data, rules, settings, tab]) => {
74+
if (details.type === 'main_frame' && !isDetectablePageUrl(details.url)) {
75+
clearTabDetectionState(details.tabId)
76+
return
77+
}
78+
if (details.type !== 'main_frame' && !isDetectablePageUrl(tab.url)) {
79+
clearTabDetectionState(details.tabId)
80+
return
81+
}
5482
const record = buildHeaderRecord(details, rules.headers || {}, settings)
5583
if (details.type === 'main_frame') {
5684
data.main = record
5785
data.apis = []
5886
data.frames = []
5987
delete data.page
6088
delete data.dynamic
61-
} else if (details.type === 'xmlhttprequest' || (details.type as string) === 'fetch') {
89+
} else if (details.type === 'xmlhttprequest' || (details.type as string) === 'fetch' || details.type === 'websocket') {
6290
data.apis = dedupeApiRecords([record, ...(data.apis || [])])
6391
} else if (details.type === 'sub_frame') {
6492
data.frames = dedupeApiRecords([record, ...(data.frames || [])]).slice(0, 10)
@@ -68,6 +96,6 @@ chrome.webRequest.onHeadersReceived.addListener(
6896
})
6997
.catch(() => {})
7098
},
71-
{ urls: ['<all_urls>'] },
99+
{ urls: ['http://*/*', 'https://*/*', 'ws://*/*', 'wss://*/*'] },
72100
['responseHeaders', 'extraHeaders']
73101
)

src/background/message-router.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import { getTechnologyUrl } from './tech-links'
22
import { augmentPageWithWordPressThemeStyles, detectWordPressThemeStylesFromPage } from './wordpress'
3-
import { getTabData, getTabSnapshot } from './tab-store'
3+
import { clearBadge, clearTabSession, getTabData, getTabSnapshot } from './tab-store'
44
import { queueDynamicSnapshot } from './dynamic-snapshot'
55
import { addStoredCustomHeaderRules } from './headers'
66
import { buildPopupRawResult, cleanPageDetectionRecord, cleanTechnologyRecords, getPopupResultResponse } from './popup-cache'
77
import { runActivePageDetection, saveTabDataAndBadge } from './detection'
88
import { loadDetectorSettings } from './detector-settings'
9+
import { checkPageSupport, isDetectablePageUrl } from '@/utils/page-support'
10+
11+
const clearUnsupportedTab = async (tabId: number) => {
12+
await clearTabSession(tabId)
13+
clearBadge(tabId)
14+
}
915

1016
export const registerMessageRouter = () => {
1117
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
@@ -36,8 +42,16 @@ export const registerMessageRouter = () => {
3642
sendResponse({ ok: false, error: '缺少有效 tabId' })
3743
return false
3844
}
39-
Promise.all([getTabData(tabId), loadDetectorSettings(), getTabSnapshot(tabId)])
40-
.then(([data, settings, tab]) => buildPopupRawResult(addStoredCustomHeaderRules(data, settings), settings, tab))
45+
getTabSnapshot(tabId)
46+
.then(async tab => {
47+
const support = checkPageSupport(tab.url)
48+
if (!support.supported) {
49+
await clearUnsupportedTab(tabId)
50+
throw new Error(support.reason)
51+
}
52+
const [data, settings] = await Promise.all([getTabData(tabId), loadDetectorSettings()])
53+
return buildPopupRawResult(addStoredCustomHeaderRules(data, settings), settings, tab)
54+
})
4155
.then(data => sendResponse({ ok: true, data }))
4256
.catch(error => sendResponse({ ok: false, error: String(error) }))
4357
return true
@@ -58,7 +72,14 @@ export const registerMessageRouter = () => {
5872
return false
5973
}
6074
sendResponse({ ok: true })
61-
runActivePageDetection(tabId).catch(() => {})
75+
getTabSnapshot(tabId)
76+
.then(tab => {
77+
if (!isDetectablePageUrl(tab.url)) {
78+
return clearUnsupportedTab(tabId)
79+
}
80+
return runActivePageDetection(tabId)
81+
})
82+
.catch(() => {})
6283
return false
6384
}
6485

@@ -75,9 +96,17 @@ export const registerMessageRouter = () => {
7596
sendResponse({ ok: false, error: '缺少有效 tabId' })
7697
return false
7798
}
78-
queueDynamicSnapshot(tabId, message.snapshot)
79-
sendResponse({ ok: true })
80-
return false
99+
getTabSnapshot(tabId)
100+
.then(tab => {
101+
if (!isDetectablePageUrl(tab.url)) {
102+
return clearUnsupportedTab(tabId).then(() => false)
103+
}
104+
queueDynamicSnapshot(tabId, message.snapshot)
105+
return true
106+
})
107+
.then(queued => sendResponse({ ok: true, queued }))
108+
.catch(error => sendResponse({ ok: false, error: String(error) }))
109+
return true
81110
}
82111

83112
if (message.type === 'PAGE_DETECTION_RESULT') {
@@ -86,8 +115,12 @@ export const registerMessageRouter = () => {
86115
sendResponse({ ok: false, error: '缺少有效 tabId' })
87116
return false
88117
}
89-
Promise.all([getTabData(tabId), loadDetectorSettings()])
90-
.then(async ([data, settings]) => {
118+
Promise.all([getTabData(tabId), loadDetectorSettings(), getTabSnapshot(tabId)])
119+
.then(async ([data, settings, tab]) => {
120+
if (!isDetectablePageUrl(tab.url)) {
121+
await clearUnsupportedTab(tabId)
122+
return
123+
}
91124
const page = await augmentPageWithWordPressThemeStyles(message.page)
92125
data.page = cleanPageDetectionRecord(page)
93126
data.updatedAt = Date.now()

0 commit comments

Comments
 (0)