| T-01 |
TB-01 Sender browser → Frontend |
Spoofing |
Attacker phishes sender to a lookalike frontend, capturing wallet seed phrase or signing malicious transactions |
HTTPS, no seed phrase in app |
No CSP / SRI enforcement documented; no anti-phishing domain monitoring |
High |
| T-02 |
TB-01 Sender browser → Frontend |
Tampering |
XSS injection via user-supplied input (recipient details, memo fields) modifies transaction parameters before signing |
sanitizeInput helper in backend; frontend input handling unknown |
Frontend-side sanitisation not confirmed; React dangerouslySetInnerHTML usage not audited |
High |
| T-03 |
TB-01 Sender browser → Frontend |
Repudiation |
Sender denies authorising a transaction after signing; no durable client-side audit trail |
On-chain event log is immutable |
Frontend session logs not persisted server-side; only on-chain events constitute non-repudiation |
Medium |
| T-04 |
TB-01 Sender browser → Frontend |
Information Disclosure |
Browser dev-tools or browser extensions read localStorage/sessionStorage containing JWT or API keys |
HTTPS in transit |
Key storage strategy in browser not specified; risk of token theft via extension or XSS |
High |
| T-05 |
TB-01 Sender browser → Frontend |
Denial of Service |
Attacker floods frontend CDN with requests, degrading service |
CDN rate limiting (provider-level) |
No documented CDN WAF or DDoS mitigation configuration |
Medium |
| T-06 |
TB-01 Sender browser → Frontend |
Elevation of Privilege |
CSRF forces authenticated sender to submit a cancel or create request without consent |
Not documented |
No CSRF token or SameSite=Strict cookie policy documented |
High |
| T-07 |
TB-02 Frontend → API Service |
Spoofing |
Attacker replays a stolen JWT to impersonate a legitimate sender |
JWT-based auth (JWT_SECRET in Secrets Manager) |
JWT expiry and rotation policy not documented; no refresh-token revocation mechanism confirmed |
High |
| T-08 |
TB-02 Frontend → API Service |
Tampering |
Attacker modifies JSON request body in transit to change remittance amount or agent address |
HTTPS TLS |
Certificate pinning not implemented; relies on CA trust chain |
Medium |
| T-09 |
TB-02 Frontend → API Service |
Repudiation |
API service does not log sufficient detail of each request to attribute actions to a specific user |
compliance_report_audit table logs exports |
General request audit logging completeness not confirmed; officer ID defaults to 'anonymous' if header absent |
Medium |
| T-10 |
TB-02 Frontend → API Service |
Information Disclosure |
Verbose error messages from API expose internal stack traces, DB schema, or secret key names |
Not documented |
Error handling middleware and response sanitisation not confirmed |
Medium |
| T-11 |
TB-02 Frontend → API Service |
Denial of Service |
API endpoint flooded beyond rate limit; disrupts all users |
Per-API-key rate limit (API_KEY_RATE_LIMIT_MAX=200/min); global RATE_LIMIT_MAX_REQUESTS=100 per 15 min |
Unauthenticated endpoints (e.g. health) have no documented rate limit |
Medium |
| T-12 |
TB-02 Frontend → API Service |
Elevation of Privilege |
Horizontal privilege escalation: authenticated sender crafts requests acting as a different sender by manipulating sender address parameter |
sender.require_auth() on contract |
API service may forward user-supplied sender address directly to contract without ownership verification at API layer |
High |
| T-13 |
TB-03 API Service → Smart Contract |
Spoofing |
API service submits transaction signed with a key not belonging to the actual user (e.g. using stored ADMIN_SECRET_KEY) |
Soroban require_auth enforces signing key matches parameter |
execute_transaction has no explicit require_auth; relies only on implicit token transfer auth |
Critical |
| T-14 |
TB-03 API Service → Smart Contract |
Tampering |
create_remittance called with a colliding idempotency key but different payload, causing a conflicting duplicate remittance record |
IdempotencyConflict (error 50) returned on key reuse with different payload |
If idempotency key is guessable (sequential or user-supplied without validation), attacker can probe for collisions |
High |
| T-15 |
TB-03 API Service → Smart Contract |
Repudiation |
No on-chain signature of remittance intent beyond the require_auth call; sender can claim they never authorised the exact parameters |
require_auth proves sender signed the transaction |
Amount and agent address are parameters, not explicitly signed as a structured message; replay of signed tx within TTL is possible |
Medium |
| T-16 |
TB-03 API Service → Smart Contract |
Information Disclosure |
All remittance amounts, sender/agent addresses, and status are publicly readable on-chain |
By design (blockchain transparency) |
recipient_hash only hashes recipient details; amount and parties are fully public |
Medium |
| T-17 |
TB-03 API Service → Smart Contract |
Denial of Service |
Spam batch_create_remittances to bloat contract persistent storage, forcing storage TTL extension costs |
enforce_daily_send_limit, rate limiter, blacklist |
Daily limit is per-currency/country, not per-address globally; a sender could use multiple currency/country pairs |
Medium |
| T-18 |
TB-03 API Service → Smart Contract |
Elevation of Privilege |
migrate_to_governance is a one-time migration callable only by the legacy single admin; if called prematurely with a low quorum, it entrenches a weak governance model |
One-time guard (GovernanceAlreadyInitialized error 67) |
Quorum and timelock parameters are caller-supplied with no minimum enforced on-chain; quorum=1 effectively reverts to single-admin |
High |
| T-19 |
TB-04 Smart Contract → USDC Token Contract |
Spoofing |
Malicious USDC-lookalike token contract is whitelisted, allowing an attacker to create remittances with worthless tokens |
Token whitelist (add_whitelisted_token); asset_verification.rs checks |
Asset verification data is admin-supplied (off-chain); no on-chain proof of Circle issuance |
High |
| T-20 |
TB-04 Smart Contract → USDC Token Contract |
Tampering |
Cross-contract call to USDC token is intercepted by a re-entrancy exploit |
Soroban VM is single-threaded; no mid-execution callbacks |
VM guarantee eliminates classic re-entrancy; cross-contract reentrancy via callback pattern not applicable in Soroban |
Low |
| T-21 |
TB-04 Smart Contract → USDC Token Contract |
Repudiation |
Token transfer failure is silently swallowed, leaving escrow state inconsistent with token balances |
Soroban panics on failed cross-contract calls, rolling back the whole tx |
If token contract returns an error code rather than panicking, the contract must handle it; depends on USDC implementation |
Medium |
| T-22 |
TB-04 Smart Contract → USDC Token Contract |
Information Disclosure |
Token transfer events reveal escrow amounts and parties to all ledger observers |
By design |
No mitigation possible on a public blockchain |
Low |
| T-23 |
TB-04 Smart Contract → USDC Token Contract |
Denial of Service |
Circle pauses or upgrades USDC token contract, breaking all transfers |
Outside contract's control |
No fallback token or circuit-breaker for USDC contract-level pause |
High |
| T-24 |
TB-04 Smart Contract → USDC Token Contract |
Elevation of Privilege |
batch_settle_with_netting has no require_auth — any caller (not just a registered agent) can invoke it while the contract is unpaused |
Blocked while paused; is_agent_registered check present per README note |
README explicitly flags this as a known security gap: "only blocked while the contract is paused; treat as a known gap" |
Critical |
| T-25 |
TB-05 Contract events → Backend Event Listener |
Spoofing |
Attacker operates a rogue RPC node that returns fabricated contract events, causing the backend to process phantom remittances |
Backend uses Stellar SDK to poll a configured SOROBAN_RPC_URL |
No multi-RPC consensus or event signature verification; single RPC node is a trust dependency |
High |
| T-26 |
TB-05 Contract events → Backend Event Listener |
Tampering |
Attacker with RPC node access modifies event payload in transit, changing remittance IDs or amounts seen by the backend |
HTTPS in transit |
No on-chain event hash that the backend can independently verify against ledger state |
High |
| T-27 |
TB-05 Contract events → Backend Event Listener |
Repudiation |
Backend processes an event but fails to persist it before a crash, causing the event to be dropped without an audit trail |
Events persisted to PostgreSQL on receipt |
Retry logic and at-least-once delivery guarantee not confirmed; event gaps are possible |
Medium |
| T-28 |
TB-05 Contract events → Backend Event Listener |
Information Disclosure |
All contract events are publicly visible on-chain; backend processing reveals internal business logic through webhook timing |
By design |
Webhook timing can be used to infer business activity patterns |
Low |
| T-29 |
TB-05 Contract events → Backend Event Listener |
Denial of Service |
Attacker spams process_expired_remittances (permissionless) with large batches to overload backend event processing and exhaust DB write capacity |
set_max_expired_batch_size limits batch to 1–200; permissionless by design |
High-frequency permissionless calls can generate high event volume that the backend must process; no throttle on external callers |
Medium |
| T-30 |
TB-05 Contract events → Backend Event Listener |
Elevation of Privilege |
Backend automatically escalates actions (e.g. KYC approval, webhook delivery) based on unvalidated event data, bypassing manual review |
Events trigger KYC and webhook flows |
If backend trusts event fields without cross-checking contract state, a forged event could approve KYC or trigger fraudulent payouts |
High |
| T-31 |
TB-06 Backend → Anchor (SEP-24) |
Spoofing |
Forged webhook from a non-trusted anchor passes HMAC verification if the webhook secret is shared or rotated without invalidating old requests |
TRUSTED_ANCHOR_IDS list; WEBHOOK_SECRET_{ANCHOR_ID} per-anchor HMAC |
HMAC secret rotation procedure not documented; no nonce/timestamp check to prevent replay within HMAC validity window |
High |
| T-32 |
TB-06 Backend → Anchor (SEP-24) |
Tampering |
Anchor returns a manipulated SEP-24 transaction status (e.g. complete for a transaction still pending), causing premature KYC approval or payout |
Backend polls anchor status and cross-checks |
Anchor response is trusted as authoritative; no secondary confirmation source |
High |
| T-33 |
TB-06 Backend → Anchor (SEP-24) |
Repudiation |
Anchor denies having sent a particular status update; no anchor-signed proof in the webhook payload |
Audit log records received webhooks with IP |
No cryptographic anchor signature on webhook; only HMAC with shared secret |
Medium |
| T-34 |
TB-06 Backend → Anchor (SEP-24) |
Information Disclosure |
SEP-12 KYC webhook delivers PII (name, document number) over HTTP if TLS misconfigured |
HTTPS expected |
TLS validation and certificate pinning to anchor endpoint not confirmed |
High |
| T-35 |
TB-06 Backend → Anchor (SEP-24) |
Denial of Service |
Anchor goes offline; ANCHOR_TIMEOUT_HOURS expires, triggering mass timeout events that flood the backend webhook dispatcher |
ANCHOR_TIMEOUT_HOURS=24 with ANCHOR_TIMEOUT_WEBHOOK_URL notification |
No circuit breaker for anchor unavailability; cascading timeouts possible |
Medium |
| T-36 |
TB-06 Backend → Anchor (SEP-24) |
Elevation of Privilege |
Malicious anchor sends SEP-12 KYC approval for a blacklisted or fraudulent user, bypassing on-chain blacklist if backend accepts without re-checking |
On-chain blacklist_user and is_kyc_approved |
Backend must call set_kyc_approved on-chain; if it does so based solely on anchor webhook, a forged webhook = on-chain KYC approval |
Critical |
| T-37 |
TB-07 Backend → KYC Provider |
Spoofing |
Attacker impersonates KYC provider API, returning approvals for any user |
HTTPS + API key (FX_API_KEY pattern suggests keyed access) |
No certificate pinning to KYC provider; DNS hijack of provider hostname would succeed if TLS validation is disabled |
High |
| T-38 |
TB-07 Backend → KYC Provider |
Tampering |
Man-in-the-middle modifies KYC approval response to approve a fraudulent user or deny a legitimate one |
HTTPS in transit |
See T-37; same TLS dependency |
High |
| T-39 |
TB-07 Backend → KYC Provider |
Information Disclosure |
PII submitted to KYC provider is logged at DEBUG level, leaking into log files or monitoring systems |
Standard practice assumption |
Log redaction policy for PII fields not documented |
High |
| T-40 |
TB-07 Backend → KYC Provider |
Denial of Service |
KYC provider outage blocks all new user onboarding; no fallback or cached approvals |
KYC_RENEWAL_BASE_URL for re-verification |
No documented fallback or grace period for KYC provider downtime |
Medium |
| T-41 |
TB-08 Backend → PostgreSQL |
Spoofing |
Attacker with network access connects directly to PostgreSQL using credentials from a leaked .env file |
DATABASE_URL expected in Secrets Manager in production |
Default dev config stores credentials in .env; no documented network-level DB access control (VPC/security group) |
Critical |
| T-42 |
TB-08 Backend → PostgreSQL |
Tampering |
SQL injection via unsanitised query parameters in compliance report endpoint modifies audit records |
sanitizeInput helper; parameterised queries in compliance.ts ($1, $2…) |
Parameterised queries confirmed in compliance.ts; full audit of all DB queries not confirmed |
High |
| T-43 |
TB-08 Backend → PostgreSQL |
Repudiation |
Attacker with DB write access deletes or modifies compliance_report_audit rows, erasing evidence of fraudulent exports |
Audit table exists |
No append-only or immutable audit log (e.g. PostgreSQL logical replication to read-only replica) |
High |
| T-44 |
TB-08 Backend → PostgreSQL |
Information Disclosure |
Database backup files (pg_dump) stored without encryption expose all PII and secrets |
AWS Secrets Manager for prod secrets |
Backup encryption and retention policy not documented |
High |
| T-45 |
TB-08 Backend → PostgreSQL |
Denial of Service |
Long-running compliance report query with unindexed filters exhausts DB connection pool (DB_POOL_MAX=20), blocking all backend operations |
Connection pool config (DB_POOL_MAX=20) |
No query timeout or statement-level timeout documented for compliance endpoint |
Medium |
| T-46 |
TB-08 Backend → PostgreSQL |
Elevation of Privilege |
Backend service account has DDL privileges; compromised backend process can DROP TABLE or alter schema to destroy audit trails |
Not documented |
Principle of least privilege for DB service account not confirmed; DDL should require a separate migration role |
High |
| T-47 |
TB-09 Admin CLI → Smart Contract |
Spoofing |
Attacker with stolen admin secret key submits admin transactions to drain fees or register a rogue agent |
M-of-N multisig for withdraw_fees, pause, UpdateFee; ADMIN_SECRET_KEY in Secrets Manager |
register_agent and blacklist_user are single-admin operations not covered by multisig |
Critical |
| T-48 |
TB-09 Admin CLI → Smart Contract |
Tampering |
emergency_pause called by a single admin without multisig requirement; contract halted, blocking all user transactions |
pause / emergency_pause require admin role |
Only one admin vote needed to pause (by design for emergency); unpause requires quorum — asymmetric and could be abused for DoS |
High |
| T-49 |
TB-09 Admin CLI → Smart Contract |
Repudiation |
Admin calls add_admin to add a sockpuppet admin, then later denies responsibility for actions taken by that admin address |
On-chain event log is immutable |
No off-chain admin identity registry linking Stellar address to named individual |
Medium |
| T-50 |
TB-09 Admin CLI → Smart Contract |
Information Disclosure |
Admin CLI logs include the secret key in shell history or process list |
Secrets Manager integration |
Local .env stores ADMIN_SECRET_KEY=SXXX...; key visible in ps aux or shell history |
Critical |
| T-51 |
TB-09 Admin CLI → Smart Contract |
Denial of Service |
Admin calls set_max_expired_batch_size(1) to reduce batch throughput, causing a backlog of expired remittances that cannot be cleared |
Max batch capped at 200 |
Minimum batch size of 1 still allows an admin to throttle permissionless expiry processing |
Low |
| T-52 |
TB-09 Admin CLI → Smart Contract |
Elevation of Privilege |
import_migration_batch can overwrite any persistent storage key if the migration snapshot is crafted maliciously; attacker with admin key can alter stored balances |
require_admin gate; hash verification on each batch |
Migration hash is computed from snapshot content — if export_migration_snapshot is compromised first, the hash matches the tampered data |
Critical |
| T-53 |
TB-10 Agent → Smart Contract |
Spoofing |
Attacker registers a lookalike agent address and tricks a sender into directing remittances to it |
is_agent_registered check; admin must explicitly call register_agent |
Social engineering of admin to register a rogue agent is possible; no on-chain identity binding |
High |
| T-54 |
TB-10 Agent → Smart Contract |
Tampering |
Agent calls confirm_payout with a forged proof parameter that doesn't match the actual settlement; contract accepts it if proof validation is optional |
Settlement hash stored on first confirmation; DuplicateSettlement prevents double-payout |
Proof is optional (proof?); if not supplied, no off-chain evidence of fiat delivery is required |
High |
| T-55 |
TB-10 Agent → Smart Contract |
Repudiation |
Agent calls mark_failed without actually attempting fiat payout, then disputes resolution favours agent — agent receives USDC without having paid out fiat |
raise_dispute / resolve_dispute flow; admin adjudicates |
Dispute resolution is admin-discretionary; no cryptographic proof of fiat delivery required |
High |
| T-56 |
TB-10 Agent → Smart Contract |
Information Disclosure |
Agent can enumerate all remittances assigned to them via get_remittances_by_agent, exposing sender addresses and amounts |
Public query, by design |
Agents learn the full transfer history of all senders they've served |
Low |
| T-57 |
TB-10 Agent → Smart Contract |
Denial of Service |
Malicious registered agent calls mark_failed on all assigned pending remittances in bulk, forcing mass refunds and degrading platform throughput |
Admin can remove_agent; agent auth required |
No rate limit on mark_failed per agent; a single rogue agent can drain the pending queue |
High |
| T-58 |
TB-10 Agent → Smart Contract |
Elevation of Privilege |
confirm_payout contains double-payout risk: if settlement hash storage is bypassed or has a collision, the same remittance could be paid twice |
Settlement hash locked via SettlementPacked storage key; DuplicateSettlement (error 12) |
Hash collision in the deterministic settlement hash function (uses remittance ID + on-chain data) is theoretically possible but practically negligible |
Low |
| T-59 |
TB-11 Backend → Stellar RPC |
Spoofing |
Attacker operates a rogue Soroban RPC node at the configured SOROBAN_RPC_URL; backend submits admin transactions to this node, which could drop or reorder them |
HTTPS + configured URL |
No multi-RPC submission or transaction status re-confirmation from a second node |
High |
| T-60 |
TB-11 Backend → Stellar RPC |
Tampering |
RPC node returns stale or manipulated ledger state, causing the backend to make decisions based on incorrect contract storage values |
Stellar consensus guarantees ledger state |
A single trusted RPC node is the backend's view of truth; no independent verification |
High |
| T-61 |
TB-11 Backend → Stellar RPC |
Repudiation |
Backend submits a transaction that gets included in the ledger but the backend crashes before recording the submission; transaction is orphaned |
On-chain ledger is canonical |
Backend must reconcile on restart; no documented recovery procedure for orphaned transactions |
Medium |
| T-62 |
TB-11 Backend → Stellar RPC |
Information Disclosure |
RPC node logs all submitted transactions including fee bumps; if node is operated by a third party, submitted transaction content is visible to node operator |
Standard RPC trust model |
Sensitive operational patterns (e.g. batch timing) visible to RPC node operator |
Low |
| T-63 |
TB-11 Backend → Stellar RPC |
Denial of Service |
RPC node becomes unavailable or rate-limits the backend, preventing event polling, transaction submission, and contract state queries |
SOROBAN_RPC_URL is configurable |
No fallback RPC URL documented; single RPC dependency is an availability risk |
High |
| T-64 |
TB-11 Backend → Stellar RPC |
Elevation of Privilege |
Backend uses ADMIN_SECRET_KEY to sign and submit admin transactions; if the RPC node is compromised, it could substitute a different transaction for the one submitted |
Stellar transaction signing is cryptographic |
Signed transaction content is tamper-evident; substitution would invalidate the signature |
Low |