홈 / 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. 지적 사항마다 한 줄로, 정확한 줄 번호와 백틱으로 감싼 심볼 이름, 구체적인 수정 방법을 씁니다. 필요하면 심각도 태그(🔴 버그, 🟡 위험, 🔵 사소한 지적, ❓ 질문)를 붙입니다.
  3. 얼버무리는 표현과 diff 내용 반복을 뺍니다. 보안 문제, 설계 이견, 새 팀원 온보딩 상황에서는 제대로 된 문단으로 씁니다.

이럴 때 좋습니다

diff나 Pull Request를 리뷰하면서 지적 사항을 빠르게 훑어보고 싶을 때.

알아 둘 점

코멘트만 작성합니다. 코드를 고치거나, 승인·변경 요청을 하거나, 린터를 실행하지 않습니다.

참고 사항 및 위험

지시문만 담긴 파일입니다. 스크립트가 없고, 네트워크에 연결하지 않으며, 파일을 쓰지 않습니다. 패키지에는 원본 저장소의 README, LICENSE, NOTICE도 들어 있습니다. 리뷰 지적 사항은 모델이 만든 결과이니 게시하기 전에 확인하세요.

SKILL.md 원본패키지에 파일 4개