{"id":"088d70389347c6f6","repo":"gofiber/fiber","slug":"field-must-not-contain-cr-or-lf","errorCode":null,"errorMessage":"field must not contain CR or LF","messagePattern":"field must not contain CR or LF","errorType":"validation","errorClass":"errInvalidField","httpStatus":null,"severity":"warning","filePath":"middleware/sse/event.go","lineNumber":15,"sourceCode":"package sse\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gofiber/utils/v2\"\n)\n\nvar errInvalidField = errors.New(\"field must not contain CR or LF\")\n\n// Event defines a single Server-Sent Event frame.\ntype Event struct {\n\t// Data is written as one or more data fields. Strings and byte slices are\n\t// written as-is; other values are JSON encoded.\n\tData any\n\n\t// ID sets the SSE id field.\n\tID string\n\n\t// Name sets the SSE event field.\n\tName string\n\n\t// Retry sets the SSE retry field for this event.\n\tRetry time.Duration\n}\n\nfunc writeEvent(w *bufio.Writer, event Event, jsonMarshal ...utils.JSONMarshal) error {","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/sse/event.go#L1-L33","documentation":"Declared as errInvalidField in sse/event.go and returned (wrapped as 'sse: invalid id' / 'sse: invalid event') by writeEvent when an Event.ID or Event.Name contains a carriage return or line feed. SSE frames are delimited by line breaks, so a CR/LF in a single-line field would let attacker-controlled data inject additional fields/events — a response-splitting/injection vector. sanitizeField rejects the whole event rather than silently stripping.","triggerScenarios":"Calling stream.Event(sse.Event{ID: '...\\n...', ...}) or setting Event.Name to a value containing \\r or \\n — typically because the id/name came from an un-sanitized user input (e.g. Last-Event-ID echo, a username used as an event name, or a database value with embedded newlines).","commonSituations":"Echoing client-supplied Last-Event-ID into the next event's id; using user display names or record fields as event names; CRLF line endings from Windows-edited content bleeding into event metadata.","solutions":["Strip/validate CR and LF from any id/name before building the Event (the data field may contain newlines; id/event may not).","Treat the wrapped error as fatal for that frame and drop or log it rather than retrying with the same value.","Source-filter: reject or sanitize upstream inputs that populate Event.ID / Event.Name."],"exampleFix":"// before\nstream.Event(sse.Event{ID: lastEventID, Name: topic, Data: payload})\n\n// after — sanitize single-line fields\nid := strings.NewReplacer(\"\\r\", \"\", \"\\n\", \"\").Replace(lastEventID)\ntopic := strings.NewReplacer(\"\\r\", \"\", \"\\n\", \"\").Replace(topic)\nstream.Event(sse.Event{ID: id, Name: topic, Data: payload})","handlingStrategy":"validation","validationCode":"// Strip CR/LF from any value used as Event.ID or Event.Name before building the event.\nfunc sseField(s string) string {\n    return strings.NewReplacer(\"\\r\", \"\", \"\\n\", \"\").Replace(s)\n}\n\nstream.Event(sse.Event{ID: sseField(id), Name: sseField(name), Data: payload})","typeGuard":null,"tryCatchPattern":"if err := stream.Event(ev); err != nil {\n    if strings.Contains(err.Error(), \"field must not contain CR or LF\") {\n        // id/name had an embedded newline — drop the frame\n        return nil\n    }\n    return err\n}","preventionTips":["Never put raw user input into Event.ID or Event.Name — sanitize first.","Remember Event.Data MAY contain newlines; Event.ID and Event.Name may not.","Echoing Last-Event-ID back into Event.ID is the most common cause — sanitize it."],"tags":["sse","validation","injection","security","response-splitting"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}