handler_misbehaving.h (11892B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_ 6 #define CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_ 7 8 #include <time.h> 9 10 #include <algorithm> 11 #include <chrono> 12 #include <fstream> 13 #include <map> 14 #include <iostream> 15 #include <utility> 16 #include <vector> 17 #include <regex> 18 #include <windows.h> 19 20 #include "content_analysis/sdk/analysis.pb.h" 21 #include "content_analysis/sdk/analysis_agent.h" 22 #include "agent/src/event_win.h" 23 #include "handler.h" 24 25 enum class Mode { 26 // Have to use a "Mode_" prefix to avoid preprocessing problems in StringToMode 27 #define AGENT_MODE(name) Mode_##name, 28 #include "modes.h" 29 #undef AGENT_MODE 30 }; 31 32 extern std::map<std::string, Mode> sStringToMode; 33 extern std::map<Mode, std::string> sModeToString; 34 35 // Writes a string to the pipe. Returns ERROR_SUCCESS if successful, else 36 // returns GetLastError() of the write. This function does not return until 37 // the entire message has been sent (or an error occurs). 38 static DWORD WriteBigMessageToPipe(HANDLE pipe, const std::string& message) { 39 std::cout << "[demo] WriteBigMessageToPipe top, message size is " 40 << message.size() << std::endl; 41 if (message.empty()) { 42 return ERROR_SUCCESS; 43 } 44 45 OVERLAPPED overlapped; 46 memset(&overlapped, 0, sizeof(overlapped)); 47 overlapped.hEvent = CreateEvent(/*securityAttr=*/nullptr, 48 /*manualReset=*/TRUE, 49 /*initialState=*/FALSE, 50 /*name=*/nullptr); 51 if (overlapped.hEvent == nullptr) { 52 return GetLastError(); 53 } 54 55 DWORD err = ERROR_SUCCESS; 56 const char* cursor = message.data(); 57 for (DWORD size = message.length(); size > 0;) { 58 std::cout << "[demo] WriteBigMessageToPipe top of loop, remaining size " 59 << size << std::endl; 60 if (WriteFile(pipe, cursor, size, /*written=*/nullptr, &overlapped)) { 61 std::cout << "[demo] WriteBigMessageToPipe: success" << std::endl; 62 err = ERROR_SUCCESS; 63 break; 64 } 65 66 // If an I/O is not pending, return the error. 67 err = GetLastError(); 68 if (err != ERROR_IO_PENDING) { 69 std::cout 70 << "[demo] WriteBigMessageToPipe: returning error from WriteFile " 71 << err << std::endl; 72 break; 73 } 74 75 DWORD written; 76 if (!GetOverlappedResult(pipe, &overlapped, &written, /*wait=*/TRUE)) { 77 err = GetLastError(); 78 std::cout << "[demo] WriteBigMessageToPipe: returning error from " 79 "GetOverlappedREsult " 80 << err << std::endl; 81 break; 82 } 83 84 // reset err for the next loop iteration 85 err = ERROR_SUCCESS; 86 std::cout << "[demo] WriteBigMessageToPipe: bottom of loop, wrote " 87 << written << std::endl; 88 cursor += written; 89 size -= written; 90 } 91 92 CloseHandle(overlapped.hEvent); 93 return err; 94 } 95 96 // An AgentEventHandler that does various misbehaving things 97 class MisbehavingHandler final : public Handler { 98 public: 99 using Event = content_analysis::sdk::ContentAnalysisEvent; 100 101 static 102 std::unique_ptr<AgentEventHandler> Create( 103 const std::string& modeStr, 104 std::vector<unsigned long>&& delays, 105 const std::string& print_data_file_path, 106 RegexArray&& toBlock = RegexArray(), 107 RegexArray&& toWarn = RegexArray(), 108 RegexArray&& toReport = RegexArray()) { 109 auto it = sStringToMode.find(modeStr); 110 if (it == sStringToMode.end()) { 111 std::cout << "\"" << modeStr << "\"" 112 << " is not a valid mode!" << std::endl; 113 return nullptr; 114 } 115 116 return std::unique_ptr<AgentEventHandler>(new MisbehavingHandler(it->second, std::move(delays), print_data_file_path, std::move(toBlock), std::move(toWarn), std::move(toReport))); 117 } 118 119 private: 120 MisbehavingHandler(Mode mode, std::vector<unsigned long>&& delays, const std::string& print_data_file_path, 121 RegexArray&& toBlock = RegexArray(), 122 RegexArray&& toWarn = RegexArray(), 123 RegexArray&& toReport = RegexArray()) : 124 Handler(std::move(delays), print_data_file_path, std::move(toBlock), std::move(toWarn), std::move(toReport)), 125 mode_(mode) {} 126 127 128 template <size_t N> 129 DWORD SendBytesOverPipe(const unsigned char (&bytes)[N], 130 const std::unique_ptr<Event>& event) { 131 content_analysis::sdk::ContentAnalysisEventWin* eventWin = 132 static_cast<content_analysis::sdk::ContentAnalysisEventWin*>( 133 event.get()); 134 HANDLE pipe = eventWin->Pipe(); 135 std::string s(reinterpret_cast<const char*>(bytes), N); 136 return WriteBigMessageToPipe(pipe, s); 137 } 138 139 bool SetCustomResponse(AtomicCout& aout, std::unique_ptr<Event>& event) override { 140 std::cout << std::endl << "----------" << std::endl << std::endl; 141 std::cout << "Mode is " << sModeToString[mode_] << std::endl; 142 143 bool handled = true; 144 if (mode_ == Mode::Mode_largeResponse) { 145 for (size_t i = 0; i < 1000; ++i) { 146 content_analysis::sdk::ContentAnalysisResponse_Result* result = 147 event->GetResponse().add_results(); 148 result->set_tag("someTag"); 149 content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule* 150 triggeredRule = result->add_triggered_rules(); 151 triggeredRule->set_rule_id("some_id"); 152 triggeredRule->set_rule_name("some_name"); 153 } 154 } else if (mode_ == 155 Mode::Mode_invalidUtf8StringStartByteIsContinuationByte) { 156 // protobuf docs say 157 // "A string must always contain UTF-8 encoded text." 158 // So let's try something invalid 159 // Anything with bits 10xxxxxx is only a continuation code point 160 event->GetResponse().set_request_token("\x80\x41\x41\x41"); 161 } else if (mode_ == 162 Mode::Mode_invalidUtf8StringEndsInMiddleOfMultibyteSequence) { 163 // f0 byte indicates there should be 3 bytes following it, but here 164 // there are only 2 165 event->GetResponse().set_request_token("\x41\xf0\x90\x8d"); 166 } else if (mode_ == Mode::Mode_invalidUtf8StringOverlongEncoding) { 167 // codepoint U+20AC, should be encoded in 3 bytes (E2 82 AC) 168 // instead of 4 169 event->GetResponse().set_request_token("\xf0\x82\x82\xac"); 170 } else if (mode_ == Mode::Mode_invalidUtf8StringMultibyteSequenceTooShort) { 171 // f0 byte indicates there should be 3 bytes following it, but here 172 // there are only 2 (\x41 is not a continuation byte) 173 event->GetResponse().set_request_token("\xf0\x90\x8d\x41"); 174 } else if (mode_ == Mode::Mode_invalidUtf8StringDecodesToInvalidCodePoint) { 175 // decodes to U+1FFFFF, but only up to U+10FFFF is a valid code point 176 event->GetResponse().set_request_token("\xf7\xbf\xbf\xbf"); 177 } else if (mode_ == Mode::Mode_stringWithEmbeddedNull) { 178 event->GetResponse().set_request_token("\x41\x00\x41"); 179 } else if (mode_ == Mode::Mode_zeroResults) { 180 event->GetResponse().clear_results(); 181 } else if (mode_ == Mode::Mode_resultWithInvalidStatus) { 182 // This causes an assertion failure and the process exits 183 // So we just serialize this ourselves in SendCustomResponse() 184 /*content_analysis::sdk::ContentAnalysisResponse_Result* result = 185 event->GetResponse().mutable_results(0); 186 result->set_status( 187 static_cast< 188 ::content_analysis::sdk::ContentAnalysisResponse_Result_Status>( 189 100));*/ 190 } else { 191 handled = false; 192 } 193 return handled; 194 } 195 196 bool SendCustomResponse(std::unique_ptr<Event>& event) override { 197 if (mode_ == Mode::Mode_largeResponse) { 198 content_analysis::sdk::ContentAnalysisEventWin* eventWin = 199 static_cast<content_analysis::sdk::ContentAnalysisEventWin*>( 200 event.get()); 201 HANDLE pipe = eventWin->Pipe(); 202 std::cout << "largeResponse about to write" << std::endl; 203 DWORD result = WriteBigMessageToPipe( 204 pipe, eventWin->SerializeStringToSendToBrowser()); 205 std::cout << "largeResponse done writing with error " << result 206 << std::endl; 207 eventWin->SetResponseSent(); 208 } else if (mode_ == Mode::Mode_resultWithInvalidStatus) { 209 content_analysis::sdk::ContentAnalysisEventWin* eventWin = 210 static_cast<content_analysis::sdk::ContentAnalysisEventWin*>( 211 event.get()); 212 HANDLE pipe = eventWin->Pipe(); 213 std::string serializedString = eventWin->SerializeStringToSendToBrowser(); 214 // The last byte is the status value. Set it to 100 215 serializedString[serializedString.length() - 1] = 100; 216 WriteBigMessageToPipe(pipe, serializedString); 217 } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfString) { 218 unsigned char bytes[5]; 219 bytes[0] = 10; // field 1 (request_token), LEN encoding 220 bytes[1] = 13; // length 13 221 bytes[2] = 65; // "A" 222 bytes[3] = 66; // "B" 223 bytes[4] = 67; // "C" 224 SendBytesOverPipe(bytes, event); 225 } else if (mode_ == Mode::Mode_messageWithInvalidWireType) { 226 unsigned char bytes[5]; 227 bytes[0] = 15; // field 1 (request_token), "7" encoding (invalid value) 228 bytes[1] = 3; // length 3 229 bytes[2] = 65; // "A" 230 bytes[3] = 66; // "B" 231 bytes[4] = 67; // "C" 232 SendBytesOverPipe(bytes, event); 233 } else if (mode_ == Mode::Mode_messageWithUnusedFieldNumber) { 234 unsigned char bytes[5]; 235 bytes[0] = 82; // field 10 (this is invalid), LEN encoding 236 bytes[1] = 3; // length 3 237 bytes[2] = 65; // "A" 238 bytes[3] = 66; // "B" 239 bytes[4] = 67; // "C" 240 SendBytesOverPipe(bytes, event); 241 } else if (mode_ == Mode::Mode_messageWithWrongStringWireType) { 242 unsigned char bytes[2]; 243 bytes[0] = 10; // field 1 (request_token), VARINT encoding (but should be 244 // a string/LEN) 245 bytes[1] = 42; // value 42 246 SendBytesOverPipe(bytes, event); 247 } else if (mode_ == Mode::Mode_messageWithZeroTag) { 248 unsigned char bytes[1]; 249 // The protobuf deserialization code seems to handle this 250 // in a special case. 251 bytes[0] = 0; 252 SendBytesOverPipe(bytes, event); 253 } else if (mode_ == Mode::Mode_messageWithZeroFieldButNonzeroWireType) { 254 // The protobuf deserialization code seems to handle this 255 // in a special case. 256 unsigned char bytes[5]; 257 bytes[0] = 2; // field 0 (invalid), LEN encoding 258 bytes[1] = 3; // length 13 259 bytes[2] = 65; // "A" 260 bytes[3] = 66; // "B" 261 bytes[4] = 67; // "C" 262 SendBytesOverPipe(bytes, event); 263 } else if (mode_ == Mode::Mode_messageWithGroupEnd) { 264 // GROUP_ENDs are obsolete and the deserialization code 265 // handles them in a special case. 266 unsigned char bytes[1]; 267 bytes[0] = 12; // field 1 (request_token), GROUP_END encoding 268 SendBytesOverPipe(bytes, event); 269 } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfVarint) { 270 unsigned char bytes[2]; 271 bytes[0] = 16; // field 2 (status), VARINT encoding 272 bytes[1] = 128; // high bit is set, indicating there 273 // should be a byte after this 274 SendBytesOverPipe(bytes, event); 275 } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfTag) { 276 unsigned char bytes[1]; 277 bytes[0] = 128; // tag is actually encoded as a VARINT, so set the high 278 // bit, indicating there should be a byte after this 279 SendBytesOverPipe(bytes, event); 280 } else { 281 return false; 282 } 283 return true; 284 } 285 286 private: 287 Mode mode_; 288 }; 289 290 #endif // CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_