fix: default LOG_EXPOSE_API to closed (false) when unset

parseEnable()'s generic fallback (return true for an unset var) is correct
for the payment-method toggles it's shared with, but wrong for a security
gate: LOG_EXPOSE_API controlled whether /api/logs*, /openapi.json, and /docs
serve anything at all, and previously defaulted to true (open) when the env
var was absent, contradicting what .env.example already documented as the
safe default. Now explicitly defaults to 'false'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Tengku Achmad 2026-08-03 23:06:36 +07:00
parent 860a636a2c
commit 0936b0e84f
3 changed files with 7 additions and 6 deletions

View File

@ -39,9 +39,8 @@ LOG_TO_CONSOLE=true
# Max number of recent log entries kept in memory for GET /api/logs
LOG_BUFFER_SIZE=1000
# Exposes GET /api/logs, /api/logs/files, /api/logs/files/:filename, /api/logs/view.
# WARNING: defaults to true if unset — set to false unless you also configure
# LOG_BASIC_AUTH_USER/PASS below.
# Exposes GET /api/logs, /api/logs/files, /api/logs/files/:filename, /api/logs/view,
# /openapi.json, /docs. Defaults to false (closed) when unset — opt in explicitly.
LOG_EXPOSE_API=false
# Required in production to protect the log endpoints (HTTP Basic Auth, browser-native prompt).
# Leaving these empty only works in non-production NODE_ENV.

View File

@ -79,7 +79,7 @@ ERP_ENABLE_NOTIF=true
### Logging & Admin Access
```env
LOG_LEVEL=info
LOG_EXPOSE_API=false # WARNING: default true kalau tidak di-set
LOG_EXPOSE_API=false # default sudah tertutup (false) kalau tidak di-set
LOG_BASIC_AUTH_USER= # wajib diisi di production untuk lindungi /api/logs* dan /docs
LOG_BASIC_AUTH_PASS=
LOG_RETENTION_DAYS=30
@ -260,7 +260,7 @@ Paling nyaman dibaca lewat `GET /api/logs/view` (filter level, cari teks, klik n
1. **Signature Verification** — webhook, token payment link, notifikasi ERP semuanya ditandatangani/diverifikasi.
2. **Idempotency** — cegah duplikasi order/notifikasi ERP; state disimpan konsisten pakai `order_id` yang sudah disanitasi (Midtrans-safe) di semua map internal (`activeOrders`, `notifiedOrders`, `orderRetryCount`, `orderMerchantId`), sehingga tidak ada mismatch walau `order_id` asli mengandung karakter seperti `:`.
3. **API Key Authentication**`X-API-KEY` untuk `/createtransaksi` dan `POST /api/payment-links`. Dev mode fallback (bypass) hanya aktif kalau `NODE_ENV !== 'production'` **dan** key belum di-set.
4. **HTTP Basic Auth untuk admin tooling**`/api/logs*`, `/openapi.json`, `/docs` dilindungi `LOG_BASIC_AUTH_USER`/`PASS`. **Wajib di-set di production** — endpoint ini terbuka penuh secara default (`LOG_EXPOSE_API` default `true`) kalau tidak dikonfigurasi.
4. **HTTP Basic Auth untuk admin tooling**`/api/logs*`, `/openapi.json`, `/docs` dilindungi `LOG_BASIC_AUTH_USER`/`PASS`, dan tertutup total secara default lewat `LOG_EXPOSE_API=false` (harus di-set eksplisit `true` untuk mengaktifkan). **Wajib set `LOG_BASIC_AUTH_USER`/`PASS` di production** sebelum mengaktifkan `LOG_EXPOSE_API`, karena gerbang Basic Auth-nya sendiri bergantung pada `NODE_ENV=production` untuk benar-benar dipaksa (lihat poin 8).
5. **Rate Limiting**`/createtransaksi`, `/api/payment-links`, `/api/payments/charge`, `/api/payments/snap/token` dibatasi per-IP (`RATE_LIMIT_MAX` request per `RATE_LIMIT_WINDOW_MS`) untuk mencegah bot abuse / card-testing pada endpoint yang memang harus tetap bisa diakses langsung dari browser tanpa API key.
6. **CORS** — dibatasi lewat `CORS_ALLOWED_ORIGINS` (comma-separated). Kosong = izinkan semua origin (default dev).
7. **Payload Masking** — field sensitif (card number, CVV, token, server key) otomatis disamarkan di log.

View File

@ -214,7 +214,9 @@ const notifiedOrders = watchedSet(PERSISTED_STATE.notifiedOrders)
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLowerCase()
const levelOrder = { debug: 0, info: 1, warn: 2, error: 3 }
const LOG_EXPOSE_API = parseEnable(process.env.LOG_EXPOSE_API)
// Secure-by-default: unlike the other parseEnable() flags below (which default to
// enabled), an unset LOG_EXPOSE_API must default to CLOSED, not open.
const LOG_EXPOSE_API = parseEnable(process.env.LOG_EXPOSE_API ?? 'false')
const LOG_BUFFER_SIZE = parseInt(process.env.LOG_BUFFER_SIZE || '1000', 10)
const LOG_TO_FILE = parseEnable(process.env.LOG_TO_FILE ?? 'true') // Default enabled
const LOG_TO_CONSOLE = parseEnable(process.env.LOG_TO_CONSOLE ?? 'false') // Default disabled