{"record":{"id":"fc4260f6d568f8d3","repo":"conductor-oss/conductor","slug":"mcp-headers-must-not-contain-cr-or-lf-characters","errorCode":null,"errorMessage":"MCP headers must not contain CR or LF characters","messagePattern":"MCP headers must not contain CR or LF characters","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java","lineNumber":415,"sourceCode":"                request = request.newBuilder().url(target).build();\n            }\n        }\n        throw new RuntimeException(\"MCP server exceeded the redirect limit\");\n    }\n\n    private void addHeaders(Request.Builder builder, Map<String, String> headers) {\n        if (headers == null || headers.isEmpty()) {\n            return;\n        }\n        headers.forEach(\n                (name, value) -> {\n                    if (name == null\n                            || value == null\n                            || name.indexOf('\\r') >= 0\n                            || name.indexOf('\\n') >= 0\n                            || value.indexOf('\\r') >= 0\n                            || value.indexOf('\\n') >= 0) {\n                        throw new IllegalArgumentException(\n                                \"MCP headers must not contain CR or LF characters\");\n                    }\n                    builder.header(name, value);\n                });\n    }\n\n    private boolean hasSensitiveHeaders(Request request) {\n        return request.header(\"Authorization\") != null\n                || request.header(\"Cookie\") != null\n                || request.header(\"Proxy-Authorization\") != null;\n    }\n\n    private boolean isSameOrigin(String firstUrl, String secondUrl) {\n        okhttp3.HttpUrl first = okhttp3.HttpUrl.parse(firstUrl);\n        okhttp3.HttpUrl second = okhttp3.HttpUrl.parse(secondUrl);\n        return first != null\n                && second != null\n                && first.scheme().equalsIgnoreCase(second.scheme())","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java#L397-L433","documentation":"Thrown by addHeaders when any header name or value contains a CR (\\r) or LF (\\n) character. This is a CRLF-injection / header-injection guard: injecting a newline into a header value could let an attacker smuggle additional headers or start a new request. It is a deliberate security check, not a format preference.","triggerScenarios":"The headers map passed to listTools/callTool contains a name or value with \\r or \\n. This happens when header values are built from untrusted/user input without sanitization (e.g. a workflow parameter interpolated into an Authorization or X- header), or when a value accidentally includes a trailing newline.","commonSituations":"Workflow/task input templated into an MCP header without escaping; secrets read from a file that include a trailing newline; multi-line values mistakenly placed in a single header; attempted header injection via user-controlled config.","solutions":["Sanitize/strip CR and LF from any header value derived from untrusted or templated input before building the headers map.","If a value legitimately spans multiple lines, encode it (e.g. base64 or JSON) into a single-line header.","Audit where the headers map is constructed — ensure values come from trusted, single-line sources.","Strip a trailing newline from secrets loaded from files/env."],"exampleFix":"// before\nMap<String,String> headers = Map.of(\"Authorization\", \"Bearer \" + userInput);\n// after\nString safe = userInput == null ? \"\" : userInput.replaceAll(\"[\\\\r\\\\n]\", \"\");\nif (!safe.equals(userInput)) throw new IllegalArgumentException(\"header value contains CR/LF\");\nMap<String,String> headers = Map.of(\"Authorization\", \"Bearer \" + safe);","handlingStrategy":"validation","validationCode":"// Reject CR/LF in any header before building the map.\nMap<String,String> safeHeaders = new LinkedHashMap<>();\nheaders.forEach((k, v) -> {\n    if (k == null || v == null\n            || k.indexOf('\\r') >= 0 || k.indexOf('\\n') >= 0\n            || v.indexOf('\\r') >= 0 || v.indexOf('\\n') >= 0) {\n        throw new IllegalArgumentException(\"Header contains CR/LF: \" + k);\n    }\n    safeHeaders.put(k, v);\n});","typeGuard":null,"tryCatchPattern":"try {\n    mcpService.callTool(serverUrl, toolName, arguments, headers);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"CR or LF\")) {\n        // sanitize header source (often a templated/untrusted value) and retry\n    }\n    throw e;\n}","preventionTips":["Never interpolate untrusted/workflow input directly into header values.","Strip trailing newlines from secrets loaded from files/env.","Encode multi-line values (base64/JSON) rather than placing them raw in a header."],"tags":["mcp","security","header-injection","input-validation","crlf"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}