# Laporan Audit Repository `scuto-store-api`

**Tanggal audit:** 21 Agustus 2026  
**Cakupan:** source code, konfigurasi Vendure, plugin, migrasi database, deployment, dependency, dan quality checks  
**Metode:** pemeriksaan statis repository, pemeriksaan status Git, `npm audit --omit=dev`, dan `npm run build`

## Ringkasan Eksekutif

Repository berhasil dikompilasi, tetapi belum aman untuk digunakan di lingkungan production tanpa perbaikan lebih lanjut.

Temuan paling penting adalah:

1. File `.env` yang mengandung secret masih dilacak oleh Git.
2. Kegagalan RajaOngkir diperlakukan sebagai ongkir gratis.
3. Sinkronisasi schema database otomatis masih aktif.
4. Migrasi database belum mencakup seluruh custom field.
5. Implementasi webhook DOKU belum memiliki validasi dan proteksi replay yang memadai.
6. Beberapa fitur development tetap aktif pada konfigurasi production.
7. Dependency production memiliki 12 advisory keamanan.

### Rekapitulasi Temuan

| Tingkat risiko | Jumlah |
| --- | ---: |
| Kritis | 4 |
| Tinggi | 5 |
| Menengah | 7 |
| Kualitas/maintainability | 5 kelompok |

## Temuan Kritis

### AUDIT-001 — File `.env` dilacak oleh Git

**Risiko:** Kritis  
**Area:** Secret management

Pemeriksaan `git ls-files` menunjukkan bahwa `.env` telah masuk ke index Git. Sementara itu, `.gitignore` belum mengecualikan file tersebut.

File `.env` memuat nama environment variable sensitif seperti:

- `COOKIE_SECRET`
- `SUPERADMIN_USERNAME`
- `SUPERADMIN_PASSWORD`
- `DB_USERNAME`
- `DB_PASSWORD`
- `RAJA_ONGKIR_API_KEY`

Nilai secret tidak dicantumkan dalam laporan ini.

**Bukti:**

- `.gitignore`, baris 1–6
- Hasil `git ls-files .env`

**Dampak:** Secret berpotensi ditemukan di riwayat Git, clone repository, CI artifact, atau image container.

**Rekomendasi:**

1. Tambahkan `.env` ke `.gitignore`.
2. Hapus `.env` dari index Git tanpa menghapus file lokal.
3. Buat `.env.example` yang hanya berisi nama variable dan nilai contoh yang aman.
4. Rotasi seluruh credential yang pernah tersimpan dalam commit.
5. Jika repository pernah dibagikan, pertimbangkan pembersihan secret dari riwayat Git.

### AUDIT-002 — Kegagalan kalkulasi RajaOngkir menghasilkan ongkir gratis

**Risiko:** Kritis  
**Area:** Shipping/pricing integrity

Calculator RajaOngkir mengembalikan `price: 0` pada beberapa kondisi kegagalan:

- Destination ID tidak tersedia.
- API key tidak tersedia.
- API RajaOngkir mengembalikan error.
- Layanan kurir tidak ditemukan.
- Terjadi exception atau kegagalan jaringan.

**Bukti:**

- `src/plugins/raja-ongkir/raja-ongkir.calculator.ts`, baris 34–40
- `src/plugins/raja-ongkir/raja-ongkir.calculator.ts`, baris 55–59
- `src/plugins/raja-ongkir/raja-ongkir.calculator.ts`, baris 78–85
- `src/plugins/raja-ongkir/raja-ongkir.calculator.ts`, baris 109–111

**Dampak:** Pelanggan dapat menerima opsi pengiriman gratis ketika alamat tidak valid atau layanan eksternal gagal.

**Rekomendasi:** Terapkan perilaku fail-closed. Calculator harus membuat metode shipping tidak eligible atau menghentikan kalkulasi dengan error terkontrol, bukan mengembalikan harga nol.

### AUDIT-003 — `synchronize: true` aktif untuk seluruh environment

**Risiko:** Kritis  
**Area:** Database safety

Konfigurasi database selalu menggunakan `synchronize: true`, termasuk ketika aplikasi berjalan di production.

**Bukti:** `src/vendure-config.ts`, baris 48–59.

**Dampak:** Struktur database production dapat berubah otomatis saat aplikasi dimulai, dengan risiko schema drift atau kehilangan data.

**Rekomendasi:** Gunakan migration sebagai satu-satunya mekanisme perubahan schema. Set `synchronize: false`, atau paling tidak batasi hanya untuk development dengan `synchronize: IS_DEV`.

