文章总结: 该文档对SBTI前端静态项目进行源码分析,项目为单页人格测试页面,包含30道题目和27种人格结果。核心发现包括:结果完全在前端计算,题目顺序随机不影响分数;存在大量模板并列情况但缺乏明确决胜规则;安全审计未发现高危漏洞但指出innerHTML潜在XSS风险。建议增加排序决胜规则、改用安全DOM操作方式,若用于正式业务需将判分逻辑后移。 综合评分: 85 文章分类: 代码审计,WEB安全,安全工具,安全开发
SBTI为什么能霸屏朋友圈?项目分析|附源码
原创
北境 北境
0xArgus
2026年4月10日 13:25 北京
后台回复 SBTI 获取源码。
SBTI 项目源码分析报告
1. 项目概况
本次分析对象为单页静态项目,主文件为 ce.html,配套素材位于 image/ 目录。代码没有构建脚本、依赖清单、后端接口、数据库配置,也没有账号、会话、持久化存储等常见应用层结构。
从代码体量和组织方式看,这个项目属于典型的前端本地计算页面:
✅ 页面结构、样式、题库、人格库、评分逻辑全部写在 ce.html。
✅ 结果图片通过本地路径映射加载,定义在 ce.html#L1256。
✅ 页面分为 intro、test、result 三个区块,定义在 ce.html#L688、ce.html#L702、ce.html#L722。
这类实现部署简单,传播方便,维护压力也会集中到一个文件里。后续只要题目、结果库、显示规则再继续增长,单文件维护会变得吃力。
2. 本地搭建与使用方式
这个项目没有编译步骤,也没有后端依赖,本地使用门槛很低。按现有代码结构,下面三种方式都能跑起来。
2.1 直接打开文件
适用场景:
✅ 临时查看页面效果
✅ 本地快速核对题目和结果图
✅ 不需要联调接口
操作方式:
1进入项目目录。
2直接双击 ce.html。
3浏览器打开后即可开始测试。
说明:
✅ 当前项目使用的是本地相对路径图片,直接打开 HTML 文件通常可以正常显示。
✅ 这种方式最简单,适合单机查看。
2.2 用 Python 启动本地静态服务
适用场景:
✅ 想用 http://localhost 方式访问
✅ 想减少浏览器对 file:// 场景的差异影响
✅ 后续准备加脚本、接口代理或联调工具
在项目目录执行:
powershell● ● ●
python -m http.server 8080
启动后浏览器访问:
text● ● ●
http://localhost:8080/ce.html
如果机器上有多个 Python 版本,也可以用:
powershell● ● ●
py -m http.server 8080
2.3 用编辑器的静态服务插件启动
适用场景:
✅ 日常改页面和调样式
✅ 希望保存后自动刷新
常见做法:
✅ VS Code 安装 Live Server
✅ 在项目根目录右键 ce.html
✅ 选择 Open with Live Server
2.4 本地使用注意事项
✅ 页面没有数据库和本地存储,刷新后当前答题状态会清空。
✅ 图片资源依赖 image/ 目录,移动 ce.html 时需要一起保留素材目录结构。
✅ 当前项目没有构建产物概念,修改 ce.html 后重新刷新浏览器即可看到效果。
3. 页面怎么跑起来
3.1 启动流程
点击“开始测试”后,页面执行 startTest(false),入口事件绑定在 ce.html#L1732,函数定义在 ce.html#L1718。
startTest() 做了五件事:
1重置 app.answers。
2打乱 30 道正式题的顺序。
3把第一道特殊题插入到随机位置。
4调用 renderQuestions() 生成题目 DOM。
5切换到测试页。
这里的随机性只作用在题目出现顺序和特殊题插入位置上,判分过程没有使用随机数。
3.2 数据结构
代码中有四组核心数据:
✅ dimensionMeta:15 个评分维度,位置在 ce.html#L779 附近。
✅ questions:30 道正式题,位置在 ce.html#L795。
✅ specialQuestions:2 道特殊题,位置在 ce.html#L1067。
✅ TYPE_LIBRARY 与 NORMAL_TYPES:人格文案和人格模板,位置在 ce.html#L1092 与 ce.html#L1286。
当前可数出的数据规模如下:
| 项目 | 数量 | | — | — | | 正式题 | 30 | | 特殊题 | 2 | | 评分维度 | 15 | | 常规人格模板 | 25 | | 特殊人格 | 2 | | 结果图片映射 | 27 |
4. 结果是怎么算出来的
4.1 判分主线
核心计算函数是 computeResult(),定义在 ce.html#L1598。
这段逻辑可以拆成四步:
1把 30 道正式题的答案累加到 15 个维度上。
2把每个维度的原始分数压成 L / M / H 三档。
3把 15 维用户向量与 25 个常规人格模板逐一计算距离。
4取排序后的第一名作为常规最佳结果,再判断是否触发特殊覆盖。
4.2 维度分档
分档函数 sumToLevel() 位于 ce.html#L1580 附近,规则很直白:
✅ <= 3 记为 L
✅ = 4 记为 M
✅ >= 5 记为 H
每个维度由两道题组成,每题分值是 1 / 2 / 3。因此每个维度都能独立落到 L、M、H 任意一档。
4.3 模板匹配
人格模板写成固定的 15 维模式串,例如:
✅ CTRL -> HHH-HMH-MHH-HHH-MHM
✅ BOSS -> HHH-HMH-MMH-HHH-LHL
✅ THIN-K -> HHL-HMH-MLH-MHM-LHH
模板数据在 ce.html#L1286,解析函数 parsePattern() 在 ce.html#L1590。
计算时使用了一个非常常见的离散向量距离模型:
✅ L = 1
✅ M = 2
✅ H = 3
然后逐维计算绝对差值,累加成 distance。完全命中的维度数记为 exact。相似度公式写在 ce.html#L1621:
js● ● ●
const similarity = Math.max(0, Math.round((1 - distance / 30) * 100));
这里的 30 来自理论最大距离:15 个维度,每维最大差值 2,总上限 30。
4.4 特殊覆盖
常规模板匹配完成后,代码还会检查两个特殊分支:
✅ 饮酒隐藏人格 DRUNK,触发条件见 ce.html#L1601 和 ce.html#L1640。
✅ 低匹配率兜底人格 HHHH,触发条件见 ce.html#L1646。
这两条逻辑会覆盖常规人格结果。代码表达得很清楚,作者也把它当作彩蛋和兜底机制在用。
5. 同样的答案会不会得出不同结果
这个问题要分两层看。
5.1 题目顺序随机,不影响分数
页面确实会随机打乱题目顺序,随机插入第一道特殊题,相关代码在 ce.html#L1494 和 ce.html#L1718。
这部分只影响用户看到题目的顺序,不影响最终分数:
✅ app.answers 按题目 id 存储。
✅ computeResult() 遍历的是固定的 questions 数组。
✅ 维度累计只认 q.id -> q.dim -> value,不认题目出现顺序。
结论:如果两次作答的每一道题答案都相同,题目顺序变化不会改结果。
5.2 代码里存在大量“并列第一”
真正需要注意的是排序逻辑。人格模板排序写在 ce.html#L1623:
js● ● ●
}).sort((a, b) => {
if (a.distance !== b.distance) return a.distance - b.distance;
if (b.exact !== a.exact) return b.exact - a.exact;
return b.similarity - a.similarity;
});
问题在这里:
✅ similarity 本身由 distance 推导出来。
✅ 当两个类型 distance 相同、exact 相同,similarity 也会相同。
✅ 这时比较器返回 0,代码没有再做最后一级 tie-break。
我把 15 维等级空间全部扫了一遍,一共 3^15 = 14,348,907 种可能向量。其中有 2,270,028 种向量在第一名位置出现并列。可以举两个具体例子:
✅ 向量 LLLLLLLLLLLLMMM 会在 IMSB 和 DEAD 之间并列。
✅ 向量 LLLLLLLLLLLLHMH 会在 FUCK 和 DEAD 之间并列。
这类向量都能被真实答案构造出来,因为每个维度的 L / M / H 都是可达的。
5.3 这会不会真的“同答案两次结果不同”
在当前主流浏览器里,Array.prototype.sort() 是稳定排序。同一份答案、同一份模板数组、同一运行环境下,结果通常能复现。并列时会落到 NORMAL_TYPES 中排得更靠前的那个类型。
代码层面仍然留着一个不确定点:
✅ 比较器没有把并列情况定死。
✅ 一旦运行环境不同,或者未来有人调整了 NORMAL_TYPES 的顺序,并列答案的结果就可能变化。
结论可以写得更准确一点:
✅ 当前版本在同一环境下通常可复现。
✅ 代码没有把并列模板的定序规则写死。
✅ 存在一批答案会同时命中多个第一名模板。
✅ 如果后续换运行环境、换模板顺序,结果可能发生变化。
如果希望结果完全可复现,建议在排序比较器最后增加一层明确规则,例如按 code 字典序,或按一个单独的 priority 字段排序。
6. 安全审计
6.1 审计结论
这份源码没有后端接口、没有鉴权、没有数据库、没有文件上传、没有命令执行,也没有发现 fetch、XMLHttpRequest、localStorage、sessionStorage、cookie 等状态或数据传输逻辑。站在 Web 安全角度,这个页面的攻击面很窄。
本次审计没有发现高危或中危漏洞。当前更接近“低风险实现问题 + 业务可信度问题”的组合。
6.2 发现项
发现 1:并列模板没有明确决胜规则
✅ 风险等级:低
✅ 类型:业务逻辑缺陷
✅ 位置:ce.html#L1623
说明:
排序只比较 distance、exact、similarity 三项。并列时比较器返回 0。这个问题不会造成代码执行风险,但会影响结果解释的一致性,尤其是模板库继续扩充之后。
建议:
✅ 增加显式的第四排序键。
✅ 将并列规则写入注释或数据字段,方便后续维护。
发现 2:页面存在 innerHTML 渲染点
✅ 风险等级:低
✅ CWE:CWE-79
✅ 位置:ce.html#L1523、ce.html#L1672
说明:
renderQuestions() 和 renderDimList() 使用了 innerHTML 拼接 DOM。当前数据源来自同文件内的常量,页面也没有接收外部输入,所以现阶段没有可直接利用的 XSS 入口。
这个写法的问题在于扩展性。一旦未来把题库、人格文案或维度说明改成接口返回、URL 参数、后台配置,innerHTML 会立刻变成风险点。
建议:
✅ 可控文本统一改用 textContent。
✅ 需要生成结构时,优先使用 createElement 和 appendChild。
发现 3:评分规则全部暴露在前端
✅ 风险等级:低
✅ CWE:CWE-602
✅ 位置:ce.html#L1092、ce.html#L1286、ce.html#L1598
说明:
题库、模板、隐藏分支、图片映射、结果逻辑全部在客户端明文可读。对娱乐页面影响有限;如果后续用于付费测试、用户分层、招聘筛选、积分奖励,这套实现很容易被逆向和篡改。
建议:
✅ 若页面继续保持娱乐用途,这个问题可以接受。
✅ 若要承载正式业务,判分逻辑应迁移到后端,至少把模板库和结果策略从前端剥离。
6.3 未发现的常见风险
本次没有发现以下类型问题:
✅ SQL 注入
✅ 命令注入
✅ SSRF
✅ 文件上传类漏洞
✅ 身份认证绕过
✅ 会话固定
✅ 敏感凭据硬编码
原因很简单:当前项目根本没有这些能力,也就没有对应攻击面。
7. 代码层面的补充判断
还有两个实现细节值得单独记一下。
7.1 previewMode 目前没有入口
状态对象里保留了 previewMode,见 ce.html#L1472。页面代码只在 getQuestionMetaLabel() 里读取它,见 ce.html#L1514。
当前页面没有任何 UI 会把它切成 true。这更像一个未接线的调试开关。
7.2 五类模型更多是展示层概念
dimensionMeta 里给 15 个维度分了“自我模型”“情感模型”“态度模型”“行动驱力模型”“社交模型”,定义位置在 ce.html#L779 一带。
当前评分逻辑没有按这五类再做汇总、加权或单独输出。它们目前主要承担概念包装和结果说明的作用。
8. 建议
如果这个项目还要继续维护,我建议优先做四件事:
1给排序逻辑补上明确的 tie-break,先把结果一致性问题收住。
2把 innerHTML 改成更稳妥的 DOM 构造方式,提前消掉扩展阶段的 XSS 风险。
3把题库、人格库、图片映射拆到独立文件,降低单文件维护成本。
4如果未来要做正式测评、付费使用或业务分流,判分逻辑需要后移,前端当前这套实现承受不了可信度要求。
9. 总结
从源码来看,这个项目完成度不低,页面流程也顺。它的问题不在“跑不起来”,而在“结果解释能否长期站得住”。当前版本适合当娱乐型内容页使用,传播效率高,修改门槛低。只要继续沿用现有单文件实现,后面遇到的问题大概率会集中在三个方向:结果一致性、可维护性、业务可信度。
如果只回答最核心的问题,可以概括成三句话:
✅ 这个页面的结果完全在前端本地算出来,没有后台参与。
✅ 同一组答案在同一环境下通常会得到同一个结果,题目顺序随机不会改分。
✅ 模板并列的情况很多,代码没有写死最后的定序规则,这一点需要尽快补上。
🤖 灵思 · 你的AI信息触角 专注安全漏洞分析与AI前沿技术
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:0xArgus 北境 北境《SBTI为什么能霸屏朋友圈?项目分析|附源码》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论