diff --git a/features/lsp_header_injection_security.feature b/features/lsp_header_injection_security.feature new file mode 100644 index 000000000..9b71c5cd0 --- /dev/null +++ b/features/lsp_header_injection_security.feature @@ -0,0 +1,34 @@ +@tdd_issue +@tdd_issue_7112 +Feature: LSP transport header injection vulnerability (issue #7112) + + The _read_one_message() method in the LSP stdio transport must strictly + enforce ASCII-only headers. Non-ASCII bytes silently replaced by the + old ``errors="replace"`` path could be used to manipulate message + boundaries and desynchronise the protocol. + +@tdd_issue_7112 Scenario: Non-ASCII byte in Content-Length value raises LspError + Given a Transport mock with BytesIO stream containing b"Content-Length: 10\xc0\r\n\r\nhello\x00world" + When _read_one_message() is invoked + Then it should raise an LspError exception + And the error message must contain "non-ASCII" + +@tdd_issue_7112 Scenario: Non-ASCII byte in a valid Content-Length name raises LspError + Given a Transport mock with BytesIO stream containing b"Content-Length\xef\x08: 99\r\n\r\n{\"jsonrpc\":\"2.0\"}" + When _read_one_message() is invoked + Then it should raise an LspError exception + And the error message must contain "non-ASCII" + +@tdd_issue_7112 Scenario: Non-ASCII byte in an unrecognized header raises LspError + Given a Transport mock with BytesIO stream containing b"Cache-Control\xef\x08: no-cache\r\nContent-Length: 5\r\n\r\n{\"id\":1}" + When _read_one_message() is invoked + Then it should raise an LspError exception + And the error message must contain "non-ASCII" + +@tdd_issue_7112 Scenario: Valid ASCII headers are processed correctly + Given a Transport mock with BytesIO stream containing b"Content-Length: 46\r\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"success\"}" + When _read_one_message() is invoked + Then it should return a parsed JSON dict + And the result must contain "jsonrpc" == "2.0" + And the result must contain "id" == 1 + And the result must contain "result" == "success"