### AUDIT-004 — Migrasi tidak mencakup seluruh custom field

**Risiko:** Kritis  
**Area:** Database consistency

Migrasi yang tersedia hanya menambahkan:

- `Product.isBestSelling`
- `Product.isNew`
- `ProductVariant.originalPrice`

Konfigurasi juga mendefinisikan:

- `ProductVariant.weight`
- `GlobalSettings.heroBanners`
- `GlobalSettings.nanoCeramicBanner`
- `GlobalSettings.appPromoBanner`

**Bukti:**

- `src/migrations/1786344792862-add-custom-fields.ts`, baris 5–14
- `src/vendure-config.ts`, baris 66–83

**Dampak:** Setelah `synchronize` dimatikan, deployment baru atau database hasil migrasi tidak akan memiliki schema lengkap.

**Rekomendasi:** Buat migration Vendure/TypeORM baru untuk seluruh custom field dan tabel relasi yang belum tercakup. Uji migration `up` dan `down` pada database disposable.

## Temuan Risiko Tinggi

### AUDIT-005 — Verifikasi webhook DOKU belum memadai

**Risiko:** Tinggi  
**Area:** Payment security

Beberapa kelemahan ditemukan pada webhook DOKU:

- Signature dihitung dari `JSON.stringify(req.body)`, bukan raw request body.
- Signature dibandingkan menggunakan operator `!==`, bukan constant-time comparison.
- Tidak ada validasi usia `request-timestamp`.
- Tidak ada proteksi replay berdasarkan `request-id`.
- Nominal, currency, dan identitas transaksi tidak diverifikasi sebelum settlement.
- Order dicari hanya berdasarkan invoice/order code.
- Error response mengembalikan stack trace kepada pemanggil.

**Bukti:** `src/plugins/doku-payment/doku-webhook.controller.ts`, baris 14–63.

**Dampak:** Webhook valid dapat gagal karena perubahan serialisasi. Selain itu, notification lama dapat dikirim ulang dan data pembayaran yang tidak lengkap dapat menyebabkan settlement yang salah.

**Rekomendasi:**

1. Pertahankan raw request body untuk kalkulasi digest.
2. Gunakan `crypto.timingSafeEqual()` setelah memvalidasi panjang buffer.
3. Tolak timestamp yang berada di luar toleransi waktu.
4. Simpan dan deduplikasi `request-id` atau transaction ID.
5. Verifikasi client ID, invoice, amount, currency, transaction ID, dan status.
6. Pastikan proses idempotent.
7. Jangan mengembalikan stack trace pada response production.

### AUDIT-006 — Fitur development selalu aktif

**Risiko:** Tinggi  
**Area:** Production hardening

Konfigurasi selalu memasang:

- `GraphiqlPlugin`
- `dummyPaymentHandler`
- `EmailPlugin` dengan `devMode: true`
- Endpoint mailbox development

**Bukti:** `src/vendure-config.ts`, baris 61–63 dan 85–114.

**Dampak:** Production dapat mengekspos alat introspeksi, mailbox development, atau jalur pembayaran dummy.

**Rekomendasi:** Kondisikan seluruh fitur tersebut dengan `IS_DEV`. Jangan mendaftarkan dummy payment handler pada production.

### AUDIT-007 — Callback pembayaran DOKU masih menunjuk ke localhost

**Risiko:** Tinggi  
**Area:** Payment flow

Callback URL ditulis langsung sebagai `http://localhost:3001/checkout/success`.

**Bukti:** `src/plugins/doku-payment/doku-payment.handler.ts`, baris 21–35.

**Dampak:** Setelah pembayaran production, pengguna akan diarahkan ke alamat lokal yang tidak dapat diakses.

**Rekomendasi:** Baca base URL storefront dari environment di `vendure-config.ts`, lalu teruskan melalui `DokuPaymentPlugin.init()` options.

### AUDIT-008 — Docker build dapat menyertakan secret dan file yang tidak diperlukan

**Risiko:** Tinggi  
**Area:** Container security

Repository belum mempunyai `.dockerignore`, sementara Dockerfile menjalankan `COPY . .`.

**Bukti:** `Dockerfile`, baris 1–8.

**Dampak:** `.env`, `.git`, source yang tidak diperlukan, cache, dan file lokal dapat masuk ke build context atau image.

