From 5c84bf9abbbb794f61a09798a2f677b5d1005a98 Mon Sep 17 00:00:00 2001 From: Elisha Suleiman <112385548+lishmanTech@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:07:48 +0000 Subject: [PATCH 1/5] fix(frontend): inject NEXT_PUBLIC build args during Docker build --- .github/workflows/deploy-staging.yml | 10 ++++++++++ docker-compose.staging.yml | 2 +- frontend/Dockerfile | 20 +++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index a4f41042..59ee76bf 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -64,6 +64,16 @@ jobs: ghcr.io/${{ env.OWNER_LC }}/remitlend-frontend:staging-latest target: runner + build-args: | + NEXT_PUBLIC_API_URL=${{ vars.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_RPC_URL=${{ vars.NEXT_PUBLIC_RPC_URL }} + NEXT_PUBLIC_NETWORK_PASSPHRASE=${{ vars.NEXT_PUBLIC_NETWORK_PASSPHRASE }} + NEXT_PUBLIC_EXPLORER_URL=${{ vars.NEXT_PUBLIC_EXPLORER_URL }} + NEXT_PUBLIC_MANAGER_CONTRACT_ID=${{ vars.NEXT_PUBLIC_MANAGER_CONTRACT_ID }} + NEXT_PUBLIC_POOL_CONTRACT_ID=${{ vars.NEXT_PUBLIC_POOL_CONTRACT_ID }} + NEXT_PUBLIC_GOVERNANCE_CONTRACT_ID=${{ vars.NEXT_PUBLIC_GOVERNANCE_CONTRACT_ID }} + NEXT_PUBLIC_NFT_CONTRACT_ID=${{ vars.NEXT_PUBLIC_NFT_CONTRACT_ID }} + - name: Run Trivy vulnerability scanner (HIGH - warn) uses: aquasecurity/trivy-action@master with: diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index a8c6963e..9ecd1438 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -34,7 +34,7 @@ services: ports: - "3000:3000" environment: - - NEXT_PUBLIC_API_URL=http://localhost:3001 + - API_URL=http://backend:3001 depends_on: - backend diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 46cb1db0..8a25a13a 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -14,8 +14,26 @@ ENV PORT=3000 ENV HOSTNAME="0.0.0.0" CMD ["npm", "run", "dev"] -# Build Stage FROM base AS builder + +ARG NEXT_PUBLIC_API_URL +ARG NEXT_PUBLIC_RPC_URL +ARG NEXT_PUBLIC_NETWORK_PASSPHRASE +ARG NEXT_PUBLIC_EXPLORER_URL +ARG NEXT_PUBLIC_MANAGER_CONTRACT_ID +ARG NEXT_PUBLIC_POOL_CONTRACT_ID +ARG NEXT_PUBLIC_GOVERNANCE_CONTRACT_ID +ARG NEXT_PUBLIC_NFT_CONTRACT_ID + +ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL +ENV NEXT_PUBLIC_RPC_URL=$NEXT_PUBLIC_RPC_URL +ENV NEXT_PUBLIC_NETWORK_PASSPHRASE=$NEXT_PUBLIC_NETWORK_PASSPHRASE +ENV NEXT_PUBLIC_EXPLORER_URL=$NEXT_PUBLIC_EXPLORER_URL +ENV NEXT_PUBLIC_MANAGER_CONTRACT_ID=$NEXT_PUBLIC_MANAGER_CONTRACT_ID +ENV NEXT_PUBLIC_POOL_CONTRACT_ID=$NEXT_PUBLIC_POOL_CONTRACT_ID +ENV NEXT_PUBLIC_GOVERNANCE_CONTRACT_ID=$NEXT_PUBLIC_GOVERNANCE_CONTRACT_ID +ENV NEXT_PUBLIC_NFT_CONTRACT_ID=$NEXT_PUBLIC_NFT_CONTRACT_ID + RUN npm run build # Production Stage From f04e7b4b63a91872977cf00e5750444e39cdfd41 Mon Sep 17 00:00:00 2001 From: Elisha Suleiman <112385548+lishmanTech@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:13:03 +0000 Subject: [PATCH 2/5] ci: add Trivy vulnerability scanning for frontend image --- .github/workflows/deploy-staging.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 59ee76bf..027a6042 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -105,6 +105,32 @@ jobs: with: sarif_file: 'trivy-results.sarif' + - name: Run Trivy vulnerability scanner for frontend (HIGH - warn) + uses: aquasecurity/trivy-action@master + with: + image-ref: 'ghcr.io/${{ env.OWNER_LC }}/remitlend-frontend:staging-${{ github.sha }}' + format: 'table' + severity: 'HIGH' + exit-code: '0' + trivyignores: '.trivyignore' + + - name: Run Trivy vulnerability scanner for frontend (CRITICAL - fail) + uses: aquasecurity/trivy-action@master + with: + image-ref: 'ghcr.io/${{ env.OWNER_LC }}/remitlend-frontend:staging-${{ github.sha }}' + format: 'sarif' + output: 'frontend-trivy-results.sarif' + severity: 'CRITICAL' + exit-code: '1' + limit-severities-for-sarif: 'true' + trivyignores: '.trivyignore' + + - name: Upload frontend Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'frontend-trivy-results.sarif' + - name: Smoke-test the backend staging image run: | set -e From e53a1e5e4d9d494a9a4c339ea6af6c53ccbe4f1b Mon Sep 17 00:00:00 2001 From: Elisha Suleiman <112385548+lishmanTech@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:21:32 +0000 Subject: [PATCH 3/5] fix(settings): validate phone number for SMS notifications --- frontend/src/app/[locale]/settings/page.tsx | 83 ++++++++++++++------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/frontend/src/app/[locale]/settings/page.tsx b/frontend/src/app/[locale]/settings/page.tsx index f5db32b1..b4926b38 100644 --- a/frontend/src/app/[locale]/settings/page.tsx +++ b/frontend/src/app/[locale]/settings/page.tsx @@ -102,17 +102,15 @@ function Toggle({ @@ -222,11 +220,10 @@ function WalletSection() {

{network?.isSupported ? "Supported" : "Unsupported"} @@ -281,6 +278,7 @@ function NotificationsSection() { }); const [saved, setSaved] = useState(false); const [saveError, setSaveError] = useState(null); + const [phoneError, setPhoneError] = useState(null); useEffect(() => { if (!data) return; @@ -314,11 +312,30 @@ function NotificationsSection() { const handleSave = () => { setSaveError(null); + setPhoneError(null); + + const phone = prefs.phone.trim(); + + if (prefs.sms) { + if (!phone) { + setPhoneError("A phone number is required for SMS notifications."); + return; + } + + // Basic international phone validation + const phoneRegex = /^\+?[1-9]\d{7,14}$/; + + if (!phoneRegex.test(phone)) { + setPhoneError("Please enter a valid phone number."); + return; + } + } + updateNotificationPreferences.mutate( { emailEnabled: prefs.email, smsEnabled: prefs.sms, - phone: prefs.phone.trim() || null, + phone: phone || null, perTypeOverrides, }, { @@ -361,7 +378,10 @@ function NotificationsSection() { /> toggle("sms")} + onChange={() => { + setPhoneError(null); + toggle("sms"); + }} label="SMS Notifications" description="Requires a verified phone number" /> @@ -370,13 +390,21 @@ function NotificationsSection() { label="Phone number" placeholder="+14155552671" value={prefs.phone} - onChange={(e) => setPrefs((p) => ({ ...p, phone: e.target.value }))} + onChange={(e) => { + setPhoneError(null); + setPrefs((p) => ({ ...p, phone: e.target.value })); + }} helperText={ prefs.sms ? "A phone number is required for SMS notifications." : "Optional unless SMS notifications are enabled." } /> + {phoneError && ( +

+ {phoneError} +

+ )}
@@ -471,11 +499,10 @@ function SecuritySection() {
KYC Status {user?.kycVerified ? "Verified" : "Not Verified"} @@ -565,11 +592,10 @@ function DisplaySection() { @@ -653,11 +679,10 @@ export default function SettingsPage() {