conductor-oss/conductor · critical · IllegalArgumentException
plannerContext header '${name}' contains CR/LF — rejected to
Error message
plannerContext header '${name}' contains CR/LF — rejected to prevent HTTP response splitting What it means
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.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/compiler/MultiAgentCompiler.java:3222
String fetchRef = prefix + "_ctx_fetch_" + i;
WorkflowTask fetch = new WorkflowTask();
fetch.setName(PlannerContextFetchTask.TASK_TYPE);
fetch.setType(PlannerContextFetchTask.TASK_TYPE);
fetch.setTaskReferenceName(fetchRef);
Map<String, Object> headers = new LinkedHashMap<>();
Object hdrObj = e.get("headers");
if (hdrObj instanceof Map<?, ?> hdrMap) {
for (Map.Entry<?, ?> h : hdrMap.entrySet()) {
// /dg #2: escape ONLY ``${CRED_NAME}`` patterns where
// ``CRED_NAME`` is an identifier — preserves literal
// ``${...}`` substrings that don't look like
// credentials. Also reject CR/LF up-front to close
// the response-splitting injection vector.
String name = String.valueOf(h.getKey());
String value = String.valueOf(h.getValue());
if (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0) {
throw new IllegalArgumentException(
"plannerContext header '"
+ name
+ "' contains CR/LF — rejected to prevent HTTP response splitting");
}
headers.put(
name,
CREDENTIAL_PLACEHOLDER
.matcher(value)
.replaceAll("\\${workflow.secrets.$1}"));
}
}
boolean required = !Boolean.FALSE.equals(e.get("required"));
int maxBytes = 16384;
if (e.get("maxBytes") instanceof Number n) {
maxBytes = n.intValue();
}
int ttlSeconds = 60;View on GitHub (pinned to cf7c3e4a8a)
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.
Example fix
// before: header value contains a newline (e.g. from credential with trailing \n)
plannerContext = [{"url": "https://wiki/api", "headers": {"Authorization": "Bearer token\nX-Injected: evil"}}]
// after: CR/LF stripped
String safeValue = rawHeaderValue.replaceAll("[\\r\\n]", "");
plannerContext = [{"url": "https://wiki/api", "headers": {"Authorization": safeValue}}] Defensive patterns
Strategy: validation
Validate before calling
void validatePlannerContextHeaders(AgentConfig config) {
if (config.getPlannerContext() == null) return;
for (Map<String, Object> entry : config.getPlannerContext()) {
Object hdrObj = entry.get("headers");
if (hdrObj instanceof Map<?, ?> hdrMap) {
for (Map.Entry<?, ?> h : hdrMap.entrySet()) {
String value = String.valueOf(h.getValue());
if (value.indexOf('\r') >= 0 || value.indexOf('\n') >= 0) {
throw new IllegalArgumentException(
"plannerContext header '" + h.getKey()
+ "' contains CR/LF — rejected to prevent HTTP response splitting");
}
}
}
}
} Try / catch
try {
compiler.compile(agentConfig);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("response splitting")) {
// strip CR/LF from the offending header value
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Access denied: path traversal sequences are not allowed
- SWARM handoff type must be on_tool_result, on_text_mention,
- SWARM handoff target must name a swarm agent: ${handoff.getT
- on_tool_result requires toolName and resultContains
- on_text_mention requires text
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/c83beb6c97da527c.
Report an issue: GitHub.