# Ktor CWE-407 Scan — CLEAN

**Date:** 2026-03-29
**Target:** Ktor framework (`~/git/ktor/`)
**Language:** Kotlin
**Version:** main branch (depth-1 clone)

## Scan Scope

| Area | Files Checked |
|------|--------------|
| Server core | `ktor-server/ktor-server-core/common/src/io/ktor/server/routing/` (RoutingResolveContext, RouteSelector, HostsRoutingBuilder, RoutingBuilder), `response/ResponseHeaders.kt`, `engine/BaseApplicationResponse.kt` |
| HTTP layer | `ktor-http/common/src/io/ktor/http/` (HttpHeaders, ContentTypes, HeaderValueWithParameters, HttpAuthHeader) |
| Plugins (server) | `ktor-server-content-negotiation` (RequestConverter, ResponseConverter), `ktor-server-cors` (CORS.kt, CORSUtils.kt, CORSConfig.kt), `ktor-server-auth` (Authentication, AuthenticationInterceptors) |
| Plugins (client) | `ktor-client-core/common/src/io/ktor/client/engine/HttpClientEngine.kt`, `ktor-client-plugins/ktor-client-auth/` |
| Utilities | `ktor-utils/common/src/io/ktor/util/StringValues.kt`, `ktor-utils/common/src/io/ktor/util/CaseInsensitiveSet.kt` |
| WebSockets | `ktor-shared/ktor-websockets/common/src/io/ktor/websocket/WebSocketExtension.kt` |

## Methodology

Searched for Kotlin `List.contains()`, `Collection.contains()`, `.indexOf()`, `.indexOfFirst()`, and `.any { it == x }` calls nested inside per-request loops. Verified the backing type of each collection at the declaration site.

## Findings

| Location | Pattern | Collection / Backing Type | Verdict |
|----------|---------|--------------------------|---------|
| `HttpHeaders.kt:139` | `UnsafeHeadersArray.any { it.equals(header) }` | `Array<String>` of exactly 2 elements | O(2) = constant |
| `HttpClientEngine.kt:188–190` | `for (ext in requiredCapabilities) { supportedCapabilities.contains(ext) }` | `supportedCapabilities: Set<HttpClientEngineCapability<*>>` | O(1) |
| `CORS.kt:66–78` | `hostsNormalized` and `hostsWithWildcard` lookups per request | Both `HashSet<>` | O(1) |
| `CORSUtils.kt:104–105` | `requestHeaders.all { header in allHeadersSet }` | `allHeadersSet: Set<String>` | O(1) per lookup |
| `ContentTypes.kt:84–105` | `for (patternName in pattern.parameters) { parameter(patternName) }` — inner scan of `this.parameters` | `List<HeaderValueParam>`, Content-Type params bounded at 1-3 entries | O(P²) where P ≤ 3: effectively constant |
| `HttpAuthHeader.kt:318` | `parameters.indexOfFirst { it.name == name }` | Called once per challenge construction, not in per-request loop | O(P) isolated |
| `ResponseConverter.kt:53–55` | `acceptItems.flatMap { registrations.filter { it.contentType.match(contentType) } }` | O(A×R): A ≤ 5 Accept types, R ≤ 3 registrations | O(15) effectively constant |
| `StringValues.kt` | Key lookup via `listForKey(name)` | Hash table (open-addressing with `hashBuckets`/`hashNext`) | O(1) |
| `ResponseHeaders.kt:63` | `managedByEngineHeaders.contains(name)` | `Set<String>` | O(1) |

## Notable Non-Defects

- **`HttpHeaders.isUnsafe()`**: `UnsafeHeadersArray` is a 2-element compile-time constant array. Even though it is scanned linearly, the bound is fixed at 2 and will never grow with request load.

- **`ContentType.match()`** (`ContentTypes.kt:84`): Outer loop over `pattern.parameters`, inner `parameter()` scans `this.parameters`. Both are Content-Type parameter lists, bounded in practice to 2-3 entries (e.g., `charset=utf-8`, `boundary=xxx`).

- **`ResponseConverter.kt:53–55`**: `acceptItems.flatMap { registrations.filter { ... } }` is O(A×R). Both A (Accept header items) and R (content negotiation registrations) are tiny in all real deployments. The subsequent `.distinct()` uses Kotlin's `LinkedHashSet`-backed dedup.

## Result

**CLEAN** — no CWE-407 defects confirmed in Ktor.

The codebase uses hash-backed sets (`Set<>`, `HashSet<>`, `CaseInsensitiveSet`) for all membership checks in hot per-request paths. The few cases of linear list scan (`isUnsafe`, `parameter()`, `withReplacedParameter`) operate on collections bounded by a small protocol-defined constant.
- CORSConfig.kt: CaseInsensitiveSet (Set impl) — O(1)
- CallId.kt: dictionarySet: Set<Char> — O(1)

Scan date: 2026-03-29
