cookiejack15 opened a new issue, #146:
URL: https://github.com/apache/logging-log4j-kotlin/issues/146

   ## Bug Description
   
   `ContextStack.push(message, vararg args)` delegates to 
`ThreadContext.push(message, args)` **without the Kotlin spread operator 
(`*`)**. This causes the `args` array to be wrapped inside another array when 
passed to the Java varargs method, resulting in systematically corrupted 
context stack entries for every parameterized call.
   
   **Affected file:** 
`log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/ContextStack.kt`
 (line 106)
   **Introduced in:** 1.3.0 (when ContextStack facade was added)
   **Affected versions:** 1.3.0, 1.4.0, 1.5.0 (current latest), 1.6.0-SNAPSHOT
   
   ## Root Cause
   
   In Kotlin, when passing an array to a Java varargs method, the spread 
operator `*` is required to unpack the array into individual varargs elements. 
Without it, the Kotlin compiler wraps the entire array as a single element of a 
new array.
   
   **Current code (buggy):**
   ```kotlin
   fun push(message: String, vararg args: Any?) = ThreadContext.push(message, 
args)
   ```
   
   **Expected code (fix):**
   ```kotlin
   fun push(message: String, vararg args: Any?) = ThreadContext.push(message, 
*args)
   ```
   
   ## What Happens
   
   When a user calls:
   ```kotlin
   ContextStack.push("requestId={} userId={}", "req-123", "user-42")
   ```
   
   1. Kotlin creates the vararg array: `args = Object[]{"req-123", "user-42"}`
   2. Without `*`, the call compiles to: `ThreadContext.push(message, new 
Object[] { new Object[]{"req-123", "user-42"} })`
   3. The args array is wrapped inside another array — Java's varargs receives 
**one** argument (the inner array) instead of two separate strings
   
   **Actual output:** `requestId=[req-123, user-42] userId={}`
   **Expected output:** `requestId=req-123 userId=user-42`
   
   Even single-parameter calls are corrupted:
   ```kotlin
   ContextStack.push("userId={}", "user-42")
   // Actual:   "userId=[user-42]"
   // Expected: "userId=user-42"
   ```
   
   ## Proof of Concept
   
   ```kotlin
   import org.apache.logging.log4j.kotlin.ContextStack
   
   fun main() {
       // Multi-parameter: all values merged, second value lost
       ContextStack.push("requestId={} userId={}", "req-abc-123", "user-42")
       println(ContextStack.peek())
       // Actual:   "requestId=[req-abc-123, user-42] userId={}"
       // Expected: "requestId=req-abc-123 userId=user-42"
       ContextStack.pop()
   
       // Single parameter: brackets injected
       ContextStack.push("userId={}", "user-42")
       println(ContextStack.peek())
       // Actual:   "userId=[user-42]"
       // Expected: "userId=user-42"
       ContextStack.pop()
   
       // Three parameters: 2nd and 3rd entirely lost
       ContextStack.push("a={} b={} c={}", "1", "2", "3")
       println(ContextStack.peek())
       // Actual:   "a=[1, 2, 3] b={} c={}"
       // Expected: "a=1 b=2 c=3"
       ContextStack.pop()
   }
   ```
   
   ## Unit Test
   
   ```kotlin
   import org.apache.logging.log4j.kotlin.ContextStack
   import org.junit.jupiter.api.AfterEach
   import org.junit.jupiter.api.Test
   import kotlin.test.assertEquals
   
   class ContextStackPushTest {
   
       @AfterEach
       fun tearDown() {
           ContextStack.clear()
       }
   
       @Test
       fun `parameterized push loses second parameter`() {
           ContextStack.push("user={} action={}", "admin", "login")
           assertEquals("user=admin action=login", ContextStack.peek())
       }
   
       @Test
       fun `single parameter push corrupts value format`() {
           ContextStack.push("user={}", "admin")
           assertEquals("user=admin", ContextStack.peek())
       }
   
       @Test
       fun `three parameters - values lost from designated fields`() {
           ContextStack.push("a={} b={} c={}", "1", "2", "3")
           assertEquals("a=1 b=2 c=3", ContextStack.peek())
       }
   }
   ```
   
   ## Proposed Fix
   
   One-character fix — add the spread operator `*`:
   
   ```diff
   - fun push(message: String, vararg args: Any?) = ThreadContext.push(message, 
args)
   + fun push(message: String, vararg args: Any?) = ThreadContext.push(message, 
*args)
   ```
   
   ## Impact
   
   - The bug is **systematic** — 100% of parameterized `ContextStack.push()` 
calls produce corrupted output
   - The bug is **silent** — no exception, no warning, corrupted data is 
silently pushed
   - Context data used for log correlation, audit trails, and request tracing 
is corrupted or lost
   - The method has been non-functional for its intended purpose since its 
introduction in 1.3.0


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to