**Rekomendasi:** Tambahkan `.dockerignore` untuk sedikitnya `.env*`, `.git`, `node_modules`, `dist`, `.vendure`, test output, log, dan file editor. Gunakan multi-stage build dan jalankan runtime sebagai non-root user.

### AUDIT-009 — Dependency production memiliki advisory keamanan

**Risiko:** Tinggi  
**Area:** Supply-chain security

`npm audit --omit=dev` melaporkan 12 vulnerability:

- 6 high
- 6 moderate

Dependency terdampak mencakup:

- `sharp`
- `image-size`
- `lodash`
- `ws`
- `file-type`
- `uuid`
- Apollo Server dan dependency transitif Vendure

**Dampak:** Risiko meliputi denial of service, memory disclosure, prototype pollution, dan code injection pada jalur tertentu.

**Rekomendasi:** Jangan menjalankan `npm audit fix --force` secara langsung karena rekomendasi audit dapat melakukan downgrade breaking ke Vendure 2.x. Evaluasi upgrade Vendure dan dependency secara terkontrol, lalu jalankan regression test.

## Temuan Risiko Menengah

### AUDIT-010 — Endpoint pencarian RajaOngkir tidak dibatasi

**Risiko:** Menengah

Query Shop API dapat memanggil RajaOngkir tanpa batas minimum keyword, rate limit, caching, atau timeout.

**Bukti:** `src/plugins/raja-ongkir/raja-ongkir.resolver.ts`, baris 5–33.

**Dampak:** Endpoint dapat digunakan untuk menghabiskan kuota API atau membebani aplikasi.

**Rekomendasi:** Validasi panjang keyword, batasi hasil, tambahkan rate limiting, cache jangka pendek, dan timeout request.

### AUDIT-011 — Penanganan respons API eksternal lemah

**Risiko:** Menengah

Resolver dan calculator langsung memanggil `response.json()` tanpa pemeriksaan `response.ok`, content type, timeout, atau validasi schema. Data respons sebagian besar bertipe `any`.

**Bukti:**

- `src/plugins/raja-ongkir/raja-ongkir.resolver.ts`, baris 13–31
- `src/plugins/raja-ongkir/raja-ongkir.calculator.ts`, baris 67–106

**Rekomendasi:** Gunakan `AbortController`, periksa status HTTP, validasi response schema, definisikan interface, dan normalisasi error tanpa mengekspos detail upstream.

### AUDIT-012 — Respons penuh DOKU disimpan pada metadata

**Risiko:** Menengah

Payment handler menyimpan objek `fullResponse` dari gateway ke metadata pembayaran.

**Bukti:** `src/plugins/doku-payment/doku-payment.handler.ts`, baris 65–74.

**Dampak:** Informasi pelanggan atau informasi internal gateway dapat tersimpan dan terekspos lebih luas dari yang diperlukan.

**Rekomendasi:** Simpan hanya field yang diperlukan melalui allowlist, seperti payment URL, external transaction ID, dan expiry time.

### AUDIT-013 — Asset URL production masih placeholder

**Risiko:** Menengah

Asset URL production masih menggunakan `https://www.my-shop.com/assets/`.

**Bukti:** `src/vendure-config.ts`, baris 89–96.

**Rekomendasi:** Ambil public asset URL dari environment dan validasi nilainya saat startup production.

### AUDIT-014 — Email production belum dikonfigurasi

**Risiko:** Menengah

Alamat pengirim masih menggunakan `example.com`, URL verifikasi/reset masih localhost, dan email plugin tetap berada pada dev mode.

**Bukti:** `src/vendure-config.ts`, baris 100–113.

**Dampak:** Email tidak terkirim ke pelanggan atau membawa link yang tidak dapat digunakan.

**Rekomendasi:** Konfigurasikan transport SMTP/provider, sender address, dan storefront URL melalui environment.

### AUDIT-015 — Environment webhook DOKU belum tersedia di `.env`

**Risiko:** Menengah

Webhook membutuhkan `DOKU_CLIENT_ID` dan `DOKU_SECRET_KEY`, tetapi kedua key tersebut tidak ditemukan dalam daftar variable `.env` saat audit.

**Bukti:**

- `src/plugins/doku-payment/doku-webhook.controller.ts`, baris 17–24
- Pemeriksaan nama variable `.env`

**Dampak:** Webhook akan mengembalikan HTTP 401 kecuali nilai disuntikkan oleh environment deployment lain.

**Rekomendasi:** Dokumentasikan kedua variable pada `.env.example`, validasi saat startup, dan gunakan sumber konfigurasi yang sama dengan payment handler.

