4 · Scorecard — workflow-api
อ้างอิง 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 |
|---|---|---|
| 🔴 Critical | active security/data-loss/availability risk | ต้องแก้ก่อนเปิดกว้าง network |
| 🟡 High | correctness/maintainability hazard | วางแผนแก้ใน quarter นี้ |
| 🟢 Medium | friction/hygiene | แก้ตามโอกาส |
Severity: Critical (🔴)
SEC-WORKFLOW-01 · ไม่ verify JWT + อนุมาน role จาก sourceDB
ที่:
workflow-api/src/main.ts (branch: uat):9-82— ไม่มีuseGlobalGuardsworkflow-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-65—inferHospitalUserRole(sourceDB)- Swagger ยอมรับเองว่า signature ไม่ถูก verify —
main.ts:42
Evidence:
export function decodeJwtPayloadUnsafe(token: string): Record<string, unknown> | null {
const parts = token.split('.');
// Buffer.from(parts[1], 'base64url') → JSON.parse — ไม่มี jwt.verify
}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
การแก้ไข:
- เพิ่ม Passport JWT / Nest guard ที่ verify ด้วย secret/JWKS จริง
- อย่าอนุมาน role จาก
sourceDBอย่างเดียว — ใช้ claim ที่ IdP ออกให้ หรือตารางสิทธิ์บน server - ปิดการเข้าถึง 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 ชุดเดียวกันในแพลตฟอร์ม
การแก้ไข:
- Rotate ทุก secret ที่เคยอยู่ในไฟล์
- แทนที่ใน
.env.exampleด้วย placeholder - ตรวจ
.env.uat/ secrets อื่นใน git ด้วย - ใช้ 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:
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.ts | 2753 |
embedding.service.ts | 2527 |
health-program-config.service.ts | 1398 |
รวมหลายความรับผิดชอบ — แยกตาม 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 ID | Severity | หมวด | สรุปสั้น |
|---|---|---|---|
| SEC-WORKFLOW-01 | 🔴 Critical | Security | ไม่ verify JWT; role จาก sourceDB |
| SEC-WORKFLOW-02 | 🔴 Critical | Security | secrets ใน .env.example |
| CORR-WORKFLOW-01 | 🟡 High | Correctness | ต่อ DB ของ service อื่นตรง ๆ |
| CORR-WORKFLOW-02 | 🟡 High | Correctness | enum interpolate ใน SQL |
| CORR-WORKFLOW-03 | 🟡 High | Correctness | PMS synchronize=true ในตัวอย่าง |
| QUAL-WORKFLOW-01 | 🟢 Medium | Quality | audit fallback / schema drift |
| QUAL-WORKFLOW-02 | 🟢 Medium | Quality | ชื่อ repo/package/domain ไม่ตรง |
| QUAL-WORKFLOW-03 | 🟢 Medium | Quality | schema.sql เก่า |
| QUAL-WORKFLOW-04 | 🟢 Medium | Quality | service ไฟล์ใหญ่เกิน |
| QUAL-WORKFLOW-05 | 🟢 Medium | Quality | typo souceDb ใน fallback |
| OBS-WORKFLOW-01 | 🟢 Medium | Observability | gateway auth UNVERIFIED |
| OBS-WORKFLOW-02 | 🟢 Medium | Observability | disk artifacts ไม่ชัดใน deploy |
Modernization priority
- Rotate secrets + ล้าง
.env.example(SEC-WORKFLOW-02) - ใส่ JWT verify guard จริง + เลิกเชื่อ
sourceDBอย่างเดียว (SEC-WORKFLOW-01) - ปิด
DB_PMS_SYNCHRONIZEในทุก non-dev env (CORR-WORKFLOW-03) - Parameterize SQL status ทั้งหมด (CORR-WORKFLOW-02)
- วางแผน API boundary แทน direct DB (CORR-WORKFLOW-01)
- แยก god services + sync schema.sql (QUAL-*)
UNVERIFIED
- มี API gateway ภายนอกที่ verify JWT ก่อนถึง
workflow-apiหรือไม่ benefitread/write แยกโฮสต์จริงในทุก environment หรือไม่- coverage % ของ Jest suite
- SMTP/notification ของ IPD ถูกเปิดใช้ใน production หรือไม่
- ตาราง IPD อยู่บน database
vtrcจริงในทุก env (อนุมานจาก connection name + raw SQL; ไม่มี entity file ใน repo ยืนยัน DDL)