Fix IllegalFormatConversionException StringModuleImpl#onStringFormat by jandro996 · Pull Request #9907 · DataDog/dd-trace-java
What Does This Do
Fixes IllegalFormatConversionException in IAST when Scala's BigDecimal/BigInt are used with String.format().
Added unwrapScalaNumbers() in StringOpsCallSite that:
- Detects
scala.math.ScalaNumberinstances via reflection - Calls
underlying()method to extract Java numeric types (java.math.BigDecimal,java.math.BigInteger) - Applies unwrapping before passing arguments to onStringFormat
This ensures type compatibility while preserving IAST taint tracking.
Enhanced formatValue() error handling to catch IllegalFormatException and log telemetry with parameter type information. This provides context for detecting similar format conversion bugs in the future while maintaining existing exception behavior.
Motivation
stack trace
java.util.IllegalFormatConversionException
at java.base/java.util.Formatter$FormatSpecifier.failConversion(Unknown Source)
at java.base/java.util.Formatter$FormatSpecifier.printFloat(Unknown Source)
at java.base/java.util.Formatter$FormatSpecifier.print(Unknown Source)
at java.base/java.util.Formatter.format(Unknown Source)
at java.base/java.util.Formatter.format(Unknown Source)
at java.base/java.lang.String.format(Unknown Source)
at com.datadog.iast.propagation.StringModuleImpl.onStringFormat(StringModuleImpl.java:537)
at com.datadog.iast.propagation.StringModuleImpl.onStringFormat(StringModuleImpl.java:487)
at datadog.trace.instrumentation.scala.StringOpsCallSite.afterInterpolation(StringOpsCallSite.java:50)
at (redacted: 22 frames)
at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source)
Additional Notes
Scala's String.format() internally calls unwrapArg() to convert scala.math.BigDecimal → java.math.BigDecimal before formatting. However, IAST's @CallSite.After interceptor captures arguments after Scala execution completes, receiving the original Scala types. This causes IllegalFormatConversionException when StringModuleImpl#formatValue attempts to format with incompatible types.
Contributor Checklist
- Format the title according the contribution guidelines
- Assign the
type:and (comp:orinst:) labels in addition to any useful labels - Don't use
close,fixor any linking keywords when referencing an issue.
Usesolvesinstead, and assign the PR milestone to the issue - Update the CODEOWNERS file on source file addition, move, or deletion
- Update the public documentation in case of new configuration flag or behavior
Jira ticket: APPSEC-59883