Pentest Human Gate
Trang này hướng dẫn chạy 5 lệnh curl kiểm tra mọi cách approve không phải người đều bị chặn bằng 403 trước go-live. Chạy lại mỗi khi đổi auth hoặc proxy để xác nhận chốt Human Gate còn hiệu lực.
Đối tượng: approver phối hợp sysadmin, chạy l ại mỗi khi đổi auth/proxy. Điều kiện: có 1 task ở
reviewđể bắn approve thử; môi trường dev local bậtAGENTBOARD_DEV_IDENTITY=1(ngoài loopback, header giả không có tác dụng). Kỳ vọng: mọi cách approve "không phải người" đều phải 403.
Chốt chặn gồm 2 lớp
- Lớp 1 (route):
actor.typephải là'human'— service token phân giải vềsystemnên rớt ngay. - Lớp 2 (phân quyền theo Space): role đọc từ bảng
space_members, không bao giờ đọc từ header (x-forwarded-rolebị lờ đi có chủ ý). Header danh tính chỉ tin khi peer là proxy trong allowlist, hoặc JWT Cloudflare Access verify chữ ký, hoặc dev identity (loopback + ngoài production + opt-in). - Scope
gate:approvecố ý không tồn tại trong scope-registry (đã verify:ALL_SERVICE_SCOPESchỉ cóprojects:read,agents:read,orchestrations:*,tasks:relationships) — không token nào xin được quyền này.
Ba phát bắn bắt buộc
Chuẩn bị 1 task ở cột review (l ấy <id> của nó), rồi bắn từ máy ngoài proxy tin cậy:
# 1. Không JWT, chỉ gửi header giả mạo → PHẢI 403
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
http://127.0.0.1:8080/api/tasks/<id>/approve \
-H 'x-forwarded-email: attacker@evil.com' -H 'x-forwarded-role: admin'
# → 403
Header x-forwarded-role luôn bị lờ; x-forwarded-email từ peer không trong allowlist cũng bị lờ —
actor rớt về system và Human Gate chặn.
# 2. Service token (actor 'system') approve → PHẢI 403
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
http://127.0.0.1:8080/api/tasks/<id>/approve \
-H "Authorization: Bearer <service-token>"
# → 403
Bearer token (kể cả legacy API_KEY) chỉ chứng minh "service nào gọi", không chứng minh "người nào bấm" —
actor vẫn là system, rớt lớp 1.
# 3. User role viewer/member approve → PHẢI 403
# Dev local: giả danh đúng role qua dev identity (loopback + AGENTBOARD_DEV_IDENTITY=1)
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
http://127.0.0.1:8080/api/tasks/<id>/approve \
-H 'x-mock-user-email: viewer-that-la@cty.vn' -H 'x-mock-user-role: viewer'
# → 403
Production: đăng nhập bằng tài khoản viewer/member thật rồi bấm Approve trên UI (hoặc gọi API với
JWT của họ) — kỳ vọng 403 thiếu role admin.
Dừng nghiệm thu, gọi sysadmin kiểm tra: Cloudflare JWT verify + nginx strip header + server bind loopback. Xem Kiến trúc tổng thể.
Hai case bổ sung sau Chặng 0B (đã vá — chạy để xác nhận vá còn hiệu lực)
# 4. F1: member Space A đọc/sửa plan của Space B → PHẢI chặn đúng Space
# (trước vá: GET|PATCH /api/plans/:id rơi về rule field-optional, "optional"
# thành không-scope). Dev local: giả danh member chỉ-thuộc-A
curl -s -o /dev/null -w '%{http_code}\n' \
http://127.0.0.1:8080/api/plans/<plan-cua-B> \
-H 'x-mock-user-email: <email-chi-thuoc-A>' -H 'x-mock-user-role: member'
# → 403 (không còn 200 kèm cây plan Space khác)
Production thay cặp header dev bằng JWT của member chỉ-thuộc-A. Mọi route plan theo id
(GET|PATCH /plans/:id, regenerate-branch, apply) đều giải Space từ chính draft.
# 5. Rollback 502-quyền: admin Space rollback → PHẢI 200, và KHÔNG ai được
# "sửa" bằng cách nâng role Arkon
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
http://127.0.0.1:8080/api/wiki/pages/<slug>/revisions/1/rollback \
-H 'Content-Type: application/json' -d '{"projectId":"<id>"}'
# → admin/owner 200; member/viewer/service-token 403.
# Nếu 502 kèm detail về quyền: kiểm global_role + đợi 1 nhịp reconciler —
# CẤM đặt role='admin' trong Arkon (vỡ lọc department, đọc wiki mọi Space).
# Chi tiết: [Phân quyền Arkon](../../reference/phan-quyen-arkon).
Bảng mã rollback để đối chiếu (không đoán — đúng chữ server trả):
| Gọi rollback | Mã + message |
|---|---|
Thiếu projectId | 400 {"error": "projectId is required to roll back a wiki page"} |
version không phải số nguyên dương | 400 {"error": "version must be a positive integer"} |
| Space chưa link Wiki / Arkon vắng | 400 {"error": "Wiki space not linked or offline"} |
| Trang không thuộc Department của Space | 404 {"error": "Wiki page not found in this Space"} |
| Vượt cổng Board nhưng Arkon từ chối quyền | 502 {"error": "Failed to roll back page: …"} — cổng Arkon, không phải Board sập |
Vì sao không có bypass?
- Xác thực 2 lớp ở
POST /api/tasks/:id/approve:actor.typephải là'human', role trên Space>= admin. - Scope
gate:approvecố ý không tồn tại trong scope-registry — không token nào xin được quyền này. - Chi tiết: Vì sao Human Gate không bypass được.
Xác minh
- Cả 5 phát trên đều về đúng mã kỳ vọng; phát nào lệch là dừng nghiệm thu.
- Admin Space trong Arkon vẫn là
role='employee'+global_role='knowledge_manager'. - Task bắn thử vẫn ở
review(chưa có approve nào lọt) — kiểm traGET /api/tasks/<id>/status.
Gặp lỗi? Xem Xử lý sự cố.