首页 / Skills / 开发提效 / Caveman 代码审查
开发提效

Caveman 代码审查

Julius BrusseeApache-2.0Skill 语言: 英文
协议已核对不含脚本
演示真实运行记录 · claude-opus-5-5 · 2026年10月
Please review the diff before I merge it: ```diff --- a/src/orders/service.ts +++ b/src/orders/service.ts @@ -12,14 +12,24 @@ export class OrderService { async getOrderTotal(orderId: string): Promise<number> { - const order = await this.repo.findById(orderId); - return order.items.reduce((sum, i) => sum + i.price * i.qty, 0); + const order = await this.repo.findById(orderId); + let total = 0; + for (let i = 0; i <= order.items.length; i++) { + total += order.items[i].price * order.items[i].qty; + } + return total; } async applyCoupon(orderId: string, code: string) { - const coupon = await this.coupons.find(code); + const coupon = this.coupons.find(code); if (coupon.expiresAt < Date.now()) throw new Error("expired"); - await this.repo.update(orderId, { couponId: coupon.id }); + this.repo.update(orderId, { couponId: coupon.id }).catch(() => {}); } + + async listOrders(userId: string) { + return this.db.query(`SELECT * FROM orders WHERE user_id = '${userId}'`); + } } ```
Skill: caveman-review

I wouldn't merge this yet. There are 3 bugs that will break things, 1 SQL injection, and 1 risk. The original code was correct before this change.

src/orders/service.ts

  • L15: 🔴 bug: the loop runs one step too far. i <= order.items.length reads order.items[length], which is undefined, so .price throws a TypeError on every call. Use <, or keep the original reduce, which was correct and shorter.
  • L22: 🔴 bug: await was removed from this.coupons.find(code), so coupon is now a Promise. coupon.expiresAt is undefined and undefined < Date.now() is false, so the expiry check never fires and expired coupons are accepted. coupon.id is also undefined. Put the await back.
  • L24: 🔴 bug: the update is fire-and-forget and .catch(() => {}) hides any failure. Callers see success even when the coupon was never saved, and nothing is logged. Put back await this.repo.update(...) and let errors reach the caller.
  • L22-23: 🟡 risk: if find returns null for an unknown code, coupon.expiresAt throws a TypeError instead of a clear error. Add if (!coupon) throw new Error("invalid coupon").

L28: 🔴 security: SQL injection in listOrders. userId is put straight into the SQL string. A value like ' OR '1'='1 returns every user's orders, and with stacked queries an attacker could change or delete data (CWE-89, OWASP A03). Even if userId comes from an auth token today, this function can't assume that, and a single caller passing a request param makes it exploitable. Use a parameterized query, e.g. this.db.query("SELECT * FROM orders WHERE user_id = $1", [userId]) (use whatever placeholder syntax your driver expects).

  • L28: 🔵 nit: SELECT * returns every column and will pick up any column added later. List the columns you need.
  • L27: 🔵 nit: no return type, unlike getOrderTotal. Add Promise<Order[]>.

Real run recorded with claude-code / claude-opus-5-5. Output is shown verbatim, unmodified.

它做什么

按 L<行号>: <问题>. <修法>. 的紧凑格式写代码审查意见,可以直接贴到 Pull Request 里。

工作方式

  1. 输入 /caveman-review,或说 "review this PR"、"review the diff" 时触发。
  2. 每个问题一行,写明准确行号、用反引号标出的符号名和具体修法,可加严重程度标记:🔴 bug、🟡 风险、🔵 小问题、❓ 疑问。
  3. 去掉含糊措辞和对 diff 的复述;遇到安全问题、架构分歧和新人上手的场景,则改写完整的一段说明。

适合场景

审查 diff 和 Pull Request,希望意见一眼就能扫完。

需要了解

它只写审查意见,不改代码、不点批准或要求修改,也不跑 lint。

说明与风险

纯指令文件:没有脚本、不联网、不写文件。 压缩包里另有原仓库的 README、LICENSE 和 NOTICE。 审查结论是模型生成的,发出去之前请自己核对。

SKILL.md 原文包内共 4 个文件