Skip to content

7.1.4 · Scorecard — meeting-vtrc-api (V1 depth)

branch: prod-meeting-vtrc-api (180 tracked files)

บทสรุป debt จาก 01 Repository map, 02 Core pipeline, และ 03 Persistence & integrations

อย่าคัดลอกค่า secret / password / PII จริงลง ticket — อ้างชื่อตัวแปรและ file:line


Severity legend

Levelเกณฑ์Action
Criticalactive security / data-loss / availability riskปิด surface หรือแก้ก่อน feature ใหม่
Highcorrectness / auth hygiene hazardวางแผนใน sprint ถัดไป
Mediumfriction / hygieneแก้ตามโอกาส
Lowcleanup / consistencybacklog

Markdown list (สำหรับ raw readability):

  • Critical = active security/data-loss/availability risk
  • High = correctness/maintainability hazard, real impact
  • Medium = friction/hygiene, not yet dangerous

Severity: Critical

SEC-MTVTRC-01 · .env* commit เป็น plaintext พร้อม secret จริง

ที่: .envdev, .envuat, .envprod ใน root repo (ไม่ถูก .gitignore)

Evidence: ไฟล์มี API_KEY, SECRET_JWT_*, MAIL_PASS, และใน .envuat/.envdev มี PASSWORD / HOST ของ MSSQL จริง

Impact: ใครมีสิทธิ์อ่าน repo ได้ credential ครบสำหรับ DB, SMTP, JWT, apiKey — หมุน (rotate) ทุกตัวที่เคย commit แล้ว

Remediation:

  1. ย้าย secret ไป secret manager / CI env
  2. เพิ่ม .env* เข้า .gitignore เหลือแค่ .env.example ที่ไม่มีค่าจริง
  3. Purge จาก git history
  4. Rotate ทุก key ที่หลุด

SEC-MTVTRC-02 · GET /meeting/mockuser/one คืน PII จริงโดยไม่มี auth

ที่: routes/mockdelatlast/mockuser.ts:29-60 · mount app.ts:209-210

Evidence:

javascript
route.get("/one", async (req, res) => {
  const mockdata = {
    user: [
      {
        userID: 5596233,
        name: "วินัย ตั้งสกุล",
        email: "viani@redcross.or.th",
        tel: "0976564322",
      },
      // ...
    ],
  };
  res.status(200).send(mockdata);
});

Impact: unauthenticated PII disclosure; ข้อมูลฝังในซอร์สด้วย

Remediation: ลบ route จาก production build; ถ้าต้องมี mock ให้ใช้ข้อมูลสมมติ + ปิดด้วย feature flag / env ≠ prod


