Skip to content

4 · Scorecard — workflow-api

บทสรุป debt จาก 0103

อ้างอิง branch: uat


Severity legend

  • 🔴 Critical = active security/data-loss/availability risk
  • 🟡 High = correctness/maintainability hazard, real impact
  • 🟢 Medium = friction/hygiene, not yet dangerous
Levelเกณฑ์Action
🔴 Criticalactive security/data-loss/availability riskต้องแก้ก่อนเปิดกว้าง network
🟡 Highcorrectness/maintainability hazardวางแผนแก้ใน quarter นี้
🟢 Mediumfriction/hygieneแก้ตามโอกาส

Severity: Critical (🔴)

SEC-WORKFLOW-01 · ไม่ verify JWT + อนุมาน role จาก sourceDB

ที่:

  • workflow-api/src/main.ts (branch: uat):9-82 — ไม่มี useGlobalGuards
  • workflow-api/src/common/auth/jwt-actor.util.ts (branch: uat):1-4,33-47 — decode อย่างเดียว
  • workflow-api/src/modules/ipd/ipd-user.util.ts (branch: uat):20-65inferHospitalUserRole(sourceDB)
  • Swagger ยอมรับเองว่า signature ไม่ถูก verify — main.ts:42

Evidence:

typescript
export function decodeJwtPayloadUnsafe(token: string): Record<string, unknown> | null {
  const parts = token.split('.');
  // Buffer.from(parts[1], 'base64url') → JSON.parse — ไม่มี jwt.verify
}
typescript
function inferHospitalUserRole(sourceDB?: string): IpdRole {
  if (sourceDB === 'dbHRMI_RC_C_rep') return 'SOMDEJ';
  if (sourceDB === 'dbHRMI_RC_B_rep') return 'SABC';
  return 'CHULA';
}

Impact: ถ้าเรียก service ได้โดยตรง (ไม่ผ่าน gateway ที่ verify) ผู้โจมตีประกอบ Bearer ปลอมแล้ว:

  • สร้าง/ส่ง batch ในนาม CHULA
  • ตัดสินในบทบาท SABC / SOMDEJ
  • เขียน audit ด้วยชื่อปลอมใน health-checkup

Mitigation ที่ยัง UNVERIFIED: มี API gateway ชั้นนอก verify JWT หรือไม่ — ไม่มีหลักฐานใน repo นี้; แม้มี gateway ความเสี่ยงยังเหลือจาก traffic ภายใน network

การแก้ไข:

  1. เพิ่ม Passport JWT / Nest guard ที่ verify ด้วย secret/JWKS จริง
  2. อย่าอนุมาน role จาก sourceDB อย่างเดียว — ใช้ claim ที่ IdP ออกให้ หรือตารางสิทธิ์บน server
  3. ปิดการเข้าถึง service โดยตรงจากนอก trust boundary

SEC-WORKFLOW-02 · Secrets จริงใน .env.example

ที่: workflow-api/.env.example (branch: uat) — บรรทัดที่มี AI_ROUTER_API_KEY, DB_PMS_PASSWORD, DB_PASSWORD (และ host จริง)

Evidence: ไฟล์ที่ควรเป็น template มีค่าที่ใช้ได้จริง ไม่ใช่ changeme / *** — ถูก track ใน git

Impact: AI router abuse, เข้า PostgreSQL PMS, เข้า MSSQL ร่วมกับบริการอื่นที่ใช้ sa password ชุดเดียวกันในแพลตฟอร์ม

การแก้ไข:

  1. Rotate ทุก secret ที่เคยอยู่ในไฟล์
  2. แทนที่ใน .env.example ด้วย placeholder
  3. ตรวจ .env.uat / secrets อื่นใน git ด้วย
  4. ใช้ secret manager ใน deploy

เอกสารนี้ตั้งใจ ไม่ คัดลอกค่า secret ลงคู่มือ


Severity: High (🟡)

CORR-WORKFLOW-01 · Cross-service direct DB access

ที่: workflow-api/src/core/database/database.module.ts (branch: uat):51-199

Evidence: connections vtrc, hrmi, hrmi_nbc, benefit, benefit_write ชี้ database ของบริการอื่น

Impact: schema drift / lock / permission change ในทีม vtrc-api หรือ benefit-api ทำให้ IPD/health พังโดยไม่มี API versioning

การแก้ไข: ค่อย ๆ ย้ายไปเรียก API ของเจ้าของข้อมูล หรือทำ shared contract + migration coordination ที่บังคับ


CORR-WORKFLOW-02 · ค่าคงที่ enum ถูก interpolate ใน raw SQL

ที่: workflow-api/src/modules/ipd/ipd-batch.service.ts (branch: uat):1942-1948 (และจุดคล้ายกันที่ใส่ '${BATCH_STATUS...}' ใน SQL)

Evidence จาก submitBatch:

typescript
await em.query(
  `UPDATE ipdBillingBatch
   SET status='${BATCH_STATUS.PENDING_REVIEW}',
       updatedBy=@0,updatedAt=GETDATE()
   WHERE batchId=@1`,
  [user.fullName, batchId],
);

