From 3e9fd2dc97929ec9f808329ae1aa28139a62d943 Mon Sep 17 00:00:00 2001 From: Drsakura Date: Thu, 22 Oct 2026 15:22:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(customer):=20=E8=81=94=E7=B3=BB=E4=BA=BA?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=8E=E8=B7=9F=E8=BF=9B=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E8=A1=A5=E4=B8=8A=E5=BD=92=E5=B1=9E=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两个端点都只有 requirePermission('customer.edit'),而那只说明「这个人有权 编辑客户」,没说明「有权编辑**这一条**客户」。实测一个 sales_rep 账号: GET /api/customers/4 → 404(读是隔离的) POST /api/customers/4/contacts → 201(写进去了) PATCH /api/customers/4/follow-ups/12 → 200 把「客户确认 3000 套订单」改成了「客户取消了订单」 也就是说一个销售能往看不见的客户里插联系人(还能把自己设成主联系人), 还能改写另一个销售客户的跟进历史。 两处都有对照组:紧挨着的 PATCH /contacts 和 POST /follow-ups 一直有这道 检查,所以是漏了而不是设计如此。补上后重放两个利用都是 403,归属人自己 仍然 201/200。 Co-Authored-By: Claude Opus 5 --- backend/server.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/server.js b/backend/server.js index c01e679..41a68fe 100644 --- a/backend/server.js +++ b/backend/server.js @@ -484,6 +484,13 @@ app.post('/api/customers/:id/contacts', auth.requirePermission('customer.edit'), try { const customer = getCustomer(req.params.id); if (!customer) return res.status(404).json({ error: '客户不存在' }); + /* customer.edit 只说明「这个人有权编辑客户」,没说明「有权编辑**这一条**客户」。 + 少了这道归属校验,任何销售业务员猜个 id 就能往别人名下、甚至别组织的客户里 + 插联系人(GET 同一条客户是 404,写却成功),还能把 is_primary 设成 1 + 把自己变成主联系人。隔壁的 PATCH 一直有这道检查,这里是漏了。 */ + if (!customerAccess.canEdit(req.user, customer)) { + return res.status(403).json({ error: '这条客户不在你名下,请先到客户公海领取' }); + } const name = String(req.body?.name || '').trim(); if (!name) return res.status(400).json({ error: '联系人姓名不能为空' }); const fields = ['customer_id', 'name', 'name_en', 'position', 'department', 'email', 'phone', 'whatsapp', 'wechat', 'is_primary', 'notes', 'created_at']; @@ -564,6 +571,13 @@ app.patch('/api/customers/:id/follow-ups/:followUpId', auth.requirePermission('c try { const customer = getCustomer(req.params.id); if (!customer) return res.status(404).json({ error: '客户不存在' }); + /* 同 POST 联系人:校验了「记录属于这条客户」不等于校验了「这条客户属于我」。 + 漏了这道,别人能把我客户的跟进记录改写掉 —— 比如把 + 「客户确认 3000 套订单」改成「客户取消了订单」,CRM 历史就被篡改了。 + POST /follow-ups 一直有这道检查,这里是漏了。 */ + if (!customerAccess.canEdit(req.user, customer)) { + return res.status(403).json({ error: '这条客户不在你名下,请先到客户公海领取' }); + } const record = db.prepare('SELECT * FROM follow_up_records WHERE id = ? AND customer_id = ?').get(Number(req.params.followUpId), customer.id); if (!record) return res.status(404).json({ error: '跟进记录不存在' }); const fields = [];