Skip to content

Commit a4fed80

Browse files
committed
fix: 修一批响应头与规则误报
CSP / Permissions-Policy / Report-To / NEL 这种「允许列表」性质的响应头从 headerBlob 里剔除——这些只是声明可加载源/上报端点,不代表实际使用,之前会导致 x.com 因 CSP 包含 googleusercontent.com 而误报 Google Cloud CDN。同时收紧多个规则匹配: - 前端库 / UI 框架同名重复时(如 t.me 上 Bootstrap 同时进 UI / CSS 框架和前端库),自动去掉前端库的重复条目 - Framework7 的 patterns 去掉 `f7-`(裸字符串,Twitter 等站点正文里出现就误命中),仅保留 framework7 资源文件名和 classPrefixes - Play Framework 把 `play\.api`、`csrfToken.*play` 这种过宽模式收紧为 play.api.mvc / playframework.com / play_session 等精确特征 - 注入脚本 detectFeeds 过滤增加 oembed 排除,并要求 type/href 命中真正的 feed MIME / 路径,避免 application/json+oembed 被误识别成 JSON Feed - 兜底前端库黑名单加 tgwallpaper(Telegram 自家壁纸脚本,不是公共库) - 新增 Telegram CDN 规则(telesco.pe / cdn*.telesco.pe)+ 把 t.me 和 telesco 加入 self-host 抑制 将版本号提升到 1.3.43。
1 parent 22e7476 commit a4fed80

11 files changed

Lines changed: 65 additions & 23 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.3.42",
4+
"version": "1.3.43",
55
"type": "module",
66
"description": "StackPrism 用于检测网页前端、后端、CDN、SaaS、广告营销、统计、登录、支付、网站程序和主题模板线索。",
77
"scripts": {

public/rules/page/backend-hints-page.json

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,7 @@
6565
{
6666
"name": "ThinkPHP",
6767
"confidence": "",
68-
"patterns": [
69-
"thinkphp",
70-
"index\\.php\\?s=",
71-
"(?:^|/)index/(?:index|home|pay|user|api)/[a-z0-9_-]+(?:[?#\\s\"'<>]|$)"
72-
]
68+
"patterns": ["thinkphp", "index\\.php\\?s=", "(?:^|/)index/(?:index|home|pay|user|api)/[a-z0-9_-]+(?:[?#\\s\"'<>]|$)"]
7369
},
7470
{
7571
"name": "CakePHP",
@@ -107,11 +103,7 @@
107103
"name": "FreeMarker",
108104
"confidence": "",
109105
"matchIn": ["url", "resources", "html", "dynamic"],
110-
"patterns": [
111-
"freemarker",
112-
"\\.ftl(?:[?#\\s\"'<>]|$)",
113-
"(?:^|/)ftl(?:/|[?#\\s\"'<>]|$)"
114-
]
106+
"patterns": ["freemarker", "\\.ftl(?:[?#\\s\"'<>]|$)", "(?:^|/)ftl(?:/|[?#\\s\"'<>]|$)"]
115107
},
116108
{
117109
"name": "Apache Wicket",
@@ -137,7 +129,7 @@
137129
},
138130
{
139131
"name": "Play Framework",
140-
"patterns": ["play-framework|play\\.api|csrfToken.*play"]
132+
"patterns": ["play-framework", "play\\.api\\.mvc", "playframework\\.com", "play_session"]
141133
}
142134
]
143135
},

public/rules/page/cdn-providers-page.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@
314314
"name": "GitHub Raw Content",
315315
"patterns": ["githubusercontent\\.com"]
316316
},
317+
{
318+
"name": "Telegram CDN",
319+
"patterns": ["telesco\\.pe", "cdn[0-9]*\\.telesco\\.pe"]
320+
},
317321
{
318322
"name": "GitLab Pages",
319323
"patterns": ["gitlab\\.io"]

public/rules/page/frontend-extra.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@
127127
},
128128
{
129129
"name": "Fluent UI / Fabric",
130-
"patterns": ["@fluentui|office-ui-fabric|ms-Fabric|ms-Button"],
131-
"classPrefixes": ["ms-"]
130+
"patterns": ["@fluentui|office-ui-fabric|ms-Fabric|ms-Button"]
132131
},
133132
{
134133
"name": "Blueprint",
@@ -212,7 +211,7 @@
212211
},
213212
{
214213
"name": "Framework7",
215-
"patterns": ["framework7(?:\\.bundle)?(?:\\.min)?\\.(?:js|css)|f7-"],
214+
"patterns": ["framework7(?:\\.bundle)?(?:\\.min)?\\.(?:js|css)"],
216215
"globals": ["Framework7"],
217216
"classPrefixes": ["f7-"]
218217
},
@@ -240,7 +239,7 @@
240239
},
241240
{
242241
"name": "Varlet",
243-
"patterns": ["varlet|@varlet\\/|var-"]
242+
"patterns": ["varlet|@varlet\\/|varletjs"]
244243
},
245244
{
246245
"name": "Semi Design",

public/rules/page/selfhosted-saas-assets.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,8 @@
370370
{
371371
"name": "Outline Wiki",
372372
"kind": "知识库 / 文档",
373-
"resourceHints": ["outline"],
374-
"patterns": ["outline[^\\s\"'<>]*\\.(?:js|css|svg|png)(?:\\?|$)"]
373+
"resourceHints": ["getoutline", "outline-wiki", "outlinewiki", "/outline/"],
374+
"patterns": ["getoutline\\.com", "(?:^|/)outline(?:-wiki|wiki)?[._-][^\\s\"'<>]*\\.(?:js|css)", "/outline/"]
375375
},
376376
{
377377
"name": "Wiki.js",

public/rules/self-host-suppress.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"reddit.com": ["Reddit"],
2727
"discord.com": ["Discord"],
2828
"slack.com": ["Slack"],
29-
"telegram.org": ["Telegram"],
29+
"telegram.org": ["Telegram", "Telegram CDN"],
30+
"t.me": ["Telegram", "Telegram CDN"],
3031
"whatsapp.com": ["WhatsApp"],
3132
"medium.com": ["Medium"],
3233
"substack.com": ["Substack"],

public/tech-links.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,7 @@
13451345
"GitHub Pages": "https://pages.github.com",
13461346
"GitHub Assets": "https://github.com",
13471347
"GitHub Raw Content": "https://raw.githubusercontent.com",
1348+
"Telegram CDN": "https://telegram.org",
13481349
"GitLab Pages": "https://docs.gitlab.com/user/project/pages",
13491350
"Render": "https://render.com",
13501351
"Fly.io": "https://fly.io",

src/background/dynamic-snapshot.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ const isLikelyDynamicLibraryFileName = name => {
150150
'require',
151151
'requirejs',
152152
'system',
153-
'systemjs'
153+
'systemjs',
154+
// 站点自身的内部脚本,不是公共库
155+
'tgwallpaper'
154156
])
155157
return !genericNames.has(name.toLowerCase())
156158
}

