fix: 第12轮搜索审计修复 — _normalize_field_label 崩溃/keyset NULLSLAT/共享列表变异等21项修复

P0 (3): _normalize_field_label 函数缺失导致 (a OR b)[TI] 崩溃;
      共享列表变异污染 result.groups;POST /search/advanced 缺少用户认证
P1 (8): has_not 忽略括号内 NOT;紧凑日期 YYYYMMDD 未归一化;
      日期字段非日期文本 SQL 错误;keyset NULLSLAT ~50% 空值行跳过;
      first_author 非 dict JSON 崩溃;MeSH 无匹配静默丢弃;
      相关性排序含否定词;词数检查在 PubMed 清洗之前;
      普通搜索缺错误处理;#N 引号感知不完整
P2 (6): 月越界退化;MH/MAJR FALSE 语义;field 白名单缺字段;
      retracted/negative_result/tag_ids 验证器;resolveQuery 重复调用;
      SearchRequestBody.page 可选;restoreFromQuery showCustomYear
This commit is contained in:
34047007@qq.com
2026-07-28 12:38:17 +08:00
parent 89e154caa2
commit 6f861c8543
9 changed files with 324 additions and 88 deletions
+9 -4
View File
@@ -26,9 +26,11 @@ function saveAll(entries: HistoryEntry[]) {
/** 把 #N 引用替换为 expanded_query(加括号保护优先级) */
export function resolveQuery(query: string, entries: HistoryEntry[]): string {
return query.replace(/#(\d+)/g, (_m, num) => {
// P12: skip quoted #N, only expand unquoted references
return query.replace(/"[^"]*"|'[^']*'|#(\d+)/g, (m, num) => {
if (num === undefined) return m // inside quotes, literal
const found = entries.find(e => e.id === `#${num}`)
return found ? `(${found.expanded_query})` : _m
return found ? `(${found.expanded_query})` : m
})
}
@@ -39,8 +41,11 @@ export function expandQuery(query: string, entries: HistoryEntry[]): string {
let current = query
for (let i = 0; i < 10; i++) {
if (current === prev) break
const refs = current.match(/#(\d+)/g)
if (refs) {
// P12: only count #N outside quotes for cycle detection
const refs = [...current.matchAll(/"[^"]*"|'[^']*'|#(\d+)/g)]
.filter(m => m[1] !== undefined)
.map(m => `#${m[1]}`)
if (refs.length) {
const uniqueRefs = [...new Set(refs)]
for (const ref of uniqueRefs) {
if (seen.has(ref)) return prev // 循环引用 → 返回上次安全结果
+1 -1
View File
@@ -330,7 +330,7 @@ export interface SearchRequestBody {
field?: string
boolean?: string // "and" | "or"
exact_phrase?: boolean
page: number
page?: number
page_size: number
sort?: string
cursor_val?: string
+8 -2
View File
@@ -397,8 +397,14 @@ function restoreFromQuery() {
if (dt.length >= 4) yearToStr.value = dt.slice(0, 4)
}
} else {
if (route.query.year_from) yearFromStr.value = String(route.query.year_from)
if (route.query.year_to) yearToStr.value = String(route.query.year_to)
if (route.query.year_from) {
yearFromStr.value = String(route.query.year_from)
showCustomYear.value = true
}
if (route.query.year_to) {
yearToStr.value = String(route.query.year_to)
showCustomYear.value = true
}
}
if (route.query.tag) selectedTags.value = String(route.query.tag).split(',')
if (route.query.tier) selectedTiers.value = String(route.query.tier).split(',')
@@ -226,8 +226,11 @@ function resolveQuery(q: string): string {
let current = q
for (let i = 0; i < 10; i++) {
if (current === prev) break
const refs = current.match(/#\d+/g)
if (refs) {
// P12: only count #N outside quotes for cycle detection (quoted #N are literals)
const refs = [...current.matchAll(/"[^"]*"|'[^']*'|#(\d+)/g)]
.filter(m => m[1] !== undefined)
.map(m => `#${m[1]}`)
if (refs.length) {
const uniqueRefs = [...new Set(refs)]
for (const ref of uniqueRefs) {
if (seen.has(ref)) return prev
@@ -304,9 +307,9 @@ function validateQuery(q: string): { valid: boolean; query: string; error?: stri
}
// 校验括号匹配(在展开后的查询上检查,确保历史引用展开后也平衡)
const expandedForCheck = resolveQuery(q)
const expanded = resolveQuery(q)
let depth = 0
for (const ch of expandedForCheck) {
for (const ch of expanded) {
if (ch === '(') depth++
if (ch === ')') depth--
if (depth < 0) {
@@ -317,7 +320,7 @@ function validateQuery(q: string): { valid: boolean; query: string; error?: stri
return { valid: false, query: q, error: '括号不匹配:缺少右括号(结合历史查询展开后)' }
}
return { valid: true, query: resolveQuery(q) }
return { valid: true, query: expanded }
}
const loading = ref(false)