Skip to content

3.10 · Scorecard + known bugs

บทสุดท้ายของ Volume 3 — รวมสิ่งที่เรียนรู้ทั้งหมดจาก 3.1–3.9 ไว้เป็นรายการ severity และ scorecard ที่ใช้ตัดสินใจได้

ไม่ใช่รายการ bug ครบถ้วน — เป็น "สิ่งที่ต้องรู้ก่อนแตะ vtrc-api" รายละเอียดเชิงลึกจะอยู่ใน Volume 9 (Security), Volume 14 (Tech Debt) และ Volume 15 (Modernization)


Severity ranking — ทั้งหมดที่พบใน Volume 3

รายการนี้ครอบคลุมเฉพาะปัญหาที่อยู่ใน vtrc-api repo เท่านั้น ปัญหา cross-service อยู่ใน Volume 2.8

Severity: Critical

V3-S1 · execSync('qpdf --encrypt ...') — command injection

ที่ — vtrc-api/api/src/routes.js:73, 120, 227, 277, 355 (5 endpoints)

password ที่ interpolate เข้า shell มาจาก user (passwordPDF, identityCard) — ถ้าค่ามี shell metacharacter ก็ execute ได้

รายละเอียดอยู่ใน บท 3.7

V3-S2 · JWT_SECRET default 'secret'

ที่ — vtrc-api/api/src/config.js:12

ถ้า env ไม่ตั้ง → ใครก็ forge JWT ได้เพราะ secret เป็น public knowledge

รายละเอียดใน บท 3.3

V3-S3 · signInByPass ที่ centralize รับ user lookup จาก vtrc-api

ที่ — flow ผ่าน vtrc-api/api/src/centralize/controllers/auth.js → cu-central-api มี bypass path

vtrc-api ส่ง empCode ไปขอ JWT โดยไม่ต้อง verify password ที่ cu-central — ถ้า vtrc-api resolver ไหนเรียก path นี้โดยไม่ได้ intend ก็ mint token ได้

รายละเอียดใน บท 3.4

V3-S4 · signInWithUserAndPassword auth mismatch

ที่ — vtrc-api/api/src/lib/repositories/session/session.js#checkSession + checkMenuAuth

bug ใน RBAC check ที่ใช้ getCurrentRoleUser ที่ดึง role จาก context ผิดตัว ทำให้ user อาจได้สิทธิ์เกินจริง หรือเห็น menu ของคนอื่น

รายละเอียดใน บท 3.3


Severity: High

V3-H1 · transaction: t ผิดตำแหน่ง — transaction ไม่ทำงาน

ที่ — กระจายอยู่ทั่ว lib/controllers/ (~20+ function)

pattern ที่ผิด

js
await Model.create({ ... }, { transaction: t })   // ✓ ถูก
await Model.create({ ... }, { transaction: t })   // ✗ แท้จริงแล้ว argument ที่ 2 คือ options
                                                  //   แต่หลายที่ส่ง transaction เป็น arg ที่ 3

ผล — หลาย flow ที่คิดว่า "atomic" แท้จริงแล้วไม่ใช่ ถ้า query ที่ 2 fail query ที่ 1 ยัง commit

รายละเอียดใน บท 3.5

V3-H2 · firebasePushNotification hard-coded device token + PII log

ที่ — vtrc-api/api/src/lib/firebase/firebasePushNotification.js:41, 44, 47, 80

  • FCM token ของ device ทดสอบติดอยู่ใน source (commit leak)
  • console.log device token / setData ในทุกครั้งที่ส่ง → PII ใน stdout

รายละเอียดใน บท 3.9

V3-H3 · CORS ไม่ทำงานตามที่ตั้งใจ

ที่ — vtrc-api/api/src/index.js:76-91

  • key corss (typo) ส่งเข้า applyMiddleware แต่ Apollo รับ cors → config ถูก discard
  • ต่อมา app.use(cors()) ใช้ default = origin * เปิดหมด

ผล — allowlist 5 origins ที่ตั้งใจไว้ไม่ทำงานจริง

