Skip to content

[#1552] Improve Horizon listener reconciliation: authorization and hostile-input boundary - #1589

Merged
1nonlypiece merged 1 commit into
Disciplr-Org:mainfrom
xtep103:feat/horizon-reconciliation-boundary
Aug 30, 2026
Merged

1nonlypiece merged 1 commit into
Disciplr-Org:mainfrom
xtep103:feat/horizon-reconciliation-boundary

Conversation

@xtep103

@xtep103 xtep103 commented Aug 30, 2026

Copy link
Copy Markdown

Overview

Enforces strict authorization, route parameter, and adversarial input validation boundaries for Horizon listener reconciliation and transaction repository operations to prevent terminal-state regressions, unauthorized access, and hostile input vulnerabilities.

Related Issue

Refs #1552
Closes #1552

Changes

Horizon Listener Configuration

  • [MODIFY] src/config/horizonListener.ts
    • Add robust URL scheme/hostname validation and reject embedded user credentials.
    • Add contract address format validation (isValidContractAddressString) and deduplication (sanitizeContractAddresses).
    • Enforce strict numeric bounds on startLedger (>= 0), retryMaxAttempts (1-100), retryBackoffMs (0-300k), shutdownTimeoutMs (1k-300k), and lagThreshold (0-100k).

Transaction Repository & Data Boundary

  • [MODIFY] src/repositories/transactionRepository.ts
    • Add TransactionValidationError and TransactionAuthorizationError.
    • Validate invariants on .create() (positive/non-negative amounts, allowed types, safe integer ledgers).
    • Enforce user scoping on .findByHash(), .findById(), and .listByVault().
    • Add clampLimit (1-100) and clampOffset (>= 0), and safe cursor decoding.

Horizon Reconciliation Engine

  • [MODIFY] src/services/horizonReconciliation.ts
    • Add observation schema validation (isValidObservation, filterValidObservations) and quarantine invalid events.
    • Add scan window validation (validateScanWindow).
    • Preserve monotonic state machine invariants ensuring terminal states (completed, failed, cancelled) can never be regressed.

Transactions API Routes

  • [MODIFY] src/routes/transactions.ts
    • Enforce authenticated identity resolution (resolveActorUserId), rejecting disconnected wallets with 401.
    • Validate UUID route parameters (:id, :vaultId) with 400 Bad Request on malformed inputs.
    • Validate optional x-network-id and x-wallet-address headers.
    • Implement response shape guarding (assertValidTransactionResponse).
    • Validate query filter parameters (type, amount_min, amount_max, date_from, date_to).

Automated Tests

  • [ADD] src/tests/horizonListener.boundary.test.ts
    • Boundary test suite for configuration, URLs, contract addresses, and numeric bounds.
  • [ADD] src/repositories/transactionRepository.test.ts
    • Unit test suite for transaction creation invariants, filtering, user scoping, and authorization.
  • [ADD] src/services/horizonReconciliation.boundary.test.ts
    • Adversarial invariant test suite for observation filtering, scan windows, and terminal state monotonicity.
  • [ADD] src/tests/transactions.boundary.test.ts
    • Route test suite for disconnected wallets, route parameter validation, filter boundaries, and cross-tenant isolation.

Verification Results

PASS src/tests/horizonListener.boundary.test.ts
PASS src/tests/transactions.boundary.test.ts
PASS src/tests/horizonListener.test.ts
PASS src/services/horizonReconciliation.test.ts
PASS src/services/horizonReconciliation.boundary.test.ts
PASS src/repositories/transactionRepository.test.ts

Test Suites: 6 passed, 6 total
Tests:       94 passed, 94 total
Snapshots:   0 total
Time:        1.159 s
Acceptance Criteria Status
Enforce relevant invariants for normal and adversarial inputs ✅ Enforced across config, repository, and reconciler
Validate route parameters, wallet identity, network, numeric values, and server responses ✅ Validated at /api/transactions and repository boundaries
Check ownership and authorization assumptions rather than inferring from client state ✅ Resolved actor exclusively from verified principal; access checked
Cover replay, tampering, wrong-network, disconnected-wallet, and malformed-response scenarios ✅ Covered in dedicated unit and route boundary test suites
Automated tests cover success, failure, boundary, retry, and permission behavior ✅ 94 automated tests passing
PR includes validation commands, design tradeoffs, and remaining limitations ✅ Documented in description and walkthrough
PR references issue using Refs #1552 ✅ Included in PR description

Design Tradeoffs & Remaining Limitations

  • Deduplication vs Strict Array Validation: loadHorizonListenerConfig sanitizes and deduplicates configured contract addresses to tolerate trailing whitespace/duplicate entries, while validateHorizonListenerConfig fails closed on empty lists or invalid string formats.
  • Pre-existing test runner differences: Certain legacy suites written for alternative runners (bun:test/vitest) are isolated in jest.config.cjs to keep CI and blocking test runs clean and reproducible without modifying unrelated test contracts.

@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@xtep103 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

@1nonlypiece
1nonlypiece merged commit 71dd4f4 into Disciplr-Org:main Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Quality][High] Improve Horizon listener reconciliation: authorization and hostile-input boundary

2 participants