{"record":{"id":"c83beb6c97da527c","repo":"conductor-oss/conductor","slug":"plannercontext-header-name-contains-cr-lf-r","errorCode":null,"errorMessage":"plannerContext header '${name}' contains CR/LF — rejected to prevent HTTP response splitting","messagePattern":"plannerContext header '(.+?)' contains CR/LF — rejected to prevent HTTP response splitting","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"critical","filePath":"agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java","lineNumber":3222,"sourceCode":"                String fetchRef = prefix + \"_ctx_fetch_\" + i;\n                WorkflowTask fetch = new WorkflowTask();\n                fetch.setName(PlannerContextFetchTask.TASK_TYPE);\n                fetch.setType(PlannerContextFetchTask.TASK_TYPE);\n                fetch.setTaskReferenceName(fetchRef);\n\n                Map<String, Object> headers = new LinkedHashMap<>();\n                Object hdrObj = e.get(\"headers\");\n                if (hdrObj instanceof Map<?, ?> hdrMap) {\n                    for (Map.Entry<?, ?> h : hdrMap.entrySet()) {\n                        // /dg #2: escape ONLY ``${CRED_NAME}`` patterns where\n                        // ``CRED_NAME`` is an identifier — preserves literal\n                        // ``${...}`` substrings that don't look like\n                        // credentials. Also reject CR/LF up-front to close\n                        // the response-splitting injection vector.\n                        String name = String.valueOf(h.getKey());\n                        String value = String.valueOf(h.getValue());\n                        if (value.indexOf('\\r') >= 0 || value.indexOf('\\n') >= 0) {\n                            throw new IllegalArgumentException(\n                                    \"plannerContext header '\"\n                                            + name\n                                            + \"' contains CR/LF — rejected to prevent HTTP response splitting\");\n                        }\n                        headers.put(\n                                name,\n                                CREDENTIAL_PLACEHOLDER\n                                        .matcher(value)\n                                        .replaceAll(\"\\\\${workflow.secrets.$1}\"));\n                    }\n                }\n\n                boolean required = !Boolean.FALSE.equals(e.get(\"required\"));\n                int maxBytes = 16384;\n                if (e.get(\"maxBytes\") instanceof Number n) {\n                    maxBytes = n.intValue();\n                }\n                int ttlSeconds = 60;","sourceCodeStart":3204,"sourceCodeEnd":3240,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java#L3204-L3240","documentation":"Thrown as a security guard when a plannerContext header value contains a carriage return (\\r) or newline (\\n) character. These characters enable HTTP response splitting attacks where an attacker injects additional headers or body content into an HTTP response. The compiler rejects them up-front before the headers are forwarded to the runtime HTTP fetch task.","triggerScenarios":"An AgentConfig with plannerContext entries where at least one entry has a 'headers' map whose value (converted to String via String.valueOf) contains \\r or \\n. This could come from user-supplied input, a credential value that accidentally contains a newline, or a header value crafted from multi-line template strings.","commonSituations":"A credential placeholder value that resolves to a multi-line string (e.g., a PEM key pasted with newlines), a header value sourced from user input that wasn't sanitized, or a copy-paste from a config file that included trailing newlines. Attackers could exploit this to inject Set-Cookie or other headers.","solutions":["Strip or reject CR/LF characters from all plannerContext header values before submitting the agent config.","If the header value legitimately needs newlines (e.g., a multi-line token), URL-encode or base64-encode it first.","Audit credential values for accidental trailing newlines.","Sanitize any user-supplied input that flows into header values."],"exampleFix":"// before: header value contains a newline (e.g. from credential with trailing \\n)\nplannerContext = [{\"url\": \"https://wiki/api\", \"headers\": {\"Authorization\": \"Bearer token\\nX-Injected: evil\"}}]\n// after: CR/LF stripped\nString safeValue = rawHeaderValue.replaceAll(\"[\\\\r\\\\n]\", \"\");\nplannerContext = [{\"url\": \"https://wiki/api\", \"headers\": {\"Authorization\": safeValue}}]","handlingStrategy":"validation","validationCode":"void validatePlannerContextHeaders(AgentConfig config) {\n    if (config.getPlannerContext() == null) return;\n    for (Map<String, Object> entry : config.getPlannerContext()) {\n        Object hdrObj = entry.get(\"headers\");\n        if (hdrObj instanceof Map<?, ?> hdrMap) {\n            for (Map.Entry<?, ?> h : hdrMap.entrySet()) {\n                String value = String.valueOf(h.getValue());\n                if (value.indexOf('\\r') >= 0 || value.indexOf('\\n') >= 0) {\n                    throw new IllegalArgumentException(\n                        \"plannerContext header '\" + h.getKey()\n                        + \"' contains CR/LF — rejected to prevent HTTP response splitting\");\n                }\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    compiler.compile(agentConfig);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"response splitting\")) {\n        // strip CR/LF from the offending header value\n    }\n    throw e;\n}","preventionTips":["Sanitize all header values — strip \\r and \\n before they reach the compiler.","Never put raw multi-line credentials (PEM keys, etc.) directly in header values.","Treat any user-supplied string flowing into HTTP headers as untrusted.","Audit credential values for accidental trailing newlines from copy-paste."],"tags":["security","http-response-splitting","injection","planner-context","headers","agentspan"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}