รายละเอียดใน บท 3.9

V3-H4 · clouse.js limit cap bug

ที่ — vtrc-api/api/src/lib/clouse.js:21-36

let limit ไม่มีค่า → if (limit > 50) เป็น undefined > 50false เสมอ

ผล — client ขอ limit: 10000 ได้ (potential DoS ที่ GraphQL query)

V3-H5 · statLog cron — SQL injection ผ่าน string concat

ที่ — vtrc-api/api/src/lib/controllers/statLog/statLog.js:22-28, 99-104, 175-180

ค่า param จาก log service ถูก concat เข้า SQL string โดยตรง ใน 3 block query + insert 6 จุด ถ้า log service ถูก compromise → SQL injection ที่ db2 (vtrc-centralize schema)

รายละเอียดใน บท 3.8


Severity: Medium

V3-M1 · /webdeployment ไม่มี auth — รัน shell binary ได้

ที่ — vtrc-api/api/src/routes.js (execSync binary vtrc-update-uat-loadtest)

endpoint ที่ trigger deployment ไม่มี auth ไม่มี parameter check

V3-M2 · updateDelegatesExpire — fire-and-forget ไม่มี await ไม่มี log

ที่ — vtrc-api/api/src/lib/controllers/fund/fund.js:926-928

function ไม่ async ไม่ return promise ถ้า query fail จะกลายเป็น unhandledRejection ไม่มีใครเห็น debug ยาก

รายละเอียดใน บท 3.8

V3-M3 · Redis wrapper มี typo crash ที่ tr

ที่ — vtrc-api/api/src/lib/controllers/redis.controller.js:16

getCacheData มี tr เฉยๆ ใน body — ถ้า revive cache caller จะ crash ทันที (ReferenceError)

ปัจจุบันไม่ trigger เพราะ caller ทุกตัวถูก comment แต่เป็น landmine

V3-M4 · ชื่อ centralize/ ในโค้ด ≠ NestJS centralize-api

ที่ — vtrc-api/api/.env.example:36-37 + centralize/controllers/graphql.js

โฟลเดอร์และ env ชื่อ centralize / END_POINT_CENTRALIZE แต่ URL จริงชี้ GraphQL ของ cu-central-api (CLIENT_TARGET ใน .env.example อาจเป็น 8082; CODE_DEFAULT APP_PORT=8080) ไม่ใช่ NestJS centralize-api (port 3000)

รายละเอียดใน บท 3.4

V3-M5 · formatError มี console.log raw ในทุก error

ที่ — vtrc-api/api/src/index.js:61-64

production จะมี error object raw ปนอยู่ใน stdout ทำให้ grep ที่ structured log ยาก

V3-M6 · ไม่มี health check endpoint

ที่ — ไม่มีทั้งใน routes.js และ index.js

k8s ใช้ TCP probe แทน ทำให้ detect "app ready แต่ serve ไม่ได้" ไม่ได้

V3-M7 · ไม่มี graceful shutdown

ไม่มี SIGTERM handler ปิด HTTP server + DB + cron อย่างสะอาด ตอน rolling deploy request ถูกตัดกลางคัน


Severity: Low

V3-L1 · node-schedule in-process — ไม่ persist

ถ้า process restart ระหว่าง cron tick → job หาย ไม่มี retry ไม่มี dead-letter queue

V3-L2 · APP_NO leader election แบบ manual

ถ้า replica #1 ที่รัน fund cron down → cron หายทั้งระบบ ไม่มี takeover

V3-L3 · wlog synchronous I/O

fs.appendFileSync block event loop ทุก log line ใน workload หนัก response time พุ่ง

V3-L4 · GraphQLLogging plugin ไม่ log duration

debug "request ช้า" ต้องเทียบ timestamp ของ request กับ response เอง

V3-L5 · typo ที่ต้อง maintain

  • responError / getResposeMessage — function name ผิด แต่ใช้ทั่วระบบ
  • CONINCLE_USERNAMEvtrc-api/api/src/config.js
  • EncryptAll — naming inconsistent (All suffix)
  • clouse — ผิดจาก clause