SEC-MTVTRC-05 · Global /meeting/secure/* auth ถูก comment — พื้นผิวเปิดเกือบทั้ง API

ที่: app.ts:104-120

Evidence: ก้อน app.all("/meeting/secure/*", checkBypassMiddleWare, checkApiKeyMiddleWare, …) ถูก comment ทั้งชุด; route ส่วนใหญ่ไม่ใส่ middleware เอง

Impact: สร้าง/ลบ/อนุมัติประชุม, ดึงรายชื่อ, สั่งส่งเมล (/corn-email), สร้าง PDF — เรียกได้โดยไม่มี Bearer/apiKey ถ้าระบบ expose path นี้

Remediation:

  1. เปิด global guard กลับ (หรือใส่ middleware ทีละ route อย่างน้อย mutation)
  2. ตรวจว่า checkBypassMiddleWare verify secret ถูกต้อง (ดู SEC-MTVTRC-06)
  3. Rate-limit + WAF ที่ reverse proxy

SEC-MTVTRC-06 · checkBypassMiddleWare ไม่ยิง vtrc และเรียก decodeJwt ผิดลายเซ็น

ที่: middleware/checkbypass.ts:38-78 · util/helper/jwtdecode.ts:12-19

Evidence:

  • สร้าง GraphQL query fetchEmployeeProfileForOtherService แต่ไม่ vtrcConnector.post
  • เรียก decodeJwt(token) ส่ง arg เดียว ในขณะที่ฟังก์ชันต้องการ (token, secert)

Impact: แม้จะเปิด global guard กลับ การตรวจ Bearer ผ่าน middleware นี้ก็ไม่น่าเชื่อถือ; path ที่ไม่มี Bearer ยัง next() ได้ (checkbypass.ts:28-30)

Remediation: ให้ mirror logic จาก checkbypasswhenredirect.ts (verify ด้วย SECRET_JWT_VTRC หรือ meeting secret + ตรวจ TokenMeeting); ปฏิเสธเมื่อไม่มี Bearer แทนการ next()


Severity: High

SEC-MTVTRC-03 · Health-check ส่งอีเมลจริงไปที่อยู่ส่วนบุคคล

ที่: routes/secure/meeting/health-check.ts:9-17 · mount app.ts:230-231

Hardcode address ในซอร์ส; ไม่มี auth; เรียก SMTP จริงทุกครั้ง

Remediation: เปลี่ยนเป็นเช็ค DB/authenticate หรือ SMTP connect โดยไม่ส่งเมล; ถ้าต้องทดสอบ ให้ auth + allowlist


SEC-MTVTRC-07 · Static /meeting/file และ ops hooks ไม่มี auth

Pathความเสี่ยงหลักฐาน
/meeting/file/*อ่านไฟล์แนบ/PDF ถ้าเดาชื่อได้app.ts:147; guard เก่า comment :90-103
/meeting/secure/corn-email/สั่งส่งเมล+push ทั้งคิวcorn-email.ts:10-14
/meeting/secure/meeting/sendsendMail(4403) hardcodemeeting.ts:73-81

Remediation: auth บน static (signed URL หรือ Bearer); ลบ/ปิด test hooks ใน prod


SEC-MTVTRC-08 · cdbConnector ฝัง api-key + Bearer JWT ในซอร์ส

ที่: util/axiosinterceptors/cdb.connector.ts:3-26

Impact: credential ถาวรใน git; ทุก environment ใช้ token ชุดเดียวกันตาม interceptor

Remediation: ย้ายเข้า env; rotate; อย่า commit ค่าจริง


CORR-MTVTRC-05 · Cron scheduleEmail() ไม่ถูกเรียกตอน boot

ที่: app.ts:4 require myCorn แต่ไม่เรียก scheduleEmail; นิยามอยู่ที่ corn-job/email.corn.ts:6-18

Impact: ถ้าไม่มี external scheduler ยิง /corn-email อีเมลเชิญจะไม่ออกอัตโนมัติ — availability ของ notification path

Remediation: เรียก myCorn.scheduleEmail() หลัง listen หรือยืนยัน cron ภายนอกแล้วลบ dead module ให้ตรงเอกสาร ops


CORR-MTVTRC-01 · refreshtoken.ts dead + ไม่ persist

ที่: routes/auth/refreshtoken.ts:5-19 — ไม่มี app.use ใน app.ts:148-231

createToken สร้าง uuid แล้ว comment ว่าควรเก็บ DB แต่ไม่เก็บ

Remediation: mount + persist หรือลบไฟล์


Severity: Medium

SEC-MTVTRC-04 · Tinify API key hardcode

ที่: lib/Uploadimage.class.ts:5 — call tinify.fromFile comment ที่ :29-30

Remediation: ลบ key / ย้าย env; rotate key เดิม


CORR-MTVTRC-02 · addNewMeeting ไม่มี transaction

ที่: routes/secure/meeting/meeting.ts:187-243

Insert Meeting → Guest → OtherGuest → Attached แยกกัน — fail กลางทาง = orphan meeting

Remediation: sequelize.transaction() ครอบทั้งชุด


CORR-MTVTRC-03 · Delegate ไม่ส่งอีเมล (call ถูก comment)

ที่: acceptancemeeting.ts:125

Remediation: uncomment + ทดสอบ หรือลบ dead sendMailToDelegate


CORR-MTVTRC-04 · ScheduleJob ดึงเฉพาะของ "วันนี้"

ที่: util/helper/corn.email.helper.ts:19-47

executeTime > TODAY_START AND < NOW — งานค้างข้ามวันหาย

Remediation: jobStatus=ACTIVE AND executeTime <= NOW โดยไม่ผูกวัน


CORR-MTVTRC-06 · emailTask mark DONE ก่อน push สำเร็จอย่างสมบูรณ์ / ไม่มี per-item try

ที่: corn.email.helper.ts:107-114

ลำดับ: sendMailDONEsendNoti — push fail แล้ว job ไม่ retry; exception ใน loop หยุดงานที่เหลือ

Remediation: try/catch ราย job; แยกสถานะ mail/push หรือ mark DONE หลังทั้งคู่สำเร็จ


CORR-MTVTRC-07 · OtherGuest.belongsTo(OtherGuest) น่าจะผิด

ที่: model/meeting/meeting.ts:531

Remediation: แก้เป็น belongsTo(Meeting) แล้ว regression test include queries


QUAL-MTVTRC-01 · Mongoose dependency + mongo.config ตายแล้ว

ที่: app.ts:8, package.json:42, util/mongo.config.ts:1-14

Remediation: ลบ dependency และไฟล์; อัปเดตเอกสารที่ไม่ให้บอกว่าเป็น Mongoose stack


QUAL-MTVTRC-02 · model/login.ts นอก domain

ที่: model/login.ts:1-122, db.config.ts:43

Remediation: ยืนยันกับทีมแล้วลบถ้าไม่ใช้


QUAL-MTVTRC-03 · emailsender.ts vs Emailsender.func.ts

ที่: util/helper/emailsender.ts, lib/Emailsender.func.ts

Remediation: เปลี่ยนชื่อให้ต่างชัด (เช่น emailQueue.ts / emailTransport.ts)


QUAL-MTVTRC-04 · สะกด corn แทน cron ทั้ง public path

ที่: corn-job/, routes/secure/meeting/corn-email.ts, app.ts:228-229

Remediation: deprecation ก่อนเปลี่ยน path; หรือคง path เดิมแล้วแก้ชื่อภายในอย่างเดียว


QUAL-MTVTRC-05 · Dockerfile มีอยู่แต่ npm start บังคับ NODE_ENV=dev

ที่: Dockerfile:1-28, package.json:7

เอกสารเก่าระบุว่าไม่มี Dockerfile — แก้แล้ว: มีไฟล์จริง

ความเสี่ยงที่เหลือ: container prod อาจโหลด logic แบบ NODE_ENV != production ใน Emailsender / redirect middleware (อ่าน .envdev)

Remediation: แยก script start:prod; ส่ง NODE_ENV ตาม ARG


QUAL-MTVTRC-06 · Swagger definition เป็น JSONPlaceholder placeholder

ที่: app.ts:50-71, apis glob ./routes//.ts (:75) น่าจะผิด

Remediation: อัปเดต OpenAPI ให้ตรง route จริง หรือถอด /docs จาก prod


QUAL-MTVTRC-07 · Import auth ใน PDF/attachment แต่ไม่ apply

ที่: createpdfinvitemeeting.ts:10-13,44, attacment.ts:7-10,29

Remediation: ใส่ middleware ใน route.post/get หรือลบ import ที่ทำให้เข้าใจผิด


OBS-MTVTRC-01 · Sentry DSN hardcode ร่วมทุก environment

ที่: app.ts:80, tracesSampleRate: 1.0 (:85)

Remediation: DSN จาก env; ลด sample rate ใน prod


OBS-MTVTRC-02 · bodyParser limit 500mb

ที่: app.ts:123

Impact: memory DoS ถ้าไม่มี limit ที่ proxy

Remediation: ลด limit ให้สมเหตุสมผล + limit ที่ nginx


ตารางสรุปเร็ว

IDระดับหัวข้อสั้น
SEC-MTVTRC-01Critical.env* secrets in git
SEC-MTVTRC-02Criticalmockuser PII leak
SEC-MTVTRC-05Criticalglobal auth commented out
SEC-MTVTRC-06Criticalcheckbypass broken verify
SEC-MTVTRC-03Highhealth-check sends real email
SEC-MTVTRC-07Highstatic files + ops hooks open
SEC-MTVTRC-08HighcdbConnector hardcode tokens
CORR-MTVTRC-05Highcron never started
CORR-MTVTRC-01Highrefreshtoken dead
SEC-MTVTRC-04MediumTinify key
CORR-MTVTRC-02..07Mediumtransaction, delegate mail, job window, DONE order, OtherGuest assoc
QUAL-MTVTRC-01..07Mediummongoose, login model, filenames, corn typo, Dockerfile/NODE_ENV, swagger, dead auth imports
OBS-MTVTRC-01..02MediumSentry DSN, 500mb body

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

  • Debug "อีเมลไม่ไป" → ดูแถว ScheduleJob (ACTIVE?) → ยืนยันว่ามีใครยิง /corn-email หรือ scheduleEmail จริง → แล้วค่อย SMTP/MAIL_PASS
  • Path มีคำว่า secure อย่าเชื่อ — เปิด app.ts + ไฟล์ route
  • Push ไม่ขึ้น → ดู sendNoti + VTRC_BASE_CONNECT + API_KEY_OUTBOUND_VTRC ฝั่ง vtrc-api mutation sendNotificationFrom3Party
  • db.login ไม่ใช่ user จริงของระบบประชุม — user มาจาก GraphQL employee directory
  • อย่าเปิด /meeting/mockuser/one ใน demo ที่บันทึก network

สิ่งที่ตรวจแล้ว / ยังไม่ตรวจ

ตรวจแล้วจากซอร์สบน prod-meeting-vtrc-apiยังไม่ตรวจ
Route mount ทั้งหมดใน app.tsพฤติกรรม runtime บน UAT/prod จริง
Model fields + associationsDDL จริงบน MSSQL / seed StatusResponse
sendNoti mutation shapeว่า vtrc-api รับ variables รูปแบบนี้สำเร็จหรือไม่
Dockerfile + composepipeline CI ที่ส่ง ENV build-arg
Auth commented + mock leakpenetration test เต็มรูปแบบ