首頁 / 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 個檔案