vtrc-api Health Scorecard

วัด 10 dimension ที่สำคัญ ของ vtrc-api โดยเฉพาะ

  • 🟢 เขียว — ผ่านเกณฑ์ ไม่มีปัญหา
  • 🟡 เหลือง — ใช้ได้ แต่มีจุดอ่อน ต้องระวังตอนแก้
  • 🔴 แดง — มีปัญหาจริง ควร refactor

สรุประดับสี

Dimensionสี
Layering (typeDefs / resolvers / lib)🟡
Auth pipeline🔴
Persistence & transaction🔴
Centralize bridge resilience🔴
REST surface security🔴
Background jobs🟡
Logging & observability🔴
Caching strategy🔴
Config & secrets🔴
Test coverage🔴

คะแนนรวม: 1 🟢 / 2 🟡 / 7 🔴

อ่านตารางอย่างไร — มองสีก่อน (🟢🟡🔴) แล้วค่อยอ่านหมายเหตุ

ตารางหลัก

Dimensionสถานะหมายเหตุ
Layering (typeDefs / resolvers / lib)🟡มี 3 layer + REST bypass ที่ข้าม controller
Auth pipeline🔴JWT_SECRET default + RBAC bug + bypass path
Persistence & transaction🔴transaction arg ผิดที่ ~20+ แห่ง + syncModelToTable ที่ boot
Centralize bridge resilience🔴ไม่มี retry + timeout ไม่ config + circuit breaker
REST surface security🔴execSync qpdf + /webdeployment + /file/:id/download bypass
Background jobs🟡node-schedule in-process + APP_NO leader election แบบ manual
Logging & observability🔴sync wlog + ไม่มี metric/tracing + ไม่มี duration log
Caching strategy🔴Redis vestigial — caller ทุกตัว comment + typo crash ใน wrapper
Config & secrets🔴GCP SA key + device keys + JWT_SECRET ใน code/env default
Test coverage🔴ไม่มี unit test / integration test ใน repo

สิ่งที่ต้อง maintain ต่อไป (แม้จะผิด)

รายการต่อไปนี้ดูผิด/แปลกตา แต่แท้จริงแล้วถูกใช้อยู่ทั่ว vtrc-api และ "มีคนพึ่งพาอยู่" — ห้ามแก้ทีเดียวจบ

  • PEDDING_CANCLE enum — สะกดผิด แต่เก็บใน DB หลายตารางแล้ว ถ้าจะแก้ต้องเขียน migration ครบทุกตารางพร้อมกัน
  • APP_NO values 1, 2, 3, 5 (ไม่มี 4) — ใช้ใน cron leader election เปลี่ยนลำดับไม่ได้
  • KEY_PLASMA UUID — ฝังใน business rule ของ clouse.js เปลี่ยนต้อง sync ทุกที่
  • responError / getResposeMessage typo — ลามไปทั้ง codebase แก้ชื่อจะกระทบ import หลายร้อยจุด
  • corss typo ใน applyMiddleware — ถ้าแก้เป็น cors ต้องลบ app.use(cors()) ออกด้วย ไม่งั้นซ้อนกัน
  • signInByPass ของ centralize — ถ้าจะลบ ต้องเช็กทุก caller ใน vtrc-api ก่อน เพราะบาง flow อาจจะตั้งใจใช้
  • tr ใน redis.controller.js:16 — ถ้าจะ revive Redis cache ต้องแก้ก่อน ไม่งั้น first call crash

กฎเหล็ก — เจอแล้วไม่แน่ใจอย่าเดาเอง ให้ถามทีม HR/i3 ก่อนเสมอ


Modernization — ลำดับ ROI

