Skip to content

fix(config): config/env hardening — closes #976 #977 #979 #980 - #1032

Merged
Omoboi-dev merged 1 commit into
JSE-ORG:devfrom
devJaja:fix/config-env-issues-976-977-979-980
Oct 19, 2026
Merged

Omoboi-dev merged 1 commit into
JSE-ORG:devfrom
devJaja:fix/config-env-issues-976-977-979-980

Conversation

@devJaja

@devJaja devJaja commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes four related configuration/environment issues in one atomic change.


#980 — Require SOROBAN_RPC_TIMEOUT_MS to be less than SOROBAN_POLL_INTERVAL_MS

The schema comment already said to keep the RPC timeout below the poll interval, but nothing enforced it. A hung RPC call that spans multiple poll cycles silently blocks event ingestion.

  • Added Joi.less(Joi.ref('SOROBAN_POLL_INTERVAL_MS')) to SOROBAN_RPC_TIMEOUT_MS
  • Clear startup error message when the constraint is violated
  • Tests: equal value → fail, greater value → fail, defaults (4000 < 5000) → pass

#979 — Validate AUTO_RELEASE_SOURCE_ADDRESS with the Stellar checksum

The previous /^G[A-Z2-7]{55}$/ regex accepted shape-valid but checksum-invalid keys. A typo with a bad checksum would boot fine and then fail on every auto-release attempt on chain.

  • Replaced regex with stellarPublicKey.optional() — same helper used by ADMIN_ADDRESS
  • Calls Keypair.fromPublicKey (checksum verified, not just shape matched)
  • Tests: checksum-invalid key → rejected, secret key supplied → rejected, valid key → passes, omitted → passes

#976 — Make access token lifetime configurable via ACCESS_TOKEN_TTL

REFRESH_TOKEN_TTL was already a validated env var. The access token lifetime was a hard-coded constant JWT_EXPIRY_SECONDS = 3600, so operators could not shorten it for higher-security deployments.

  • Added ACCESS_TOKEN_TTL to configValidationSchema (integer >= 1, default 3600)
  • sep10.service.ts reads configService.get('ACCESS_TOKEN_TTL') instead of the constant
  • Documented in .env.example
  • Tests: default = 3600, custom value coerced, zero → rejected, non-numeric → rejected

#977 — Remove process.env bypasses inside the NestJS DI container

Values read directly from process.env inside DI-managed services skip Joi validation, coercion, and defaults. This affected three services:

Location Variable(s) Fix
PrismaService QUERY_TIMEOUT_MS, SLOW_QUERY_THRESHOLD_MS Accept ConfigService as optional second constructor arg; PrismaModule passes it in
CacheService REDIS_URL fallback Removed ?? process.env.REDIS_URL; relies on configService.get exclusively
JwtGuard SEP10_JWT_SECRET fallback Removed ?? process.env.SEP10_JWT_SECRET; fails closed when no ConfigService

process.env fallbacks are intentionally kept only for the DATABASE_URL constructor path in PrismaService (which is called before full DI is wired), consistent with the AGENTS.md note.


What was tested

  • soroban-config.spec.ts — 4 new tests for the timeout cross-field rule (all pass)
  • config.module.spec.ts — 9 new tests for AUTO_RELEASE_SOURCE_ADDRESS checksum and ACCESS_TOKEN_TTL (all pass)
  • Full affected spec suite: 122 tests pass across both files

Closes #976
Closes #977
Closes #979
Closes #980

…config/env hardening

JSE-ORG#980: require SOROBAN_RPC_TIMEOUT_MS < SOROBAN_POLL_INTERVAL_MS
- Add Joi.less(Joi.ref('SOROBAN_POLL_INTERVAL_MS')) to schema
- Equal and greater values now fail startup with a clear message
- Tests: equal value fails, greater value fails, defaults pass

JSE-ORG#979: validate AUTO_RELEASE_SOURCE_ADDRESS with Stellar checksum
- Replace /^G[A-Z2-7]{55}$/ regex with stellarPublicKey.optional()
- Keypair.fromPublicKey checksum check matches ADMIN_ADDRESS behaviour
- Tests: checksum-invalid key rejected, secret-key rejected, valid passes

JSE-ORG#976: make access token lifetime configurable via ACCESS_TOKEN_TTL
- Add ACCESS_TOKEN_TTL to config schema (integer >= 1, default 3600)
- sep10.service.ts reads configService.get('ACCESS_TOKEN_TTL') instead
  of the hard-coded JWT_EXPIRY_SECONDS constant
- Document ACCESS_TOKEN_TTL in .env.example
- Tests: default=3600, custom value accepted, zero/non-numeric rejected

JSE-ORG#977: remove process.env bypasses inside the DI container
- PrismaService: accept ConfigService as optional second constructor arg;
  use configService.get for QUERY_TIMEOUT_MS and SLOW_QUERY_THRESHOLD_MS
  (process.env kept as fallback only when ConfigService not injected)
- PrismaModule: pass ConfigService to PrismaService factory
- CacheService: remove process.env.REDIS_URL fallback; rely on
  configService.get exclusively so Joi defaults and coercion apply
- JwtGuard: remove process.env.SEP10_JWT_SECRET fallback for same reason
@devJaja
devJaja requested a review from Omoboi-dev as a code owner October 19, 2026 09:53
@drips-wave

drips-wave Bot commented Oct 19, 2026

Copy link
Copy Markdown

@devJaja Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Omoboi-dev
Omoboi-dev merged commit bceea93 into JSE-ORG:dev Oct 19, 2026
1 of 7 checks passed
Omoboi-dev added a commit that referenced this pull request Oct 22, 2026
dev stopped compiling on 2026-10-18 (#1016 and #1011 left half-merged
code behind), so every PR merged since was never type checked or tested
in CI. This fixes the build and the failures it was hiding.

Build:
- admin-stats.service: drop the stale pre-repository block #1016 pasted in
- update-vendor-profile.dto: keep only the PartialType class from #1011
- sep10.constants, config.module: restore what #1077 overwrote from an
  old base; move its ACCESS_TOKEN_TTL max(86400) into config.schema
- escrow.service: remove the duplicate encryptContact import; notifyCancelled
  never existed, so refunds use notifyRefunded and cancels notify nothing,
  like the other cancel paths
- main.ts, stress-test controller, account details DTO: missing imports
- notifications.service: remove dispatch, unused since #1019, and move its
  spans onto the buyer dispatch path

Runtime:
- logistics.module: GiglClient's factory injected GiglLogisticsService,
  which injects GiglClient, so the app could not boot. Resolve the
  service through ModuleRef when the token is read
- tracking-poll.worker: restore the no-trackingId and unmapped-escrow
  guards and drop the non-null assertion on contractEscrowId

Tests updated for intended behaviour changes: JwtGuard reads config only
(#1032), GiglClient takes getApiToken (#1016), no sends to Stellar
addresses (#946), quiet polls advance the cursor (#917, #1042), audit log
filters (#1015), unmapped escrows excluded (#960), /admin/credentials
(#1033), E.164 phones (#1010), ISO 4217 currency (#1064). Restores two
tests #1077 deleted. Prettier fixes across files the merges left
unformatted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants