T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/dex_quote.py:247
- Finding
- Floating-Point Conversion Can Alter Financial Quote Amounts## Vulnerability Details **File Location**: `scripts/dex_quote.py`, lines 247–260 and line 383 **Vulnerability Type**: Numeric precision loss in financial amount processing **Risk Level**: Medium ### Vulnerable Code ```python @staticmethod def to_raw_amount(human_amount: float, decimals: int) -> str: """Convert human-readable amount to raw amount string. Uses integer math to avoid floating-point precision issues. """ # Handle decimal amounts by splitting on '.' amount_str = f"{human_amount:.{decimals}f}" if "." in amount_str: integer_part, decimal_part = amount_str.split(".") decimal_part = decimal_part[:decimals].ljust(decimals, "0") raw = int(integer_part) * (10 ** decimals) + int(decimal_part) else: raw = int(amount_str) * (10 ** decimals) return str(raw) ``` The command-line interface introduces the same issue: ```python parser.add_argument("--amount", type=float, required=True, help="Human-readable amount") ``` ### Technical Analysis Token amounts require exact fixed-point arithmetic. The CLI parses the user-supplied decimal as an IEEE-754 binary floating-point value, and `to_raw_amount()` also declares and processes the value as a `float`. Many decimal values cannot be represented exactly in binary floating point. Large values and values near token-unit boundaries can therefore be rounded before conversion into raw units. Formatting the already-rounded float does not recover the original user input. This implementation also contradicts the guidance in `SKILL.md`, which explicitly states that Python amount calculations should never use `float()`. The resulting raw amount is included in the authenticated request. Consequently, the API may quote an amount different from the one entered by the user. ### Attack Path 1. A user or calling application supplies a precision-sensitive decimal through `--amount`. 2. `argparse` conver ...[truncated 1048 chars]
- Remediation
- ## Remediation Suggestions - Accept human-readable amounts as strings rather than floats. - Parse them using `decimal.Decimal` with explicit validation. - Reject negative, zero, non-finite, exponential, and over-precision values as appropriate. - Convert to raw units using exact decimal or integer arithmetic. - Change the CLI argument to preserve the original input: ```python parser.add_argument( "--amount", type=str, required=True, help="Human-readable decimal amount", ) ``` - Use an exact conversion implementation, for example: ```python from decimal import Decimal, InvalidOperation @staticmethod def to_raw_amount(human_amount: str, decimals: int) -> str: if not isinstance(decimals, int) or decimals < 0: raise ValueError("decimals must be a non-negative integer") try: value = Decimal(human_amount) except InvalidOperation as exc: raise ValueError("Invalid decimal amount") from exc if not value.is_finite() or value <= 0: raise ValueError("Amount must be finite and greater than zero") scale = Decimal(10) ** decimals scaled = value * scale if scaled != scaled.to_integral_value(): raise ValueError( f"Amount has more than {decimals} decimal places" ) return str(int(scaled)) ``` - Add tests for values such as `0.1`, the smallest supported token unit, values with excessive precision, and large quantities.
