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/. */
5 #ifndef CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
6 #define CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
20 #include "content_analysis/sdk/analysis.pb.h"
21 #include "content_analysis/sdk/analysis_agent.h"
22 #include "agent/src/event_win.h"
26 // Have to use a "Mode_" prefix to avoid preprocessing problems in StringToMode
27 #define AGENT_MODE(name) Mode_##name,
32 extern std::map
<std::string
, Mode
> sStringToMode
;
33 extern std::map
<Mode
, std::string
> sModeToString
;
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()) {
45 OVERLAPPED overlapped
;
46 memset(&overlapped
, 0, sizeof(overlapped
));
47 overlapped
.hEvent
= CreateEvent(/*securityAttr=*/nullptr,
49 /*initialState=*/FALSE
,
51 if (overlapped
.hEvent
== nullptr) {
52 return GetLastError();
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 "
60 if (WriteFile(pipe
, cursor
, size
, /*written=*/nullptr, &overlapped
)) {
61 std::cout
<< "[demo] WriteBigMessageToPipe: success" << std::endl
;
66 // If an I/O is not pending, return the error.
68 if (err
!= ERROR_IO_PENDING
) {
70 << "[demo] WriteBigMessageToPipe: returning error from WriteFile "
76 if (!GetOverlappedResult(pipe
, &overlapped
, &written
, /*wait=*/TRUE
)) {
78 std::cout
<< "[demo] WriteBigMessageToPipe: returning error from "
79 "GetOverlappedREsult "
84 // reset err for the next loop iteration
86 std::cout
<< "[demo] WriteBigMessageToPipe: bottom of loop, wrote "
87 << written
<< std::endl
;
92 CloseHandle(overlapped
.hEvent
);
96 // An AgentEventHandler that does various misbehaving things
97 class MisbehavingHandler final
: public Handler
{
99 using Event
= content_analysis::sdk::ContentAnalysisEvent
;
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
;
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
)));
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
)),
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
*>(
134 HANDLE pipe
= eventWin
->Pipe();
135 std::string
s(reinterpret_cast<const char*>(bytes
), N
);
136 return WriteBigMessageToPipe(pipe
, s
);
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
;
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");
155 Mode::Mode_invalidUtf8StringStartByteIsContinuationByte
) {
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");
162 Mode::Mode_invalidUtf8StringEndsInMiddleOfMultibyteSequence
) {
163 // f0 byte indicates there should be 3 bytes following it, but here
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)
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);
188 ::content_analysis::sdk::ContentAnalysisResponse_Result_Status>(
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
*>(
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
207 eventWin
->SetResponseSent();
208 } else if (mode_
== Mode::Mode_resultWithInvalidStatus
) {
209 content_analysis::sdk::ContentAnalysisEventWin
* eventWin
=
210 static_cast<content_analysis::sdk::ContentAnalysisEventWin
*>(
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
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.
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
);
290 #endif // CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_