ปัจจุบันค่ามาจาก const ภายใน — ยังไม่ใช่ injection จาก user input แต่เป็น anti-pattern ที่ง่ายต่อการ regress

การแก้ไข: bind เป็น parameter ทุกค่า รวมถึง status


CORR-WORKFLOW-03 · DB_PMS_SYNCHRONIZE=true ในตัวอย่าง env

ที่: workflow-api/.env.example (branch: uat):32, factory ที่ database.module.ts:21-22

Impact: ถ้า UAT/prod ใช้ค่านี้ TypeORM จะ sync schema PostgreSQL ตาม entity ทุก boot

การแก้ไข: default false + migration ที่ review ได้


Severity: Medium (🟢)

QUAL-WORKFLOW-01 · AuditLog fallback เพราะ schema drift

ที่: audit-log.service.ts (~120-160, ~232-266) — catch invalid column แล้ว query ใหม่

การแก้ไข: รวม migration healthCheckLogs ให้ตรงทุก env แล้วลบ fallback


QUAL-WORKFLOW-02 · ชื่อ repo ≠ package ≠ เนื้อหา

  • Repo: workflow-api
  • Package: ai-pms-nest
  • เนื้อหา: PMS embedding + health checkup + IPD billing

ค้นหาและ onboarding ยาก


QUAL-WORKFLOW-03 · database/schema.sql ล้าสมัย

อธิบายตารางที่ไม่ตรง embedding/entities/* — ทำให้เข้าใจ schema ผิด


QUAL-WORKFLOW-04 · God service files

ไฟล์~บรรทัด
ipd-batch.service.ts2753
embedding.service.ts2527
health-program-config.service.ts1398

รวมหลายความรับผิดชอบ — แยกตาม sub-flow (บางส่วนแยกแล้ว เช่น ipd-document.service.ts)


QUAL-WORKFLOW-05 · Typo ใน JWT field fallback

ipd-user.util.ts:49,52 อ่าน souceDb (สะกดผิด) เป็น fallback ของ sourceDB — แสดงว่าเคยมี client ส่ง field ผิดและถูกดูดเข้ามาใน server


OBS-WORKFLOW-01 · Auth boundary พึ่ง gateway ที่มองไม่เห็นใน repo

เอกสารและ comment อ้าง "API gateway / auth layer" แต่ไม่มี config ของ gateway ใน repo — สถานะจริง UNVERIFIED


OBS-WORKFLOW-02 · Uploads / npy / logs ไม่มี volume ใน Dockerfile

Dockerfile สร้าง logs/ แต่ไม่ mount uploads — persistence ขึ้นกับ orchestrator ภายนอก


Health scorecard summary

Debt IDSeverityหมวดสรุปสั้น
SEC-WORKFLOW-01🔴 CriticalSecurityไม่ verify JWT; role จาก sourceDB
SEC-WORKFLOW-02🔴 CriticalSecuritysecrets ใน .env.example
CORR-WORKFLOW-01🟡 HighCorrectnessต่อ DB ของ service อื่นตรง ๆ
CORR-WORKFLOW-02🟡 HighCorrectnessenum interpolate ใน SQL
CORR-WORKFLOW-03🟡 HighCorrectnessPMS synchronize=true ในตัวอย่าง
QUAL-WORKFLOW-01🟢 MediumQualityaudit fallback / schema drift
QUAL-WORKFLOW-02🟢 MediumQualityชื่อ repo/package/domain ไม่ตรง
QUAL-WORKFLOW-03🟢 MediumQualityschema.sql เก่า
QUAL-WORKFLOW-04🟢 MediumQualityservice ไฟล์ใหญ่เกิน
QUAL-WORKFLOW-05🟢 MediumQualitytypo souceDb ใน fallback
OBS-WORKFLOW-01🟢 MediumObservabilitygateway auth UNVERIFIED
OBS-WORKFLOW-02🟢 MediumObservabilitydisk artifacts ไม่ชัดใน deploy

Modernization priority

  1. Rotate secrets + ล้าง .env.example (SEC-WORKFLOW-02)
  2. ใส่ JWT verify guard จริง + เลิกเชื่อ sourceDB อย่างเดียว (SEC-WORKFLOW-01)
  3. ปิด DB_PMS_SYNCHRONIZE ในทุก non-dev env (CORR-WORKFLOW-03)
  4. Parameterize SQL status ทั้งหมด (CORR-WORKFLOW-02)
  5. วางแผน API boundary แทน direct DB (CORR-WORKFLOW-01)
  6. แยก god services + sync schema.sql (QUAL-*)

UNVERIFIED

  • มี API gateway ภายนอกที่ verify JWT ก่อนถึง workflow-api หรือไม่
  • benefit read/write แยกโฮสต์จริงในทุก environment หรือไม่
  • coverage % ของ Jest suite
  • SMTP/notification ของ IPD ถูกเปิดใช้ใน production หรือไม่
  • ตาราง IPD อยู่บน database vtrc จริงในทุก env (อนุมานจาก connection name + raw SQL; ไม่มี entity file ใน repo ยืนยัน DDL)