fix: align policyId validation in search endpoint and introduce domain validators (#726)
* fix: align policyId validation in search endpoint with account endpoint
The search/transactions endpoint was missing policyId input validation
that the account endpoint already enforces. This aligns both endpoints
to use the same hex regex validation for policyId, and strengthens the
repository layer to also validate inputs before query construction.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: use JOOQ bind parameters in currency condition builders
Replace string concatenation into DSL.condition() SQL strings with
JOOQ's DSL.val() bind parameters. This makes the query construction
structurally safe — the database driver handles escaping via prepared
statement parameters, eliminating any possibility of unsanitized data
reaching SQL regardless of upstream validation.
PostgreSQL: uses jsonb_build_array(jsonb_build_object('key', ?)) to
build JSONB containment checks from bind parameters instead of
interpolating values into JSON literals.
H2: uses ? bind parameters in LIKE conditions instead of concatenating
values into the LIKE pattern string.
The requireHex() validation in BaseCurrencyConditionBuilder is retained
as a fast-fail check for better error messages, but is no longer the
sole protection against unsanitized input reaching queries.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: introduce PolicyIdValidator and TokenNameValidator classes
Consolidate scattered policyId/tokenName/hex validation into dedicated
validator classes under common.validation package. This replaces
duplicate regex patterns in AccountServiceImpl, CardanoAddressUtils,
ValidateParseUtil, and GovActionParamsUtil with single-source-of-truth
validators backed by HexUtils.isHexString().
- PolicyIdValidator: isValid(), requireValid(), isHexOnly(), requireHexOnly()
- TokenNameValidator: isValid()
- 35 unit tests for both validators
- All callers updated to delegate to validators
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: consolidate remaining hex validation calls through validators
Update SearchServiceImpl.validateCurrencySymbolIsHex and
AssetFingerprint.fromSubject to use PolicyIdValidator.isHexOnly()
instead of calling HexUtils.isHexString() directly. All hex validation
in the codebase now flows through the centralized validator classes.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: simplify validators to expose only isValid()
Validators now have a clean API - only isValid() per domain concept:
- PolicyIdValidator.isValid(): exactly 56 hex chars
- SymbolValidator.isValid(): non-empty hex string
- TokenNameValidator.isValid(): 0-64 hex chars or empty hex
Removed requireValid/requireHexOnly/requireValidHex from validators.
For raw hex checks (defense-in-depth at repository layer, AssetFingerprint),
use HexUtils.isHexString() directly.
All error types (INVALID_POLICY_ID 4023, INVALID_TOKEN_NAME 4024,
CURRENCY_SYMBOL_NOT_HEX 5059) are registered in RosettaErrorType and
returned by /network/options via ALL_ROSETTA_ERRORS.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: add validate() methods to validators and remove private wrappers
Extract validate-and-throw logic from SearchServiceImpl and AccountServiceImpl
into PolicyIdValidator, SymbolValidator, and TokenNameValidator, eliminating
duplicated private validation methods across service classes.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: replace custom requireHex/requireValidSymbol with shared validators
Remove defense-in-depth validation methods from BaseCurrencyConditionBuilder
and delegate to PolicyIdValidator.validate() and SymbolValidator.validate()
instead, keeping the template method pattern for DB-specific SQL generation.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* test: add input validation tests for currency, address, and coin identifier (#727)
Adds 4 test classes to test_error_handling.py with 17 parametrized cases:
- SearchCurrencyValidation: policyId and symbol rejection (requires PR #726)
- AccountCurrencyValidation: policyId and token name rejection
- AddressValidation: invalid bech32 on /account/balance and /account/coins
- CoinIdentifierValidation: malformed identifier without colon separator
* fix: handle malformed coin_identifier in search and align validator null semantics
Replace ArrayIndexOutOfBoundsException and NumberFormatException with proper
ApiException when coin_identifier lacks a colon separator or has a non-numeric
output index. Also align TokenNameValidator.validate() to accept null
consistently with PolicyIdValidator and SymbolValidator.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* test: enable coin_identifier validation test after fix
Remove @pytest.mark.skip now that ba4df5db5 handles malformed
coin_identifier properly. Verified against local build:
HTTP 400, code 5000, retriable false.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Mateusz Czeladka <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Lincon Vidal <[email protected]>