+ Best separates confirmed flaws from context-dependent risks.
- Grouping authorization absence as confirmed is slightly ambiguous.
Traces the paths external input takes, and states which areas it could not assess.
| Category | Development › Coding |
|---|---|
| Tags | ReviewingCodeDeveloper |
Review this code for security issues. Work by following untrusted input, not by scanning for patterns: 1. **Entry points** — every place external input enters. Request bodies, query strings, headers, file uploads, webhooks, message queues, environment. 2. For each, trace where it flows: into queries, into commands, into file paths, into templates, into deserialization, into logs, back into a response. 3. At each destination, say whether it is neutralized and how. Name the specific mechanism, not "it is validated". 4. **Authorization** — for every operation, is the check present, and is it checking the right subject against the right object. Missing object-level checks are the most common real finding and the least likely to be caught by scanners. 5. Secrets — hardcoded, logged, returned in errors, committed. 6. What errors reveal to the caller. Report per finding: the path from entry to impact, what an attacker gets, and the fix. Rules: - *Never conclude that the code is secure.* Report what you checked and what you could not. Absence of a finding is not evidence of safety. - **State explicitly what the code alone cannot tell you** — framework defaults, middleware applied elsewhere, infrastructure controls, what the caller already authenticated. - Separate confirmed issues from things that depend on context you cannot see. - Do not pad with generic advice. If there is nothing at a given step, say so.
In a security review, "this looks safe" is the most dangerous output. This follows the input paths and is explicit about what the code alone cannot determine.
ChatGPT best traces data flows while preserving uncertainty. Gemini is concrete but overclaims and runs long; [C] is absent.
+ Best separates confirmed flaws from context-dependent risks.
- Grouping authorization absence as confirmed is slightly ambiguous.
+ Clearly traces input into SQL and responses with concrete fixes.
- Overstates contextual risks as confirmed flaws and outcomes.
| Criterion | ChatGPT | Gemini | Leader |
|---|---|---|---|
| Instruction following | 9 | 7 | ChatGPT +29% |
| Accuracy | 9 | 6 | ChatGPT +50% |
| Specificity | 9 | 9 | Tie |
| Structure | 9 | 8 | ChatGPT +13% |
| Right length | 8 | 6 | ChatGPT +33% |
Scored 1–10 by gpt-5.6-sol with model names hidden (2026-09-24). This is an AI review, not a measurement.
We gave three models the same input and copied their answers unedited. Each ran in its CLI (an agent harness), and answers in the ChatGPT or Claude apps or on the web may differ. Outputs are in Korean.
Code: // routes/admin.js
router.get('/api/admin/users/:id/orders', (req, res) => {
const userId = req.params.id;
const query = `SELECT * FROM orders WHERE user_id = ${userId}`;
db.query(query, (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows);
});
});
router.post('/api/admin/refund', (req, res) => {
const { orderId, amount } = req.body;
refundService.refund(orderId, amount);
res.json({ ok: true });
});
Runtime and trust boundaries: 내부 관리자용 Node.js/Express API, AWS EC2 단일 인스턴스에 배포. 프론트는 React 어드민 페이지에서 호출. 로그인 시 발급한 JWT를 헤더에 실어 보내고 미들웨어에서 검증은 하지만, role(super_admin/staff) 체크는 일부 라우트에만 걸려 있습니다.
Data handled: 고객 이름, 전화번호, 배송지 주소, 주문 금액, 환불 내역 등 결제·개인정보를 다룹니다.경로:
req.params.id → 문자열 템플릿 삽입 → SQL 쿼리 → 주문 목록 반환
const query = `SELECT * FROM orders WHERE user_id = ${userId}`;
userId를 숫자로 검증하거나 이스케이프하지 않고 SQL 문에 직접 삽입합니다. 공격자는 다음처럼 쿼리 구조를 변경할 수 있습니다.
/api/admin/users/1 OR 1=1/orders
영향:
SELECT *로 주문에 포함된 불필요한 민감 필드까지 노출 가능또한 JWT 검증만으로는 충분하지 않습니다. 이 코드에는 현재 요청자가 super_admin 또는 허용된 staff인지, 그리고 해당 userId 객체에 접근할 권한이 있는지 확인하는 코드가 없습니다. 내부 관리자 API라도 직원 계정 간 접근 범위가 다르다면 IDOR/BOLA입니다.
수정:
router.get('/api/admin/users/:id/orders', requireJwt, requireAdminRole,
async (req, res) => {
const userId = Number(req.params.id);
if (!Number.isSafeInteger(userId) || userId <= 0) {
return res.status(400).json({ error: 'invalid user id' });
}
// 정책에 따라 객체 접근 권한 확인
if (!canViewUser(req.user, userId)) {
return res.status(403).json({ error: 'forbidden' });
}
const [rows] = await db.query(
'SELECT id, user_id, amount, status, created_at FROM orders WHERE user_id = ?',
[userId]
);
res.json(rows);
}
);
숫자 변환만으로 SQL 인젝션 방어를 대체하지 말고, 반드시 DB 드라이버의 파라미터 바인딩을 사용해야 합니다.
경로:
DB 오류 → err.message → HTTP 응답
res.status(500).json({ error: err.message });
공격자는 SQL 문, 테이블명, 컬럼명, DB 호스트 정보 또는 드라이버 내부 세부사항을 얻을 수 있습니다. 특히 위 SQL 인젝션을 시도할 때 오류 기반 탐색에도 사용됩니다.
수정:
logger.error({ err, requestId: req.id }, 'order lookup failed');
return res.status(500).json({ error: 'internal server error' });
경로:
req.body.orderId, req.body.amount → refundService.refund(orderId, amount) → 환불 처리
이 라우트에는 다음 검사가 보이지 않습니다.
super_admin인지, 환불 가능한 staff인지orderId가 실제 주문인지refundService 내부에서 모두 처리한다면 일부 문제는 없을 수 있지만, 제공된 코드만으로는 확인할 수 없습니다. 서비스에 검사가 없다면 공격자는 유효한 JWT를 가진 낮은 권한 직원으로 다른 고객의 주문을 환불하거나, 금액 조작·중복 환불을 발생시킬 수 있습니다.
수정 방향:
orderId, amount 타입·범위 검증refundService.refund(orderId, amount);
res.json({ ok: true });
refund()가 비동기 함수라면 완료를 기다리지 않습니다.
영향:
수정:
try {
const result = await refundService.refund(orderId, amount);
return res.json({ ok: true, refundId: result.id });
} catch (err) {
logger.error({ err, requestId: req.id }, 'refund failed');
return res.status(502).json({ error: 'refund failed' });
}
단, 서비스가 동기 함수인지와 내부 트랜잭션 처리는 코드 밖의 정보입니다.
id: SQL에 직접 삽입되므로 확인된 SQL 인젝션입니다.orderId, amount: 서비스 내부 구현과 검증이 보이지 않아 잠재적 권한 우회·금액 조작 문제입니다.refundService가 권한, 금액, 상태, 중복 환불을 검증하는지따라서 이 코드만으로 가장 확정적인 취약점은 SQL 인젝션과 DB 오류 정보 노출입니다. 환불 API의 권한 우회 및 금액 조작은 refundService와 실제 라우터 미들웨어를 추가로 확인해야 확정할 수 있습니다.
| 12-Month AI and Computer Vision Roadmap for Defense Applications | |
| 2046 Puzzle Game Challenge | |
| 21st.dev component prompt | |
| 3D FACTORY | |
| 3D FPS Game |