Files
competition-website/app/service/bigQuestion.js
qubiaobiao 37d07907a1 feat(big-question): 录入侧防呆——题干不得含「可直抄的答案」(R27)
背景(2026-09-28 测试人员实测反馈):
  「答题时大题部分有参考答案。」初判怀疑前端未区分 / 评分系统未隔离,
  实测两条均被证伪:
    · 前端 Competition.vue 仅渲染 description / sub_prompts,无答案区块;
    · 后端 buildSnapshot() / getForCandidate() 返回字段无 answer;
    · 建表 DDL 023-024 的 big_questions 无答案列;
    · 真实联调 question 字段 = [big_question_uid, description, score_per_sub,
      snapshot_at, sub_prompts, title, total_score]。
  ⇒ 泄露 100% 来自**题库录入内容**:中职组启用题 id=38 的题干内嵌
    「指令输入示例」(>OFFMSG,COM1*4A 等三条),而第 3 小问正是问这些指令。

为什么守点是录入入口:
  抽题快照 question_snapshot **抽题即固化**(防纠纷设计)⇒ 事后改题库
  **撤不回**已抽考生看到的版本;下发链路本身无泄漏点 ⇒ 只能在录入时拦。

改动:
  · 新增 detectAnswerLeak():扫 title / description / sub_prompt_1..3
      ① 「参考答案 / 标准答案 / 评分标准 / 评分细则 / 答案要点」字样;
      ② 带**字面参数**的指令示例(>CMD,值*两位十六进制校验码)。
  · validateQuestionPayload() 内调用 ⇒ create 与 update **同源**拦截;
    命中返回 code=SUSPECT_ANSWER_LEAK,message 直接教会管理员怎么改。
  · 兼容 > 与裸 > 两种存储形态。

边界(刻意为之,勿收紧):
  格式定义(参数用方括号占位,如 >OFFMSG[,Port[,Message]]*CS)**不拦** ——
  校验码限定「两位十六进制」天然排除占位符 *CS(S 非 hex)。
  向考生提供指令**手册**是允许的;等同于答案的**成品**才不允许。
  —— 与同日数据修复口径一致(只删示例、保留格式定义)。

实测(后端已重启加载新码,5/5 PASS):
  ① 合法格式定义题 → 放行(code=0,新增 1 行)
  ② 含指令示例 → 拒绝 HTTP200 code=400,**未落库**
  ③ 含「参考答案」字样 → 拒绝,未落库
  ④ PUT 更新路径 → 同样拒绝,且原内容未被改写
  ⑤ 清理复原:行数与 MAX(id) 回到基线
2026-09-28 11:58:14 +08:00

30 KiB