src/background/headers.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,27 @@ const markSpoofedHeaderDetections = (technologies: any[], headers: Record<string
142142
}
143143
}
144144

145+
// 这些 header 是「允许列表」性质(CSP 列出可加载的源、Report-To 列出错误上报通道等),
146+
// 内容是一长串第三方域名 / 类型字面量,不代表站点实际在用这些技术;扫描时跳过它们能避免误报
147+
const ALLOWLIST_STYLE_HEADERS = new Set([
148+
'content-security-policy',
149+
'content-security-policy-report-only',
150+
'report-to',
151+
'reporting-endpoints',
152+
'permissions-policy',
153+
'feature-policy',
154+
'expect-ct',
155+
'nel'
156+
])
157+
145158
const detectFromHeaders = (headers: Record<string, string>, url: string, headerRules: any = {}, settings: any = {}) => {
146159
const technologies: any[] = []
147160
const add = createCollector(technologies, '响应头')
148161
const server = lower(headers.server)
149162
const poweredBy = lower(headers['x-powered-by'])
150163
const headerBlob =
151164
Object.entries(headers)
165+
.filter(([name]) => !ALLOWLIST_STYLE_HEADERS.has(name.toLowerCase()))
152166
.map(([name, value]) => `${name}: ${value}`)
153167
.join('\n') + `\nurl: ${url || ''}`
154168
const lowerHeaderBlob = lower(headerBlob)

src/background/popup-cache.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ export const suppressGenericCdnFallbacks = (technologies: any[]) => {
137137
return technologies.filter(tech => tech?.category !== 'CDN / 托管' || !GENERIC_CDN_FALLBACK_NAMES.has(tech?.name))
138138
}
139139

140+
// 同名技术既被识别为 UI / CSS 框架(或前端框架),又被归到「前端库」类目时,去掉后者的重复条目
141+
// 例:Bootstrap 同时进了「UI / CSS 框架」和「前端库」,只保留前者
142+
const SPECIFIC_CATEGORIES_OVER_GENERIC_LIB = new Set(['UI / CSS 框架', '前端框架', '构建与运行时'])
143+
export const suppressGenericFrontendLibDuplicates = (technologies: any[]) => {
144+
if (!Array.isArray(technologies) || !technologies.length) return technologies
145+
const specificNames = new Set<string>()
146+
for (const tech of technologies) {
147+
if (tech?.category && SPECIFIC_CATEGORIES_OVER_GENERIC_LIB.has(tech.category) && tech.name) {
148+
specificNames.add(normalizeTechName(tech.name))
149+
}
150+
}
151+
if (!specificNames.size) return technologies
152+
return technologies.filter(tech => tech?.category !== '前端库' || !specificNames.has(normalizeTechName(tech.name)))
153+
}
154+
140155
export const filterTechnologiesBySettings = (technologies: any[], settings: any) => {
141156
const disabledCategories = new Set(cleanStringArray(settings?.disabledCategories))
142157
const disabledTechnologies = new Set(cleanStringArray(settings?.disabledTechnologies).map(name => normalizeTechName(name)))
@@ -290,7 +305,11 @@ const buildDisplayTechnologies = (data: any, settings: any, suppressMap: Record<
290305
)
291306
const pageUrl = data.page?.url || data.dynamic?.url || data.main?.url || ''
292307
return filterTechnologiesBySettings(
293-
suppressSelfHostTechs(suppressGenericCdnFallbacks(mergeDisplayTechnologyRecords(all)), pageUrl, suppressMap),
308+
suppressSelfHostTechs(
309+
suppressGenericFrontendLibDuplicates(suppressGenericCdnFallbacks(mergeDisplayTechnologyRecords(all))),
310+
pageUrl,
311+
suppressMap
312+
),
294313
settings
295314
)
296315
}

0 commit comments

Comments
 (0)