Challenge 2: Validate a Chat Message Shape — Possible Solution ==================================================================== function validateChatMessage(data) { if (typeof data !== "object" || data === null) { return false; } const keys = Object.keys(data); const allowedKeys = ["roomName", "text"]; const hasOnlyAllowedKeys = keys.every((key) => allowedKeys.includes(key)); if (!hasOnlyAllowedKeys || keys.length !== allowedKeys.length) { return false; // rejects both unexpected extra fields and missing fields } if (typeof data.roomName !== "string" || data.roomName.trim().length === 0) { return false; } if ( typeof data.text !== "string" || data.text.trim().length === 0 || data.text.length > 500 ) { return false; } return true; } WHY THIS WORKS -------------- - The function checks that "data" is a genuine object before touching any of its properties — a malformed message could just as easily be a string, a number, or null after JSON.parse, and accessing .roomName on something that isn't an object would either throw or silently return undefined depending on the value. - Checking BOTH that every key in "data" is in allowedKeys AND that the key count matches exactly is what catches unexpected extra fields — checking allowed keys alone wouldn't reject a message that has all the right fields PLUS something extra like an unexpected "isAdmin: true" field a malicious client might try to sneak in for the server to mistakenly trust later. - roomName and text are each checked for BOTH the correct type (string) and a sensible non-empty value — a message with "text": "" or "roomName": 42 would pass a type-only check written carelessly, but represents exactly the kind of "technically valid JSON, still wrong shape" case this chapter warns about. - The 500-character cap on text mirrors the chapter's own example directly — an unbounded string field is an easy way for a malicious or buggy client to send an enormous payload that gets broadcast to an entire room, wasting bandwidth and processing for everyone else connected to it. - Returning a plain boolean (rather than throwing) means the caller (the "message" event handler) can simply do "if (!validateChatMessage(data)) return;" and silently drop anything invalid, exactly as this chapter's own message-handling example does for malformed JSON.