下一章 上一章 目录 设置
18、红线相似度 代码风格比 ...
-
`style_patch.py`只有一百八十七行。
如果只看文件长度,它不该让信息办专门腾出一间隔离机房。可这份脚本同时连接了三年前的第十七步、昨天PRES-17里的旧二维码和当前匿名照片的压缩参数。它像一根从旧仓库伸到现在的细线,任何人只要用力一拉,都可能把线的另一端误认成一个具体的人。
贺承远在门口发给每个人一张分析规则。
第一,不看作者字段。
第二,先记录代码特征,再查看提交账户。
第三,不用单一习惯识别个人。
第四,参与比对的人必须提交自己的相关代码,防止只拿“别人”当样本。
沈砺川读到第四条,问:“我也交?”
“尤其是你。”贺承远说,“你用过学院旧工具模板,知道得越多,越要把自己的影响放进分析。”
沈砺川没有开玩笑。他把新生程序赛、日志解析和最近写的设备检查脚本各复制一份,删除姓名后交给陈以宁随机编号。许砚知也提交了自己的数据核验代码,虽然他写得少,仍不能把自己放在比较范围之外。
林疏月负责把样本分组。A组是三年前媒体仓库的七次正式提交,B组是未记录补丁包,C组是当前PRES-17恢复出的执行缓存,D组是材料组成员与当年相关人员自愿提供的匿名样本。所有文件先统一换行符、去除时间和路径,避免分析者从无关信息猜人。
沈砺川坐到终端前,没有立刻打开缺失补丁。他先写下要看的特征:命名方式、异常处理、路径拼接、日志格式、空值处理、注释语言、函数拆分、外部依赖。
“缩进呢?”林疏月问。
“格式化工具能统一,证据很弱。”
“变量名?”
“比缩进强,但模板也会带变量名。”
“错别字?”
“可以说明复制来源,不能直接说明作者。”
许砚知把这三条放在分析表最上面。代码风格不是指纹,更像一组会被教学、协作和工具共同塑造的习惯。两个人写得像,可能因为互相抄过,也可能因为都从同一份示例开始。
正式仓库的第一版很规整。函数名全用完整英文,错误直接抛出,日志只记录“成功”或“失败”。第三次提交开始出现短变量和中文注释,说明维护者换过。第五次提交增加自动恢复,把所有异常都捕获后写成`continue_show`,只要页面还能播放,就不阻断流程。
缺失补丁建立在第五版之上,却改了三处关键逻辑。
第一,它把`appeal_success_demo`替换成`public_success_loop`。
第二,它把恢复失败从红色告警降成灰色提示。
第三,它新增一个函数:`make_it_same()`。
函数名很随意,作用却很具体:把不同来源的视频、图片和文稿转换成相同分辨率、相同色彩配置和相同时间戳格式。昨天匿名帖里的A401照片,正好带有这一函数生成的色彩标签。
“同一个函数?”林疏月问。
“同一种输出。”沈砺川说,“源码不一定相同。只要参数一致,别的程序也能做出来。”
他没有因为线索可能指向当前匿名帖就把判断说满。
缺失补丁的异常处理最特别。正常代码遇到文件不存在会停止,补丁却先创建一个同名空文件,再继续运行。日志里不写“缺失”,而写“aligned”。这能让展示流程看起来完整,也会让后来的人误以为空文件曾经正常存在。
handover目录中的`20_42_transfer_note.docx`正是零字节。
许砚知把这两个事实并列,没有画等号。要证明补丁生成过那份文件,必须找到执行日志、文件系统时间或相同随机标记。
管理员从旧磁盘未分配区恢复出一段临时日志。二十点四十二分零三秒,`make_it_same()`处理过一个路径为`handover/20_42_transfer_note.docx`的目标;源文件读取失败,程序创建空文件并标记aligned。
这不是人手工留下的空白交接表。至少零字节文件由补丁自动生成。
纸质交接缺少附件、电子目录又出现空文件,过去的调查把两者当成“附件被删除”。现在更准确的说法是:系统曾试图对齐一个不存在或无法读取的附件,并用空文件填补位置。真实附件是否存在,仍然未知。
“谁写了`make_it_same()`?”宣传老师问。
陈以宁打开盲比结果。函数的命名和短注释与B组另外两份代码相似,异常处理则来自学院早期演示模板。四个当年账户都提交过相关片段,其中一个共享账户在补丁出现前复制过整段函数框架。
“能排除谁?”
“暂时不能。”
“那代码风格还有什么用?”
沈砺川把正式仓库的七个版本排在屏幕上:“用来找传播路径。谁把哪段代码带进来,哪次提交从哪里复制,哪些行为属于工具默认,而不是个人临时决定。”
他点开第三版的课程模板来源。`make_it_same()`的前身叫`normalize_demo_assets()`,来自两年前的一门实践课。任课助教要求所有小组使用同一份骨架。后来进入寒灯项目的人中,有六名学生上过这门课,周闻、赵嵩和唐曼都在其中。
一段相似代码把嫌疑范围从一个人扩大到一整个训练来源。
许砚知问:“当前学院还有人用这份模板吗?”
管理员检索公开课程仓库,发现模板已经更新过四次,但旧版仍被许多学生保存在个人项目中。沈砺川的新生程序赛脚本就调用了同一个路径清理函数。
盲比系统给出的最高相似样本是D-04。
D-04属于沈砺川。
房间里没人说话。沈砺川自己点开比对详情。相似部分不是`make_it_same()`,而是错误日志的短句、路径规范化和一个拼错的变量名`ouput_dir`。这个错字来自旧课程模板,沈砺川一直没改。
“如果现在直接看相似度,我最像。”他说。
林疏月立刻说:“可你三年前根本不在学校。”
“所以这个结果正好证明风格不能单独识别人。”沈砺川把自己的样本编号写回报告,“也证明当前有人如果拿相似度做文章,我会第一个被套进去。”
许砚知问管理员,盲比原始结果是否会自动上传。管理员检查设置,发现分析工具默认向学院代码教学平台提交匿名统计,目的是改进抄袭检测。这个功能在隔离机房不该启用,却因为使用了课程版配置而保持打开。
上传队列里已有一条任务,包含D-04与缺失补丁的高相似提示。
管理员立即断网、冻结队列。任务尚未成功发送,但本地教学平台缓存已经生成预览页面,标题是“疑似同源代码”。如果有人能访问缓存,就可能把沈砺川与旧补丁直接绑在一起。
“谁有访问权限?”许砚知问。
“课程管理员、助教,还有当前小测系统的自动审查模块。”
第二天正好有程序设计课堂小测。
贺承远要求管理员保留上传队列,不能因为结果误导就删除。材料组同时写一份上下文说明:D-04作者三年前未入学;相似片段来自公开教学模板;该结果只能证明同源训练材料,不能证明旧补丁作者。
沈砺川问:“要不要提前跟老师说我是谁?”
许砚知没有替他决定:“你担心什么?”
“明天小测如果触发审查,老师会先看到‘高相似’。我不想等教室里弹出来再解释。”
“那就由你申请主动说明。”
“你陪我去?”
“材料组需要两人核对记录。我可以去,但你自己说。”
“行。”
他们去找程序设计课老师。沈砺川从提交样本开始讲,没有省略自己使用旧模板,也没有先强调“不可能是我”。他说清时间、来源和盲比局限,最后请求老师在小测前关闭旧案样本与课堂作业的自动交叉比对,只保留正常题目查重。
老师没有马上同意:“关闭一项审查,会不会让你获得特殊待遇?”
许砚知本能地想解释这是隔离调查污染了教学系统,又在开口前停住。
沈砺川说:“那就不只对我关闭。旧案样本全部从教学查重库隔离,适用于全班。我的小测按普通规则检查。如果还是命中公开模板,我接受复核。”
老师看了很久,最终让信息办出具正式隔离说明,并把全班查重配置恢复到课程原始范围。沈砺川没有得到豁免,只避免了一个不该进入课堂的旧案样本。
走出办公室,许砚知说:“你刚才没拿三年前不在学校当第一理由。”
“因为那只能保护我。”沈砺川把门轻轻带上,“样本混库的问题不改,下一个人还是会被相似度先定性。”
许砚知把这句话记进调查报告的制度建议。他没有加任何评价。
下午,四人继续追补丁传播路径。B组补丁中有一段注释:“页面难看没关系,先让所有人看到同一个结果。”这句话在正式仓库、课程模板和沈砺川代码里都没有出现。它可能是补丁作者的临时说明,也可能从需求会议复制。
林疏月在三年前的会议纪要里找到相近表述。宣传组曾提出“各屏内容必须一致,避免观众看到未完成状态”。陆行简在旁边批注:“一致不等于正确,未完成可以明确显示。”
最终补丁选择了前一种方向。
风格分析再次把个人习惯推回制度选择:代码为什么捕获所有异常,不只是某个人喜欢省事,还因为展示目标要求“任何屏幕都不要停”。
“如果需求本身有问题,写代码的人就没责任了吗?”林疏月问。
“不是。”许砚知说,“要分别看谁提出、谁实现、谁审核、谁知道风险后仍然发布。”
陈以宁给责任表增加四列。过去那种一句“技术组负责”的结论,被拆成可以继续核验的动作。
为了判断匿名照片的色彩标签到底有多特殊,材料组做了一次盲测。管理员从公开图片中随机选取二十张,分别交给旧课程模板、缺失补丁、当前新版插件和一个临时重写的小程序处理。处理者只拿到参数,不知道哪张对应哪个工具。
四组输出中,有三组都能生成与匿名照片相同的色彩标签。只要选择相同的旧版编码库和默认色域,结果就会一致。真正有区分度的不是标签,而是文件尾部多出的十六字节填充。缺失补丁会写入固定顺序,新版插件会随机排列,临时程序则根本不写。
匿名照片的填充顺序与缺失补丁相同。
“这次能说同一个工具了吗?”林疏月问。
管理员仍然谨慎:“能说与该补丁或其编译副本一致。不能说同一台设备,也不能说同一操作者。”
他们又检查PRES-17恢复出的缓存。设备里确实存在缺失补丁的编译副本,但昨天的执行历史没有调用记录。二维码截图只会引导下载,无法证明设备真正运行过脚本。有人可能用PRES-17查看入口,再在另一台设备上执行;也可能只是故意留下入口,诱导他们把当前照片归给PRES-17。
沈砺川建议在学院公开仓库范围内搜索十六字节填充实现。搜索结果返回六十三份代码副本,分布在七届学生作业和多个活动项目中。大部分人不知道这段填充的用途,只是复制了能跑通的函数。
“六十三个。”林疏月靠在椅背上,“所以如果我们发一句‘匿名照片用了寒灯补丁’,至少六十三个人都可能被当成嫌疑人。”
“还不止。”陈以宁说,“代码可能继续被转发到私人仓库。”
许砚知把公开说明中的“使用旧项目专属工具”改成“使用源自旧教学模板的一类兼容工具”。措辞变得不够抓人,却更准确。
沈砺川看见后问:“公开说明要写我的盲比结果吗?”
“你愿意公开到什么程度?”
“写有一名当前成员因使用同源模板得到最高相似度,时间线证明相似度不能识别人。不写名字。”
“别人可能猜到。”
“那也比把这个例子藏起来强。”
许砚知没有以“保护搭档”为由替他删掉。两人把可能后果列在纸上:班级议论、课堂复核、论坛截取、后续调查中的偏见。沈砺川逐条看完,仍然选择保留匿名案例,但要求任何对外版本同时附上模板传播范围和时间线。
“我不想把自己当反例用完以后,再让别人只记住红线。”他说。
“那就把撤回条件也写进去。”许砚知在说明末尾增加:若无提交时间、运行记录与设备证据,风格相似度不得进入人员责任判断。
他们把这一条称为“相似度否决条件”。它不仅保护沈砺川,也约束材料组以后面对任何人。
下午五点,代码教学平台的管理员远程展示查重算法。算法把变量名、抽象语法树和日志短句一起计算,却没有把公共模板从相似度中扣除。`ouput_dir`这个错字在六十三份代码里出现了四十一份,每一份都会互相抬高分数。
任课老师看完后决定暂停旧版算法,把公共模板建立白名单。她没有撤销对沈砺川的说明要求,因为教学系统已经生成正式通知,撤销也需要留下理由。
“明天你还是来。”老师说,“但不是接受质问。你把今天的盲比过程讲清楚,让全班知道查重分数怎么会误导。”
沈砺川问:“会不会把旧案带进课堂?”
“只讲匿名代码,不讲A401和人员。课堂需要处理的是方法错误。”
这让一次可能的公开归责,变成一场可被所有人检验的方法复盘。代价仍然存在:沈砺川要站到全班面前,承认自己的代码确实与危险补丁高度相似,也要承受别人不知道全部背景时的目光。
回A401的路上,他比平常安静。许砚知没有问“你是不是紧张”,只把明天的展示顺序发给他:先公开模板,再展示盲样,再解释时间线,最后说明哪些结论不能推出。
沈砺川看完:“你不替我讲?”
“你刚才说过,你自己说明。”
“我以为你至少讲方法部分。”
“如果你需要,可以。”
沈砺川把手机揣回口袋:“不用。我自己来。你坐下面挑错。”
“可以。”
“别挑得太狠。”
“看证据。”
他终于笑了一声。那点笑意很短,却让两个人都知道,明天的课堂不是谁替谁挡住,而是一个人站上去,另一个人保证他讲出的每一步不会被随意改写。
傍晚,管理员恢复出缺失补丁的编译缓存。缓存里有一个没有出现在源码中的调试分支:当`appeal_queue`恢复失败时,程序会把失败项写入`appeal_shadow`,等待活动结束后人工回填。
这个分支证明,补丁作者至少预见过恢复失败,也为异议保留了影子记录。可正式运行包里,`appeal_shadow`被关闭了。
关闭它的不是缺失补丁,而是发布前最后一次打包参数。
参数文件由SLC共享维护账号提交。
沈砺川盯着SLC三个字母,脸上的神情一点点收紧。这个账号与他的姓名缩写相似,此前的核验已经证明它是共享账户;如今它又出现在最关键的关闭动作上。
管理员还没来得及打开提交详情,程序设计课平台发来一封自动通知。
尽管旧案样本已被隔离,昨晚生成的缓存预览仍被小测系统提前索引。通知要求沈砺川在明天第一节课前到场说明“疑似同源代码”。
邮件抄送了任课老师、学院教务和他所在班级的助教。
沈砺川把准备好的说明又读了一遍。第一版里有一句:“我与三年前补丁没有任何关系。”许砚知看完,直接在旁边标出问题。
“时间线能证明你当时不在学校,不能证明这份代码后来没有经过你使用的仓库传播到现在。”
“我确实没传播。”
“那要用提交记录证明,不能用一句保证。”
沈砺川把那句话删掉,改成:本人未参与三年前提交;当前代码与旧补丁共享公开模板,现有记录未发现D-04向旧仓库提交或执行缺失补丁。
“听起来没那么干净。”他说。
“事实通常没那么干净。”
“但够用?”
“够别人复核。”
沈砺川保存版本,把修改前后的两份都放进说明附件。第二天如果有人问他为什么改口,他不需要假装第一版从未存在。
许砚知又检查了一遍附件权限,确认同学只能看到匿名样本和算法过程,看不到旧案文件名。沈砺川则把自己的课堂代码仓库设为只读快照,防止说明前后文件继续变化。两个人各自守住一端:一个防止材料泄漏,一个防止版本被事后改写。
准备结束时已经接近熄灯。沈砺川没有要求许砚知陪他继续练,也没有用玩笑把紧张抹掉。他只说:“明天我如果漏了哪一步,你当场举手。”
“会打断你。”
“比下课后才说强。”
许砚知点头,把展示用的计时器交给他。那不是安慰,也不是替他承担,只是一项双方都同意的校验。
通知正文末尾附着一张相似度图,最高的红线从`style_patch.py`直连D-04。那条线看起来像结论,实际上只是一道尚未完成解释的题。
红线会被所有人先看见,解释必须由沈砺川自己一层层补完。许砚知能做的,是保证这些步骤不会被截掉,而不是替他跨过去。