T09 · Insecure Skill Coding Practices
- Location
scripts/parser.py:391- Finding
Unbounded Loan Period Input Can Cause Resource Exhaustion
- Content
View full analysis
Vulnerability Details
File Location:
scripts/parser.py:391-399, with affected allocation and iteration sinks inscripts/calculator.py:122, 180, 227, 279, 296, 388, 392, 439, 443-449, 479, 482
Vulnerability Type: Uncontrolled resource consumption and insufficient numeric input validation
Risk Level: MediumVulnerable Code
python # scripts/parser.py:391-399 # Period count m = re.search(r'分?(\d+)\s*期', text) if m: return int(m.group(1)) # Months m = re.search(r'(\d+)\s*个月', text) if m: return int(m.group(1))The resulting unbounded value is used directly in memory allocations and loops. Representative sinks include:
python # scripts/calculator.py:275-296 total_fee = monthly_fee * months total_payment = principal + total_fee # Calculate the actual annualized rate using IRR cash_flows = [-principal] + [monthly_payment] * months irr = calculate_irr(cash_flows) if irr is None: return { 'error': 'IRR calculation failed', 'monthly_payment': round(monthly_payment, 2), 'total_fee': round(total_fee, 2), 'total_payment': round(total_payment, 2), } # Compound annualized APR = (1+irr)^12 - 1 apr = (1 + irr) ** 12 - 1 nominal_rate = monthly_fee_rate * 12 fee_multiplier = apr / nominal_rate if nominal_rate > 0 else float('inf') schedule = [] for i in range(1, months + 1): schedule.append({ 'period': i, 'payment': round(monthly_payment, 2), 'principal': round(monthly_principal, 2), 'fee': round(monthly_fee, 2), 'remaining': round(principal - monthly_principal * i, 2), })Technical Analysis
The natural-language parser accepts any sequence of digits as a loan period and converts it to an integer without checking whether the value is positive or within a practical maximum. The calculator subsequently uses this user-controlled value for list multiplication, cash-flow construction, repayment-schedule generation, and repeated numerical o ...[truncated 1915 chars]
- Remediation
View remediation
Remediation Suggestions
- Validate parsed period counts immediately after extraction:
python MAX_MONTHS = 1200 def _validate_months(months: int) -> int: if not isinstance(months, int) or isinstance(months, bool): raise ValueError("The period count must be an integer.") if not 1 <= months <= MAX_MONTHS: raise ValueError( f"The period count must be between 1 and {MAX_MONTHS}." ) return months-
Apply equivalent validation inside every public calculator function. Parser-side validation alone is insufficient because callers may invoke calculator functions directly.
-
Impose practical bounds on all user-controlled numeric inputs, including
actual_days, principal, rates, payment amounts, and iteration limits. Reject non-finite values by usingmath.isfinite(). -
Return a controlled validation error rather than allowing division-by-zero,
OverflowError, orMemoryErrorto terminate report generation. -
Avoid generating complete schedules when they are not required. Since reports display only the first six and last periods, calculate those entries directly or enforce a strict schedule-size limit.
-
Add regression tests for:
months = 0- Negative direct API values
- Extremely large period counts
- Values just below, at, and above the configured maximum
- Excessively large
actual_days - Non-finite direct inputs such as
float("inf")andfloat("nan")
-
Run calculations under process-level CPU and memory limits as defense in depth.
