สมอง SML

คลังความรู้ / มาตรฐาน & กฎ

#standards#security#api#frontend#backend#checklist

เช็กลิสต์ความปลอดภัย API (ตัวตน/สิทธิ์/ช่องโหว่เว็บ)

ไล่ให้ครบก่อน commit ทุกครั้งที่สร้างหรือแก้ route ใน frontend/src/app/api/** และ Go handler ใน backend/internal/adapter/inbound/http/** (กฎข้อ 9, 10, 20 ใน AGENTS.md) ส่วนเงิน/transaction ดู ความถูกต้องของข้อมูล: เงิน ภาษี Transaction เดียว และ Rebuild (Data Integrity, big.Rat, ExecBatch)

A. ตัวตนและสิทธิ์ (AuthN/AuthZ)

  1. ตัวตนต้องมาจากสิ่งที่ตรวจแล้วเท่านั้น — ห้ามรับ email/userCode/database จาก body มาสร้าง session; Google ID Token ต้องตรวจ aud = Client ID ของเว็บ และ email_verified === true
  2. Route ที่เรียก backend ด้วยกุญแจ Admin (header X-Admin-Api-Key) ต้องตรวจสิทธิ์ Admin ของผู้เรียกฝั่ง server ก่อน (requireAdmin() หรือ isWorkspaceAdmin() ใน src/app/api/workspace/[...path]/route.ts) — แค่มี session ไม่พอ
  3. ค่า actor (actor_email, actor_user_code) เขียนทับหลัง spread body เช่น { ...body, actor_email: session.email } ห้ามให้ผู้ใช้ปลอม
  4. Route ที่ไม่ต้อง login ห้ามให้ผู้เรียกเลือก tenant/ฐานข้อมูลเอง (?db=, ?provider=) ให้ตรึงที่ฐานสาธิต smldemo
  5. /blob/insert และ /query/exec ใช้ write policy เดียวกัน (domainquery.ValidateClientInsert / ValidateClientPolicy ใน write_policy.go): ตารางเงิน/สต็อก/GL ห้ามเขียนตรงจากเบราว์เซอร์

B. ช่องโหว่ฝั่งเว็บ

  1. returnTo / redirect ใช้ safeReturnPath() (frontend/src/lib/safe-return.ts, มี unit test) เสมอ — startsWith("/") && !startsWith("//") ไม่พอ เพราะ /\evil.com เบราว์เซอร์ตีเป็น //evil.com (ใช้ใน login/page.tsx และ select-database/page.tsx)
  2. HTML string ที่เข้า innerHTML, Leaflet bindPopup, dangerouslySetInnerHTML ต้อง escape ข้อมูลจาก DB ทุกช่อง (escapePopupHtml ใน SmlMapPickerModal.tsx)
  3. ไฟล์อัปโหลดที่ส่งต่อให้เบราว์เซอร์ ใช้ safeMediaContentType() (frontend/src/lib/api-server.ts) อนุญาตเฉพาะรูป raster + PDF พร้อม X-Content-Type-Options: nosniff — ห้ามส่ง text/html / image/svg+xml (ใช้กับ api/crm/image, process/docpicture/image, process/itempicture/image)
  4. อ่านไฟล์จาก MinIO ไม่สำเร็จ ห้ามเขียนรูป placeholder ทับ key เดิม (แสดง placeholder แต่ไม่บันทึกทับ)

ตัวอย่าง

import { safeReturnPath } from "@/lib/safe-return";
router.replace(safeReturnPath(new URLSearchParams(location.search).get("returnTo")));

กฎความลับทั่วไป (ห้ามใส่รหัส/Token ใน repo, ใช้ env) อยู่ที่ กฎขอบเขตงานและกฎเหล็กประจำ repo (Repo Scope & Iron Rules)

ข้อควรระวังการตรวจ backend

  • เครื่องที่มีแค่ Go 1.24 (go.mod ต้องการเวอร์ชันใหม่กว่า) ให้ type-check ในสำเนา scratch ที่ใส่ replace ให้ dependency ห้ามแก้ go.mod จริง แล้วรัน go vet ./... && go test ./internal/...
  • Unit test frontend ใช้ node --experimental-strip-types --test กับไฟล์ *.test.ts
  • ปัญหาที่ยังเปิดอยู่ที่ต้องตัดสินใจเชิงออกแบบ ห้ามแก้เองโดยไม่ถามลุงจืด (KMS ระบุสถานะปัจจุบันว่าไม่มีรายการค้าง)

ที่มา

  • .agents/skills/secure-api-data-integrity-checklist/SKILL.md Workflow A–B
  • docs/kms/KMS_SECURITY_DATA_INTEGRITY_LESSONS.md ตาราง S1–S5, B1, B2, B7

ดูเพิ่ม

สารบัญมาตรฐานและกฎเหล็กของ SML Ai Account (Standards Overview) · บทเรียนบั๊กสิทธิ์และการข้ามฐานข้อมูล (Auth Bypass, Cross-Tenant, Demo Read-Only, Screen Rights) · ความถูกต้องของข้อมูล: เงิน ภาษี Transaction เดียว และ Rebuild (Data Integrity, big.Rat, ExecBatch) · Google Login, Multi-Tenant PDPA และหน้าเลือกโมดูลหลัง Login (Google OAuth, Tenant Isolation, Home Hub) · ภาพรวม Backend Go ของ SML Ai Account (Backend overview)