ถ้าจะลงทุน refactor vtrc-api ลำดับความคุ้มค่า (จากประสบการณ์)

  1. แทนที่ execSync qpdf ด้วย library หรือ worker thread — risk:reward สูงสุด ลบ S1 ออกทันที
  2. เพิ่ม health check /healthz + readiness probe — investment เล็ก คุณค่ามหาศาล (k8s detect issue ได้)
  3. ตั้ง JWT_SECRET ที่ deploy และ remove default 'secret' — fix S2
  4. แยก REST API ออกเป็น service ต่างหาก — PDF/Excel generation เป็น workload คนละแบบ ไม่ควรอยู่ใน GraphQL server
  5. เพิ่ม transaction migration — goose, atlas หรือ Sequelize migration runner แทน syncModelToTable
  6. แก้ transaction: t arg position — sweep codebase และ fix ทุกจุด
  7. แทนที่ node-schedule ด้วย BullMQ + Redis lock — leader election ที่เชื่อถือได้
  8. เพิ่ม structured logging (pino) + tracing ID — debug ง่ายขึ้นมาก
  9. แยก secret ออกจาก code — Vault, AWS Secrets Manager, หรือ k8s Secret
  10. เพิ่ม unit test + integration test — coverage สูงสุดที่ controller + repository layer

ส่วน upgrade ใหญ่ Sequelize 5 → 6, Node 12 → 20 LTS, Apollo v2 → v4 — เป็นเรื่องใหญ่ ไม่คุ้มถ้าไม่มี feature ใหม่ที่ต้องการ


เคล็ดลับตอนทำงานจริง

ก่อนแก้ resolver ไหน — อ่าน 3 อย่างนี้ก่อน

  1. typeDef ของ operation นั้น — ดู @auth directive
  2. controller ที่ resolver เรียก — ดูว่ามี transaction: t pattern ไหม
  3. repository ที่ controller เรียก — ดูว่ามี centralize call ไหม

อย่า refactor พร้อมกันหลายอย่าง

กฎ — หนึ่ง change ต่อ PR เสมอ เพราะทุก change มี risk ถ้า refactor 3 อย่างพร้อมกันแล้วพัง → ไม่รู้ว่าสาเหตุมาจากอะไร

"ใช้งานได้" ≠ "ปลอดภัย"

vtrc-api ใช้งานมาหลายปีแล้ว แต่มี hole หลายจุด (ดู V3-S1 ถึง V3-S4) ก่อนจะ expose endpoint ใหม่สู่ internet → ผ่าน security review เสมอ

ถ้าเจอ "ทำไมไม่ crash ทั้งที่ควร crash"

อาจจะเป็นเพราะ

  • caller ของ function นั้นถูก comment หมดแล้ว (เหมือน Redis)
  • function นั้น throw ใน try/catch ที่ swallow (เหมือน setCacheData ที่ return false ตอน error)
  • transaction ไม่ทำงานจริง ทำให้ race condition ไม่ manifest ชัด

Debugging order ที่แนะนำ

  1. ดูที่ error_{yyyy}-{mm}-{dd}.log ใน container ก่อน — raw สุด
  2. ดูที่ request_*.log และ response_*.log เพื่อดู operation name + variables (redacted)
  3. ถ้าเป็น GraphQL → เปิด Playground ที่ /api/graphql ใน non-prod env เพื่อ reproduce
  4. ถ้าเป็น centralize call → ดูที่ centralize/controllers/*.js แล้ว trace ไปที่ END_POINT_CENTRALIZE

จบ Volume 3

ถ้าอ่านมาถึงตรงนี้ — คุณน่าจะเข้าใจ vtrc-api ในระดับที่สามารถ maintain, debug, และ plan modernization ได้

สิ่งที่ควรทำต่อ

  • อ่าน source จริง — คู่มือเป็นแผนที่ แต่ก่อนแก้ของจริงต้องอ่าน code ใน context ของตัวเอง
  • ทำ security audit — Volume 9 จะเจาะ V3-S1 ถึง V3-S4 ในระดับ deeper
  • เขียน runbook — Volume 13 จะมี incident response playbook

บทถัดไปขอแนะนำ

ขอบคุณที่อ่านมาถึงตรงนี้