# Audit Report — POS System - AI **Date**: 2026-03-20 **Compiled from**: 15 specialist audit reports (CEO, CTO, Architect, API Architect, Backend, Frontend, Database Architect, DevOps, Security, Product Manager, QA, UX/UI Designer, Founding Engineer, Research Analyst, Technical Writer) **Platform**: GoodGo POS — 26 .NET 10 microservices, 5 frontend apps, MCP AI server **Branch**: master (d0211e5) --- ## Executive Summary GoodGo POS is a multi-vertical point-of-sale platform in **late-MVP / pre-production** phase. The architecture is exemplary — Clean Architecture + CQRS enforced across all 26 services, bilingual documentation (EN/VI), and a comprehensive observability stack. The core POS flow works end-to-end with 93% E2E test pass rate (38/41). The MCP AI server (12 tools) is a unique market differentiator no competitor offers. However, the platform has **severe security vulnerabilities** that must be addressed before any deployment: production database credentials are committed to git (CVSS 9.8), debug endpoints allow unauthenticated privilege escalation, JWT tokens are stored in localStorage, and the IdentityServer uses developer signing credentials in all environments. Test coverage sits at ~15% against a 70% target. Five services remain incomplete, and the entire marketing/CRM suite is demo-only with hardcoded fake data. **Overall Project Health Score: 5.5 / 10** | Category | Score | Key Factor | |----------|:-----:|------------| | Architecture & Patterns | 9/10 | Clean Architecture + CQRS exemplary across all services | | Code Quality | 8/10 | Strong DDD, bilingual docs, consistent patterns | | Security | 2/10 | Production credentials in git, debug endpoints, no CSP | | Test Coverage | 3/10 | ~15% coverage, 1/26 services has CI tests | | Infrastructure & DevOps | 6/10 | Good stack, K8s gaps, alerting non-functional | | Frontend & UX | 5/10 | Solid foundation, critical a11y failures, 2,316 inline styles | | Documentation | 7/10 | 102+ docs, but 96% OpenAPI specs missing | | Production Readiness | 4/10 | POS core ready, platform incomplete | | Mobile Readiness | 2/10 | iOS partial, Android template only | | Product Completeness | 5/10 | Core POS works, marketing/analytics are stubs | --- ## Critical Issues Issues that are **showstoppers** and must be fixed before any staging or production deployment. ### SEC-1: Production Database Credentials Committed to Git (CVSS 9.8) **Source**: Security Audit (CRIT-01) **Severity**: BLOCKER **Affected**: All 19 .NET microservices `appsettings.json` The Neon PostgreSQL production password (`npg_Ssfy6HKO0cXI`), Redis production password (`Velik@2026` with public IP `167.114.174.113`), SMTP credentials, and JWT signing secret are all hardcoded in `appsettings.json` files tracked by git. Anyone with repository read access can authenticate directly to the production database and Redis. All customer data, merchant data, orders, wallets, and PII are at risk. **Files**: `services/*/src/*/appsettings.json` (all 19 services) **Fix**: Rotate ALL credentials immediately (within 24 hours). Replace with environment variable placeholders. Purge from git history with `git filter-repo` or BFG Repo Cleaner. Adopt Kubernetes External Secrets Operator or HashiCorp Vault. --- ### SEC-2: Active JWT Bearer Token Committed in MCP Server .env **Source**: Security Audit (CRIT-02) **Severity**: BLOCKER **File**: `services/goodgo-mcp-server/.env:3` A live, signed JWT bearer token is committed to git. Any party with repository access can replay this token to authenticate as the associated service account. The MCP server has 12 operational tools that can read/write F&B data. **Fix**: Revoke token immediately. Remove `.env` from git tracking. Purge from history. --- ### SEC-3: Debug Endpoints Allow Unauthenticated Privilege Escalation **Source**: CTO (C1), Backend (C1), CEO (referenced) **Severity**: BLOCKER **File**: `services/merchant-service-net/src/MerchantService.API/Controllers/StaffController.cs:249-390` Five `[AllowAnonymous]` debug endpoints in production code: - `POST /api/v1/staff/debug/seed` — creates arbitrary staff data - `POST /api/v1/staff/debug/update-userid` — overwrites any staff's userId via reflection - `POST /api/v1/staff/debug/update-merchant` — overwrites any merchantId Any attacker can escalate privileges or tamper with merchant data. **Fix**: Delete these endpoints entirely or wrap with `if (env.IsDevelopment())` + `[Authorize(Roles = "SuperAdmin")]`. --- ### SEC-4: SQL Injection in StaffController **Source**: CTO (C2), Backend (C2) **Severity**: BLOCKER **File**: `services/merchant-service-net/src/MerchantService.API/Controllers/StaffController.cs:307, 367` String interpolation used directly in SQL queries. While current `Guid` types self-sanitize, any future refactor changing types to `string` creates an immediate injection vulnerability. ```csharp cmd.CommandText = $"UPDATE merchants SET user_id = '{userId}' WHERE id = '{merchantId}'"; ``` **Fix**: Use parameterized queries (`cmd.Parameters.AddWithValue()`). --- ### SEC-5: IdentityServer Using Developer Signing Credential in All Environments **Source**: Security Audit (CRIT-03) **Severity**: CRITICAL **File**: `services/iam-service-net/src/IamService.Infrastructure/DependencyInjection.cs:142` `AddDeveloperSigningCredential()` is called unconditionally — no environment check. The signing key is regenerated on every restart, invalidating all active sessions. No certificate-based signing exists for production. **Fix**: Use cert-based signing for non-development environments. Store RSA key in Vault or K8s TLS Secret. --- ### SEC-6: JWT Stored in localStorage (XSS Risk) **Source**: Frontend (CRIT-01), Security (WARN-01) **Severity**: CRITICAL **File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/Services/AuthService.cs:147-148` JWT tokens stored in `localStorage` are accessible to any JavaScript on the same origin. A single XSS vulnerability allows full token theft and account impersonation. **Fix**: Migrate to `httpOnly` cookies via the BFF server. Add CSP headers. --- ### SEC-7: Client Secret Hardcoded in Browser-Delivered WASM Code **Source**: Frontend (CRIT-02) **Severity**: CRITICAL **File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/Services/AuthService.cs:39-40` ```csharp private const string ClientId = "password-client"; private const string ClientSecret = "password-client-secret"; ``` Blazor WASM compiles to WebAssembly served to browsers. Constants are extractable via developer tools. **Fix**: Move OAuth2 token exchange to the BFF. The BFF holds secrets server-side. --- ### SEC-8: Deprecated Password Grant (OAuth2) Used **Source**: Frontend (CRIT-03) **Severity**: CRITICAL **File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/Services/AuthService.cs:136` Resource Owner Password Credentials grant is deprecated in OAuth 2.1. It cannot support MFA properly and exposes credentials to the client application. **Fix**: Migrate to Authorization Code Flow with PKCE. --- ### SEC-9: JWT Signature Validation Disabled in Development (4 Services) **Source**: Backend (C3), Founding Engineer (C4) **Severity**: CRITICAL **Files**: `merchant-service-net`, `ads-serving-service-net`, `ads-tracking-service-net`, `ads-billing-service-net` — all `Program.cs` ```csharp ValidateIssuerSigningKey = builder.Environment.IsDevelopment() ? false : true, ``` If `ASPNETCORE_ENVIRONMENT` is misconfigured on a server, all JWT signatures are bypassed. Any self-crafted token is accepted. **Fix**: Use a shared deterministic development signing key via environment variable. Always validate signatures. --- ### SEC-10: BFF CORS Wildcard + All 26 Services Use `AllowAnyOrigin()` **Source**: CTO (C3), Backend (W1), Founding Engineer (C1) **Severity**: CRITICAL **Files**: All 26 services `Program.cs`, BFF `Program.cs:74-78` Every service allows any origin to make cross-origin requests. Enables CSRF attacks and violates same-origin policy. **Fix**: Configure per-environment origin whitelists. Production: only `https://goodgo.vn`, `https://admin.goodgo.vn`. --- ### SEC-11: No Content-Security-Policy Header **Source**: Security (WARN-02), Frontend (IMP-02) **Severity**: CRITICAL **File**: `infra/traefik/dynamic/middlewares.yml` The `secure-headers` middleware is missing CSP, Referrer-Policy, and Permissions-Policy headers. Without CSP, the browser has no defense against XSS exploitation. **Fix**: Add CSP with `default-src 'self'; script-src 'self' 'wasm-unsafe-eval';` and related policies. --- ### INFRA-1: Production K8s Manifests Hardcode `:latest` Image Tag **Source**: DevOps (CRIT-1), CEO (referenced) **Severity**: CRITICAL **Files**: `deployments/production/kubernetes/*.yaml` All production Kubernetes deployments use `goodgo/*:latest`. Not reproducible, no rollback capability. **Fix**: Use commit SHA tags. Inject via Kustomize/Helm or pipeline sed-replace. --- ### INFRA-2: Alertmanager Not Configured — All Alert Rules Silent **Source**: DevOps (CRIT-2) **Severity**: CRITICAL **File**: `infra/observability/prometheus/prometheus.yml:29` Prometheus `alerting.alertmanagers.targets` is empty (`[]`). All defined alert rules (ServiceDown, HighErrorRate, HighLatencyP95) fire into the void. Production incidents will have zero notification. **Fix**: Deploy Alertmanager with Slack/PagerDuty receivers. Update targets to `['alertmanager:9093']`. --- ### INFRA-3: Health Check Endpoints Require Authentication (18/23 Services) **Source**: Founding Engineer (C2) **Severity**: CRITICAL **Files**: 18 services missing `.AllowAnonymous()` on health endpoints Kubernetes kubelet cannot authenticate. Liveness/readiness probes return 401 Unauthorized, causing pod restart loops. **Fix**: Add `.AllowAnonymous()` to all `MapHealthChecks()` calls. --- ### DATA-1: InventoryItem.CreatedAt Backing Field Not Mapped — Silent Data Loss **Source**: Database Architect (C-1) **Severity**: CRITICAL **File**: `services/inventory-service-net/src/InventoryService.Infrastructure/EntityConfigurations/InventoryItemEntityTypeConfiguration.cs:34` `builder.Ignore(i => i.CreatedAt)` discards the property but never maps the private backing field `_createdAt`. Every entity loaded from the database has `CreatedAt == DateTime.MinValue`. **Fix**: Map `_createdAt` backing field to `created_at` column. No migration needed. --- ### DATA-2: No Optimistic Concurrency on Wallet Balances — Race Condition **Source**: Database Architect (C-2) **Severity**: CRITICAL **File**: `services/wallet-service-net/src/WalletService.Infrastructure/EntityConfigurations/WalletItemEntityTypeConfiguration.cs` Two concurrent POS transactions can read the same balance and overwrite each other. Money disappears silently. **Fix**: Add `builder.UseXminAsConcurrencyToken()` (Npgsql built-in). No migration needed. --- ### SVC-1: 5 Services Still In-Progress (22% Incomplete) **Source**: CEO (C2), Research Analyst (CRIT-01) **Severity**: CRITICAL | Service | Status | |---------|--------| | `promotion-service-net` | 0 commands, 0 queries — controllers reference non-existent logic | | `mission-service-net` | Domain model done, 0 CQRS handlers | | `inventory-service-net` | 1 command handler out of 12+ endpoints needed | | `ads-analytics-service-net` | Minimal commands, incomplete aggregation | | `ads-billing-service-net` | 3 of 8+ command handlers | **Impact**: Cannot deliver full feature coverage. Order fulfillment blocked (no stock validation). Voucher campaigns non-functional. **Fix**: 3 backend engineers, 2-3 weeks dedicated effort. --- ### SVC-2: ads-serving-service-net is READ-ONLY **Source**: CEO (C3), Research Analyst (WARN-02) **Severity**: CRITICAL Zero command handlers. Auction logic is query-only. Cannot create or manage ad placements programmatically. Ads platform is non-functional for write operations. --- ### PROD-1: Marketing Suite is 100% Demo — Zero Backend Integration **Source**: Product Manager (CI-1) **Severity**: CRITICAL **Files**: 6 Marketing pages (`AiChatbot.razor`, `CustomerCrm.razor`, `LivechatConsole.razor`, `AiContentStudio.razor`, `ChatbotAutomation.razor`, `SocialHub.razor`) All 6 pages use hardcoded demo data arrays with fake customer records. This is marketed as a key differentiator vs. KiotViet/Sapo/iPOS but delivers zero actual functionality. Merchants who sign up for Growth/Pro plans expecting CRM and AI chatbot will experience immediate trust damage. **Fix**: Hide Marketing section with "Coming Soon" label, or implement real backend integration (2+ weeks). --- ### PROD-2: Analytics & Reporting — 0% Wired to Real Data **Source**: Product Manager (CI-4) **Severity**: CRITICAL Revenue Dashboard, Staff Performance, and EOD Report pages exist with full UI but all KPI values (revenue, transactions, avg order value, growth %) are hardcoded demo values. Merchants making business decisions based on fake numbers will lose real money. **Fix**: Wire to order-service data. Add `GetDailyRevenueQuery` and `GetTopProductsQuery`. --- ### API-1: Response Format Fragmentation (3 Incompatible Patterns) **Source**: API Architect (CRIT-1), Frontend (WARN-08) **Severity**: CRITICAL Three response patterns exist: IAM uses typed `ApiResponse`, Order/Catalog use anonymous objects, and Merchant returns raw entities. Frontend compensates with a "4-format smart deserializer" — a fragile workaround. SDK auto-generation is blocked. **Fix**: Extract `ApiResponse` to shared NuGet package. Enforce across all services. --- ### TEST-1: Only 1 of 26 Services Has CI Test Pipeline **Source**: QA (C1), CTO (W2) **Severity**: CRITICAL Only `iam-service-net` runs tests in CI. Regressions in 25 other services are undetected until staging deployment. The `deploy-staging.yml` runs migrations but NOT tests before deployment. **Fix**: Generate CI pipelines for all 25 missing services using `ci-iam-service.yml` as template. --- ### CROSS-1: Cross-Service Messaging Not Implemented **Source**: CTO (C5) **Severity**: CRITICAL All `IntegrationEvents/EventHandlers/` directories across 10+ services are empty. RabbitMQ is provisioned in docker-compose but no service publishes or consumes messages. System works via HTTP tight coupling, which will fail at scale. **Fix**: Implement `IEventBus` abstraction with RabbitMQ backend. Start with `OrderCreatedIntegrationEvent` to inventory deduction. --- ## Warnings Issues that should be addressed within the next 2-4 weeks. ### Security Warnings | ID | Issue | Source | Files | |----|-------|--------|-------| | W-SEC-1 | CORS `allowCredentials: true` with localhost origins in shared middleware config | Security (WARN-03) | `infra/traefik/dynamic/middlewares.yml:27` | | W-SEC-2 | `sslRedirect: false` in shared middleware — applies to all environments | Security (WARN-04), CTO | `infra/traefik/dynamic/middlewares.yml:5` | | W-SEC-3 | `Jwt__RequireHttpsMetadata=false` in staging K8s ConfigMap | DevOps (WARN-10), Security (WARN-05) | `deployments/staging/kubernetes/configmap.yaml:21` | | W-SEC-4 | Test credentials hardcoded in ROADMAP.md (checked into git) | CTO (W8) | `ROADMAP.md` Section IX | | W-SEC-5 | K8s staging secrets file contains literal placeholder strings | Security (WARN-09) | `deployments/staging/kubernetes/secrets.yaml` | | W-SEC-6 | `AllowedHosts: "*"` in IAM service — DNS rebinding risk | Security (WARN-08) | `services/iam-service-net/appsettings.json:79` | | W-SEC-7 | No CDN Subresource Integrity on Lucide script from unpkg.com | Frontend (CRIT-04) | `index.html:19` | | W-SEC-8 | TOTP verification window allows 90-second replay (no used-code tracking) | Security (WARN-10) | `TotpTwoFactorService.cs:86` | | W-SEC-9 | Unauthenticated ad tracking endpoints without rate limiting | Security (WARN-07) | Routes for `/api/v1/pixels`, `/api/v1/conversions` | | W-SEC-10 | Traefik dashboard exposed without authentication in local dev | Security (WARN-06), DevOps (WARN-8) | `docker-compose.yml:121` | ### Infrastructure Warnings | ID | Issue | Source | Files | |----|-------|--------|-------| | W-INF-1 | Docker Compose port conflicts — 4 mkt-* services all bind port 5000 | CEO (W1), CTO (C4) | `docker-compose.yml` | | W-INF-2 | Missing K8s manifests — Staging: 9 missing, Production: 11 missing | CEO (W2), CTO (W3) | `deployments/*/kubernetes/` | | W-INF-3 | 13 services missing Traefik gateway routes — bypass security middleware | CTO (W4), API Architect (CRIT-3) | `infra/traefik/dynamic/routes.yml` | | W-INF-4 | 15+ services not in CI/CD deployment pipeline | DevOps (WARN-2) | `.github/workflows/deploy-*.yml` | | W-INF-5 | `redis-exporter` scraped by Prometheus but missing from docker-compose | DevOps (WARN-1) | `prometheus.yml:132` | | W-INF-6 | Redis deployed as single instance in K8s — SPOF for real-time features | DevOps (WARN-4) | `deployments/staging/kubernetes/redis.yaml` | | W-INF-7 | No Kubernetes NetworkPolicy manifests — all pods can communicate freely | DevOps (WARN-5) | Missing from all K8s dirs | | W-INF-8 | Distributed tracing (Jaeger) commented out — no cross-service request correlation | DevOps (WARN-7) | `docker-compose.yml:1230-1241` | | W-INF-9 | `docker-build.yml` pushes `:latest` tag to Docker Hub from `main` branch | DevOps (CRIT-3) | `.github/workflows/docker-build.yml:99-103` | | W-INF-10 | PR checks only validate Node.js — no .NET build/test in PR pipeline | DevOps (WARN-3) | `.github/workflows/pr-checks.yml` | | W-INF-11 | MinIO uses default credentials `minioadmin/minioadmin123` | DevOps (WARN-6) | `docker-compose.yml:78-79` | ### Frontend Warnings | ID | Issue | Source | Files | |----|-------|--------|-------| | W-FE-1 | Token refresh not implemented — silent 401 failures on token expiry | Frontend (WARN-01) | `AuthStateService.cs` | | W-FE-2 | Global HttpClient header mutation — race condition under concurrent requests | Frontend (WARN-02) | `PosDataService.cs:40-47` | | W-FE-3 | No route guards — admin layout renders before auth check | Frontend (WARN-06) | All layouts | | W-FE-4 | `shopId` not validated against user permissions — URL manipulation possible | Frontend (WARN-07) | `AdminLayout.razor:246-286` | | W-FE-5 | ~20% of POS pages have incomplete backend integration (21 TODOs) | Frontend (WARN-10) | Multiple POS pages | | W-FE-6 | MudBlazor ThemeProvider declared in multiple layouts — duplicate instances | Frontend (WARN-04) | All layout files | | W-FE-7 | localStorage logic duplicated across 5 files | Frontend (WARN-05) | `AuthService.cs`, layouts, `LanguageSwitcher.razor` | | W-FE-8 | `eval()` used for DOM focus management in OTP input | Architect (W-2) | `OtpInput.razor` | | W-FE-9 | Incomplete vi-VN translations vs en-US.json | Frontend (WARN-11) | `wwwroot/locales/vi-VN.json` | ### UX/Accessibility Warnings | ID | Issue | Source | Files | |----|-------|--------|-------| | W-UX-1 | No `:focus-visible` styles — keyboard navigation invisible (WCAG 2.4.7) | UX/UI (Issue 1) | All CSS files | | W-UX-2 | Clickable `
` elements instead of `