### AUDIT-016 — Credential dan service development diekspos oleh Docker Compose

**Risiko:** Menengah

Docker Compose menyimpan password database dan Typesense API key secara hard-coded. Port database, Redis, Typesense, dan Elasticsearch diekspos ke host. Elasticsearch juga menonaktifkan security.

**Bukti:** `docker-compose.yml`, baris 13–107.

**Dampak:** Jika Compose digunakan pada host publik atau jaringan yang tidak dipercaya, service dapat diakses menggunakan credential yang diketahui.

**Rekomendasi:** Tegaskan bahwa file hanya untuk local development, gunakan environment interpolation, hindari pemakaian user root, dan bind port ke loopback jika akses eksternal tidak dibutuhkan.

## Kualitas dan Maintainability

### Tidak ada automated test

Tidak ditemukan unit test, integration test, atau end-to-end test. `package.json` juga tidak mempunyai script `test`.

Test prioritas yang disarankan:

1. Signature webhook valid dan invalid.
2. Replay webhook dan idempotency.
3. Ketidaksesuaian nominal pembayaran.
4. RajaOngkir success, timeout, API error, dan layanan kosong.
5. Kalkulasi berat beberapa order line.
6. Migration `up` dan `down`.
7. Pemisahan konfigurasi development dan production.

### Tidak ada lint dan formatting checks

`package.json` belum menyediakan script lint atau format. Hal ini terlihat dari formatting yang tidak konsisten, penggunaan `any`, import tidak terpakai, dan return type yang tidak eksplisit.

### Bootstrap error handling terbatas

`src/index.ts` dan `src/index-worker.ts` hanya mencetak error menggunakan `console.log`. Sebaiknya gunakan logger dan pastikan proses berhenti dengan exit code non-zero ketika bootstrap gagal.

### Dokumentasi masih berupa scaffold

README belum menjelaskan:

- Konfigurasi DOKU.
- Konfigurasi RajaOngkir.
- Daftar environment variable wajib.
- URL webhook yang harus didaftarkan.
- Prosedur migration dan deployment.
- Perbedaan konfigurasi development dan production.

## Hasil Validasi

### Build

Perintah:

```text
npm run build
```

Hasil:

- Dashboard Vite: berhasil.
- Server TypeScript: berhasil.
- Worker TypeScript: berhasil.
- Exit code: `0`.

### Dependency Audit

Perintah:

```text
npm audit --omit=dev
```

Hasil: gagal dengan exit code `1` karena terdapat 12 advisory, terdiri dari 6 high dan 6 moderate.

### Test

Tidak dapat dijalankan karena belum terdapat test suite atau script `test`.

### Kondisi Working Tree

Sebelum laporan dibuat, working tree telah memiliki perubahan pada:

- `src/vendure-config.ts`
- `src/gql/graphql-env.d.ts`

Perubahan tersebut tidak dihapus atau ditimpa selama audit.

## Rencana Remediasi yang Disarankan

### Prioritas 0 — Segera

1. Hentikan tracking `.env` dan rotasi semua secret.
2. Ubah kalkulasi ongkir menjadi fail-closed.
3. Matikan `synchronize` di production.
4. Buat migration lengkap sebelum deployment berikutnya.

### Prioritas 1 — Sebelum Production

1. Harden webhook DOKU dan tambahkan idempotency.
2. Verifikasi nominal, currency, dan transaction ID sebelum settlement.
3. Pisahkan plugin dan handler development dari production.
4. Ganti callback, email URL, dan asset URL dengan konfigurasi environment.
5. Tambahkan `.dockerignore` dan multi-stage Docker build.
6. Tambahkan integration test untuk payment dan shipping.

### Prioritas 2 — Hardening Lanjutan

1. Upgrade dependency secara terkontrol.
2. Tambahkan rate limiting, caching, timeout, dan schema validation untuk RajaOngkir.
3. Tambahkan lint, formatting, dan pemeriksaan CI.
4. Lengkapi README dan runbook deployment.
5. Perkuat konfigurasi Docker Compose untuk mencegah penggunaan tidak aman.

## Kesimpulan

Secara struktur, repository telah mengikuti pola plugin Vendure dan berhasil dibangun. Namun, payment, shipping, secret management, dan database migration masih mempunyai risiko yang dapat berdampak langsung pada keamanan transaksi dan integritas biaya pesanan.

Status yang disarankan: **belum siap production** sampai seluruh temuan kritis dan tinggi ditangani serta dilindungi oleh automated test.
