Vinay-O/slopscore

GitHub: Vinay-O/slopscore

slopscore 是一个零依赖的代码质量扫描 CLI,通过 85 个确定性检测器和 181 种模式协议来识别并治理 AI 生成代码中的安全隐患与质量问题。

Stars: 2 | Forks: 0

# 🩺 slopscore ### 扫描代码库中的 AI slop。获取 Slop Score。交付干净的代码。 **一个零依赖的 CLI + 一套面向 AI 编程代理的 181 种模式协议。** vibe-coded 软件的解药:将*生成*转化为*治理*。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/99/993938d8ce5e902ccfb9d6747725c320d855dea3235ed9a304cedf0d94c9321f.svg)](https://github.com/Vinay-O/slopscore/actions/workflows/ci.yml) [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE) [![node](https://img.shields.io/badge/node-%E2%89%A518-339933)](https://nodejs.org) [![dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json) [![self-scan](https://img.shields.io/badge/slop%20score-0%20·%20pristine-brightgreen)](#it-passes-its-own-scan)
``` npx slopscore ``` 就这样。无需安装,无需配置,无需 API key。将它指向一个代码库,它会用一个数字告诉你——你即将交付多少 AI slop,以及如何准确地修复每个问题。 ## 问题所在 在 2026 年,**46% 的新代码是由 AI 生成的**,并且 **92% 的开发者每天使用 AI 工具**——但对这些代码的信任度却从 **77% 下降到了 60%**。GitClear 对 2.11 亿行代码的分析发现,重复代码增加了 **4–8 倍**,重构骤降了 **60%**,并且 AI PR 携带的**问题多出约 1.7 倍**。大约 **45% 的 AI 生成代码带着安全弱点**交付。 来源:GitHub / Stack Overflow 2025–26 开发者调查(采用率、信任度);[GitClear, *AI Copilot Code Quality 2025*](https://www.gitclear.com/ai_assistant_code_quality_2025_research)(2.11 亿行代码——重复率、重构减少);CodeRabbit(AI PR 中的问题多出约 1.7 倍);[Veracode, *2025 GenAI Code Security Report*](https://www.veracode.com/resources/analyst-reports/2025-genai-code-security-report/)(45% 的任务引入了漏洞,涵盖 100 多个模型)。完整的归属说明见 [`ANTI_SLOP_PROTOCOL.md` §8](ANTI_SLOP_PROTOCOL.md)。 工具并没有制造这个问题。它们只是揭示了一开始就少得可怜的结构。 AI slop 之所以隐蔽,是因为它看起来已经*完工了*——命名一致、测试通过、一个精心打磨的紫色英雄区块——但在架构上却空洞无物。你无法仅凭一眼就看穿它。**slopscore 替你揪出它。** ## 查看运行效果 ``` $ npx slopscore examples/slop.tsx slopscore · 1 files · 0.03 kLOC slop.tsx :5 🔴 CRIT [058] Hardcoded secret / API key const API_KEY = "sk-proj-abc123def456ghi789jkl012mno345"; fix: Move to an env var / secret manager. A committed secret is COMPROMISED — rotate it now. :12 🔴 CRIT [073] Auth token in localStorage localStorage.setItem("auth_token", "ey.some.jwt"); fix: Store auth in httpOnly, Secure, SameSite cookies — not localStorage (XSS-readable). :13 🔴 CRIT [072] SQL injection via template literal const q = `SELECT * FROM users WHERE id = ${data.id}`; fix: Use parameterized queries / prepared statements. Never interpolate input into SQL. :15 🔴 CRIT [053] Empty catch block (silent error swallowing) } catch (e) {} fix: At minimum log with context; better, handle or rethrow and surface a real message. :29 🔴 CRIT [071] dangerouslySetInnerHTML / .innerHTML without sanitization
fix: Render as text, or sanitize with DOMPurify + an allowlist before injecting. … and 16 more ╔══════════════════════════════════════════╗ ║ S L O P S C O R E ║ ╚══════════════════════════════════════════╝ 84 weighted (33 lines scanned) 5 critical 9 major 7 minor by rule: 105 ×1 · 176 ×1 · 058 ×1 · 054 ×1 · 070 ×1 · 099 ×1 ▶ Vibe-coded. Audit before anyone depends on it. ``` 每个发现都有一个**严重程度**、一个**目录 ID**、一个**置信度**、**确切位置**和一个**修复建议**。没有模糊的“代码异味”——而是一份你可以立即采取行动的待办清单。 ## 快速开始 ``` npx slopscore # scan the current directory npx slopscore scan src --fail-on minor npx slopscore examples/slop.tsx # watch a sloppy file score 84 npx slopscore . --markdown > slop.md ``` 或者安装它: ``` npm i -g slopscore && slopscore ``` ## 两种使用方式 slopscore 提供了**同一理念的两半**:一个由你运行的确定性扫描器,以及一份你交给 AI 的协议。 ### 1. 扫描器(确定性,零依赖) 在几毫秒内在本地运行 **85 个检测器**——一次强化的**安全扫描**(密钥、SQL/命令注入、禁用的 TLS 验证、弱哈希、不安全的随机数、硬编码的私钥、不安全的反序列化、通配符 CORS、`eval`、未验证的 JWTs、明文 HTTP),以及空 catch 块、`any`、幻觉 API、缺失的 `alt`、VibeCode 紫色渐变、AI 流行语文案和超大混杂文件(god files)。没有 LLM,没有网络请求,没有依赖项。 使用 `slopscore scan . --category security` 运行一次专注的安全审计。 ### 2. 协议(面向你的编程代理) [`ANTI_SLOP_PROTOCOL.md`](ANTI_SLOP_PROTOCOL.md) 是一份面向 AI 代理的**181 种模式操作手册**。把它交给 Claude Code、Cursor、Codex CLI、Aider、Copilot、Windsurf 或 Cline,然后说: 代理会运行一个定义好的循环——定向 → 扫描 → 评分 → 分诊 → **修复** → 验证 → 报告——其中每个模式都包含 `DETECT`、`FIX` 和**修复权限**(🟢 自动修复 · 🟡 提议 · 🔴 标记给人类),这样它就知道哪些可以自行更改,哪些需要你来决定。**CLI 已经自动化的 85 种模式在目录中直接标记为 `⚙️ slopscore scan`**(根据扫描器自身的规则表生成,因此这两半永远不会产生偏差)——未标记的模式正是代理发挥价值的地方。 ``` slopscore protocol | pbcopy # copy the protocol to paste into your agent slopscore scan . --format agent # compact, context-window-friendly output for an agent slopscore explain 058 # look up any one pattern + its fix, by id ``` **让代理自动使用它。** 你不应该每次会话都得提醒你的代理。`slopscore init` 会写入一个 **`AGENTS.md`**(这是 Cursor、Codex、Claude Code、Aider、Windsurf 和 Cline 都会读取的跨工具标准),告诉代理加载 `npx slopscore protocol`,遵循它,并在宣布完成之前以 `npx slopscore scan` 作为门禁——这样协议就会自行被采用,无需复制粘贴。如果你已经有 `AGENTS.md` 或 `CLAUDE.md`,它会(以幂等方式)追加该部分,而不是覆盖它。 扫描器负责发现问题。协议负责修复问题。CI 门禁负责将其拒之门外。而 `AGENTS.md` 让你的代理在无需提醒的情况下完成这三件事。 ## Slop Score 一个数字,按严重程度加权,每 1,000 行代码进行标准化,这样大型代码库就不会仅仅因为代码量大而显得更糟: ``` SLOP SCORE = (🔴 critical × 10) + (🟠 major × 3) + (🟡 minor × 1) SLOP DENSITY = weighted findings per 1,000 lines (kLOC) ``` | 密度 (加权 / kLOC) | 结论 | |:--|:--| | 0 | **纯净。直接交付。** | | ≤ 2 | 干净。人类级别。 | | 2–6 | 轻度 slop。通过一次专注的检查即可修复。 | | 6–12 | 严重 slop。需要真正下功夫。 | | > 12 | Vibe-coded。在任何人依赖它之前进行审计。 | ### 该分数反映的是*生产*风险 测试运行器中的 `console.log` 不是生产环境中的 SQL 注入——所以它们不应该获得相同的分数。slopscore 默认具有上下文感知能力: - **生成的和 vendored 文件会被跳过**——压缩后的 bundle (`*.min.js`)、包含超长单行的文件,以及带有 `@generated` 头的文件不是你编写的代码,因此它们不会污染分数。 - **测试和工具代码会被报告,但不参与评分**——在 `test/`、`e2e/`、`scripts/`、`__tests__/`、`*.spec.*` 等目录中的发现仍会显示,但它们不会抬高标题中的 Slop Score(`5 个严重问题 … (生产环境) · + 12 个在 测试 / 工具 中 — 已报告,不参与评分`)。 你看到的数字就是你实际交付的风险。 ## 它通过了自身的扫描 一个旨在防止交付 slop 的 linter 绝不能*自身就是* slop。因此 slopscore 对自己也坚持同样的标准: - **零运行时依赖。** 整个工具完全基于 Node 内置模块。 - **此代码库上的 `slopscore scan .` 返回 `0`——“纯净”。** - 其自身的 CI 会在其源代码上以 `--fail-on minor` 运行扫描器,如果有任何漏网之鱼,则会导致构建失败。 ``` npm run selfcheck # → Pristine. Ship it. (exit 0) ``` *(规则定义文件和示例 fixture 被排除在外——它们必然包含 slopscore 正在寻找的特定字符串,就像 ESLint 排除它自己的 fixture 一样。参见 [`.slopscoreignore.md`](.slopscoreignore.md)。)* ## 将其引入 CI ``` slopscore init # writes .slopscore.json + a GitHub Action PR gate ``` 或者粘贴这段代码: ``` # .github/workflows/anti-slop.yml name: anti-slop on: [pull_request] jobs: slopscore: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npx slopscore scan . --fail-on major ``` **想在 PR diff 中内联显示发现的问题?** 导出 SARIF 并将其上传到 GitHub 代码扫描: ``` - run: npx slopscore scan . --sarif --fail-on never > slopscore.sarif - uses: github/codeql-action/upload-sarif@v3 with: { sarif_file: slopscore.sarif } ``` **想在每个 PR 上发布 Slop Score?** 从 Markdown 报告中添加一条置顶评论: ``` - run: npx slopscore scan . --markdown > slop.md || true - uses: marocchino/sticky-pull-request-comment@v2 with: { path: slop.md } ``` **Pre-commit hook**(在代码被提交之前就捕获 slop——仅扫描你暂存的内容): ``` # .git/hooks/pre-commit (对其执行 chmod +x) files=$(git diff --cached --name-only --diff-filter=ACM) [ -z "$files" ] || npx slopscore $files --fail-on major ``` ### 在杂乱的代码库中采用它——棘轮模式 一个真实的代码库在第一天不会得 0 分,而首次运行就出现“严重 slop”往往会导致一个工具被关闭和遗忘。所以**把已经存在的问题作为基线,并且只针对_新的_ slop 进行拦截:** ``` slopscore scan . --baseline # first run: snapshots current findings, exits 0 slopscore scan . --baseline # from now on: passes unless you ADD slop slopscore scan . --update-baseline # accept the current state as the new floor ``` 基线是按内容而不是行号来记录发现结果的——所以四处移动代码永远不会被记录为新的 slop。提交 `.slopscore-baseline.json`,你的 CI 门禁今天就能变绿,同时问题计数只会永远下降。 ## 自动修复安全的内容 有些 slop 只有一个正确的修复方法,不需要主观判断——一个多余的 `console.log`,一个缺失了 `alt` 的 ``,或者 Python 的 `== None`。`slopscore fix` 会为你应用这些(且仅应用这些)修复: ``` slopscore fix . --dry-run # preview every change, write nothing slopscore fix . # apply them, then review the diff slopscore fix . --only 052 # just the console.logs slopscore fix . --except 069 # everything fixable except comment removal ``` 它只会触碰那些带有确定性、保持行为不变的转换的 🟢 **AUTO**(自动)权限规则(目前包括:`052` console.log、`069` 步骤叙述注释、`081` `` alt、`152` Python `== None`→`is None`、`169` `target="_blank"`→`rel="noopener"`)。它在设计上非常保守——多行的 `console.log`、尾随注释、作为不带大括号的 `if` 主体的行,或者位于字符串字面量内部的比较,都会被原封不动地保留(可能会改变行为或破坏结构的修复永远不会被应用)。提议级别的修复器(例如 `179` Python `== True`)仅在你通过 `--only 179` 指定时才会应用。其他的所有内容都会保持为你需手动审查的提议/标记。 ## 本地开发 ``` slopscore scan . --watch # re-scan on every save — a live conscience slopscore scan . --history # record the score over time + a trend sparkline slopscore scan . --sarif # inline annotations on the PR diff (code scanning) slopscore scan . --markdown --out slop.md # write a UTF-8 report file directly ``` `--history` 会写入 `.slopscore-history.json` 并打印出 `trend █▃▁ 0 weighted · down 100% since last run`——提交它,看着 slop 随着一个接一个的 sprint 不断减少。 **Windows / 旧版终端。** slopscore 会自动检测无法渲染 Unicode 的控制台(非 UTF-8 代码页下的旧版 `cmd`/PowerShell)并回退到 ASCII 字符——使用 `--ascii` / `--unicode` 强制指定渲染方式。相比于 `>` 重定向,更推荐使用 `--out `:它直接从 Node 写入 UTF-8,因此报告永远不会输出为乱码或 UTF-16。 **置信度。** 每一个发现都带有与严重程度分离的置信度(高 / 中 / 低)——像密钥和 SQL 注入这样精确的检测器置信度为高;设计/文案习惯用法和重复块启发式检测则较柔和。使用 `--min-confidence high` 将 CI 门禁设置为只看强信号,或者直接阅读内联的 `~medium confidence` 标签。无用的 `slopscore-disable` 指令(它们所隐藏的发现已经消失)会被标记为**过期**,以免它们不断累积。 ## 配置 你代码库根目录下的 `.slopscore.json`: ``` { "preset": "library", "ignore": ["examples", "legacy", "src/generated"], "failOn": "major", "rules": { "054": false, "099": "minor" }, "paths": { "legacy/": { "*": "minor" } } } ``` | 选项 | 含义 | |:--|:--| | `preset` | 根据项目类型调整覆盖范围(见下文)。你的 `rules` 始终优先于 preset。 | | `ignore` | 额外要跳过的路径(添加到内置的 `node_modules`、`dist` 等之外) | | `failOn` | 在 `critical`、`major`、`minor` 或 `never` 时以非零状态退出 | | `rules` | 每条规则的覆盖设置——`false`/`"off"` 禁用规则;`"critical"`/`"major"`/`"minor"` 覆盖其严重程度 | | `paths` | 每个目录的覆盖设置——结构与 `rules` 相同,此外可用 `"*"` 指向某路径下的所有规则(例如弱化整个 `legacy/` 目录) | **预设**(`--preset ` 或 `preset` 配置项)可以调整覆盖范围,这样你就不会被无关的发现所困扰: | Preset | 效果 | |:--|:--| | `web` / `marketing` | 一个带有样式的 Web UI——开启所有检查(默认)。 | | `library` / `backend` | 无 UI 界面——关闭**视觉**、**文案**和 **a11y** 类别;保留代码、安全和性能。 | | `cli` | 类似于 `library`,同时还会静默 stdout 调试规则(`console.log`、Python `print`、Rust `println!)——因为 stdout 就是产品本身。 | | `mui` / `tailwind` / `chakra` / `mantine` / `emotion` / `styled-components` / `vanilla-extract` | 样式框架别名。slopscore 的视觉检测器与框架无关(它们同时匹配 Tailwind classes **和** CSS-in-JS / MUI `sx` / styled / emotion),因此这些仅仅是确认“Web UI,所有检查已开启”。 | **通过指令内联屏蔽单个发现**(为未来的你提供一个理由): ``` // slopscore-disable-next-line 054 — deliberate cast at the Deno JSON boundary const config = data as any; ``` 单独的 `// slopscore-disable-next-line`(不带 id)会屏蔽下一行的所有规则;`slopscore-disable-line ` 在同一行生效。终端报告会打印有多少个发现被屏蔽了,这样它们就不会慢慢腐烂发霉。 **它也会遵循你的 `eslint-disable`。** 一行你已经明确、慎重签核过的代码(如 `// eslint-disable-next-line @typescript-eslint/no-explicit-any` 或 `no-console`)不会被重新审查——slopscore 不会再次标记你的团队已经审查过的决定。**安全发现是例外:** `eslint-disable` 永远无法使 `eval`、SQL 注入、硬编码密钥等静默——揪出某人随意放行的漏洞正是它的核心意义所在。 ## 它能检测什么(181 项中的 85 项) CLI 运行确定性的子集;[完整的 181 种模式目录](ANTI_SLOP_PROTOCOL.md)(包括视觉、架构和重度依赖判断的模式)才是你交给你的代理的内容。 | 类别 | 示例 | |:--|:--| | 🔐 安全 | 硬编码的密钥(AWS key、GitHub PAT、**PEM 私钥**)· SQL 注入(模板**和**字符串拼接)· 命令注入 · **禁用的 TLS 验证** · **用于密钥的弱哈希 (MD5/SHA-1)** · **不安全的随机数生成 token** · **不安全的反序列化** (`pickle`, `yaml.load`) · **通配符 CORS** · **`eval`/`new Function`** · **未验证的 JWTs** (`alg: none`) · **明文 HTTP** · 没有加 `noopener` 的 `target="_blank"` · 存放在 `localStorage` 的 auth token · 暴露在 URL 参数中的密钥 · `dangerouslySetInnerHTML` · 暴露给客户端的堆栈跟踪 · 被提交的 `.env` | | 🧱 代码质量 | 空 catch 块 · 过于宽泛的异常 · 满地 `any` · 双重断言 · 幻觉 API · 超大混杂文件(感知内聚度) · 复制粘贴的重复代码块 · TODO/FIXME · `setTimeout` 竞态条件“修复” · 同义反复的测试断言 | | 🚀 性能 | 通过 `JSON.parse(JSON.stringify())` 进行深拷贝 · `SELECT *` 的过度获取 · `forEach(async …)`(未 await) · 仅为一个工具函数导入整个库 | | 🐍 特定语言 | **Python:** 可变的默认参数 · `== None` · `eval`/`exec` · f-string SQL · `os.system`/`shell=True` · `print` 调试。 **Go:** 空的 `interface{}` · 被忽略的 error · `fmt.Print` · `panic()`。 **Rust:** `.unwrap()`/`.expect()` · `todo!`/`panic!` · `unsafe` · `dbg!`/`println!` | | 📦 供应链 | 依赖臃肿 · 未固定/别名的 LLM 模型字符串(日期固定的 id 豁免)· 发布到生产环境的 source map | | ♿ 无障碍 (a11y) | 没有 `alt` 的 `` · 交互式的 `
` · 带有 `outline:none` 但没有焦点样式 | | 🎨 视觉 slop | VibeCode 紫色渐变 · 锥形/网格渐变 · 毛玻璃拟态 · 渐变裁切文本 · Sparkle/Wand 图标 · 被滥用的 AI 字体 · 彩纸/礼花特效——跨越 **Tailwind classes 和 CSS-in-JS**(MUI `sx`, styled, emotion)进行匹配 | | ✍️ 文案 | AI 流行语(“supercharge”、“seamlessly”) · 带感叹号的 CTA · “Coming soon” · “Oops!” 错误提示 · “Submit” 按钮 · “Click here” 链接 · lorem ipsum(无意义占位文本) | | 🏗️ 架构 / API | 硬编码的 localhost · SPA 中的 `window.location` 导航 · 使用 `location.reload()` 进行故障恢复 · HTTP 200 返回的错误 · 破坏性 GET 请求 · `z-index: 9999` · `alert()`/`confirm()` | 运行 `slopscore rules` 查看包含严重程度和修复权限的完整列表。 ## 横向对比 | | slopscore | ESLint | vibecop | Semgrep | |:--|:--:|:--:|:--:|:--:| | 专针对 AI slop 的模式 | ✅ | ❌ | ✅ | ⚠️ | | 视觉 / 设计 / 文案特征 | ✅ | ❌ | ❌ | ❌ | | Slop Score(单一数值) | ✅ | ❌ | ❌ | ❌ | | 面向代理的协议(修复权限) | ✅ | ❌ | ❌ | ❌ | | 零安装(`npx`,无需配置) | ✅ | ⚠️ | ⚠️ | ⚠️ | | 零依赖 | ✅ | ❌ | ❌ | ❌ | 此表的范围限定为**专门针对 AI slop 检测**——它不是质量排名。ESLint 和 Semgrep 在它们专门构建的领域(正确性规则、污点分析)表现出色,slopscore 绝不能替代它们;这里的 `❌` 仅仅意味着“不是为了捕获 slopscore 针对的那些 AI 特征而设计的”。slopscore 有意定位为一种**快速、透明、零依赖的第一道防线**——基于正则表达式/启发式检测,而非深层数据流分析。对于 AST 级别的分析(真正的圈复杂度、跨文件重复、污点追踪),请**将其与** [vibecop](https://github.com/bhvbhushan/vibecop)、Semgrep 或 CodeQL **结合使用**——协议([`ANTI_SLOP_PROTOCOL.md`](ANTI_SLOP_PROTOCOL.md))会准确地告诉你的代理何时该求助于这些工具。它是对你的 linter 的补充;而不是替代品。 ## 常见问题解答 **仅靠正则表达式足以捕获所有 AI slop 吗?** 不够,slopscore 从未声称如此。它能快速且低误报地搞定那些可确定性检测的子集,然后将重度依赖判断和 AST 级别的模式推迟给协议和专用工具处理。诚实胜过花架子。 **它会修改我的代码吗?** CLI 仅用于*报告*。修复由你完成,或者由遵循协议修复权限规则的 AI 代理完成。 **它支持 Python / Go / Rust 吗?** 是的——每种语言都有专门的检测器(Python:可变默认参数、`== None`、`eval`/`exec`、f-string SQL、`pickle`、`print`;Go:被忽略的 error、`fmt.Print`、`panic`、命令注入;Rust:`.unwrap()`、`panic!`、`unsafe`、`dbg!`)。注释/字符串屏蔽机制具有语言识别能力(Python 的 `#` 注释和文档字符串不会被当作代码扫描),并且 `test_*.py` / `*_test.go` 被正确地视作测试区域。安全和文案检测器在所有源代码上运行;JS/TS 的检测面最深,因为那是 vibe-coded slop 最集中的地方。 **它的准确性如何 / 误报率?** 在设计上误报率很低,并经过了严格测试:干净、符合语言习惯的代码能稳定获得零分。扫描器具备注释和字符串识别能力(它不会标记文档字符串中的 `eval()`、注释中的 `console.log`,或将 `firstName + " and " + lastName` 标记为 SQL),遵循你的 `eslint-disable`,跳过测试文件,豁免 ORM 的 `Column == None` 和带日期固定的模型 id,并附带了 **150 多个测试**,包括一组干净代码测试组。在成熟的代码库中,高频出现的模式(例如设计系统的重复标签)会被**聚类**为一行,这样它就不会淹没真正重要的发现,而单个嘈杂的检测器永远不会决定最终结论(评分限制了每条规则的权重)。`slopscore fix` 经过验证不会破坏代码——它永远不会删除会破坏结构的行,也不会在字符串内部进行重写。发现了错误的检测结果?[提交一个 issue。](../../issues) ## 许可证 MIT——参见 [LICENSE](LICENSE)。使用它,fork 它,把它构建到你的 pipeline 中。
**如果 slopscore 帮助你避免了交付 slop,请点一个 ⭐——这有助于下一个人发现它。** *从生成到治理。*
标签:AI辅助编程, GNU通用公共许可证, MITM代理, Node.js, 代码安全审计, 文档结构分析, 自定义脚本, 错误基检测, 静态代码分析