Bug 1811076: Part 4 - Add gtests for correctly-behaving and misbehaving DLP agents

Unit tests for mozilla::contentanalysis::ContentAnalysis class.
ContentAnalysis must remain responsive, regardless of the behavior of the
agent.

Differential Revision: https://phabricator.services.mozilla.com/D189570
This commit is contained in:
Greg Stoll 2023-10-31 16:38:55 +00:00
Родитель b13d7be12a
Коммит d4900df401
17 изменённых файлов: 1288 добавлений и 13 удалений

Просмотреть файл

@ -41,6 +41,7 @@ TEST_HARNESS_BINS = [
"crashinject",
"geckodriver",
"http3server",
"content_analysis_sdk_agent",
"minidumpwriter",
"pk12util",
"screenshot",
@ -459,6 +460,7 @@ ARCHIVE_FILES = {
"chrome/**",
"chrome.manifest",
"components/**",
"content_analysis_sdk_agent",
"http3server",
"*.ini",
"localization/**",

Просмотреть файл

@ -795,6 +795,7 @@ class WinArtifactJob(ArtifactJob):
("bin/ssltunnel.exe", ("bin", "bin")),
("bin/xpcshell.exe", ("bin", "bin")),
("bin/http3server.exe", ("bin", "bin")),
("bin/content_analysis_sdk_agent.exe", ("bin", "bin")),
("bin/plugins/gmp-*/*/*", ("bin/plugins", "bin")),
("bin/plugins/*", ("bin/plugins", "plugins")),
("bin/components/*", ("bin/components", "bin/components")),

Просмотреть файл

@ -28,6 +28,12 @@ class ContentAnalysisEventWin : public ContentAnalysisEventBase {
ResultCode Close() override;
ResultCode Send() override;
std::string DebugString() const override;
std::string SerializeStringToSendToBrowser() {
return agent_to_chrome()->SerializeAsString();
}
void SetResponseSent() { response_sent_ = true; }
HANDLE Pipe() const { return hPipe_; }
private:
void Shutdown();

Просмотреть файл

@ -10,6 +10,9 @@
#include "content_analysis/sdk/analysis_agent.h"
#include "demo/handler.h"
#include "demo/handler_misbehaving.h"
using namespace content_analysis::sdk;
// Different paths are used depending on whether this agent should run as a
// use specific agent or not. These values are chosen to match the test
@ -25,6 +28,8 @@ unsigned long delay = 0; // In seconds.
unsigned long num_threads = 8u;
std::string save_print_data_path = "";
RegexArray toBlock, toWarn, toReport;
static bool useMisbehavingHandler = false;
static std::string modeStr;
// Command line parameters.
constexpr const char* kArgDelaySpecific = "--delay=";
@ -35,9 +40,22 @@ constexpr const char* kArgUserSpecific = "--user";
constexpr const char* kArgToBlock = "--toblock=";
constexpr const char* kArgToWarn = "--towarn=";
constexpr const char* kArgToReport = "--toreport=";
constexpr const char* kArgMisbehave = "--misbehave=";
constexpr const char* kArgHelp = "--help";
constexpr const char* kArgSavePrintRequestDataTo = "--save-print-request-data-to=";
std::map<std::string, Mode> sStringToMode = {
#define AGENT_MODE(name) {#name, Mode::Mode_##name},
#include "modes.h"
#undef AGENT_MODE
};
std::map<Mode, std::string> sModeToString = {
#define AGENT_MODE(name) {Mode::Mode_##name, #name},
#include "modes.h"
#undef AGENT_MODE
};
std::vector<std::pair<std::string, std::regex>>
ParseRegex(const std::string str) {
std::vector<std::pair<std::string, std::regex>> ret;
@ -78,6 +96,9 @@ bool ParseCommandLine(int argc, char* argv[]) {
toWarn = ParseRegex(arg.substr(strlen(kArgToWarn)));
} else if (arg.find(kArgToReport) == 0) {
toReport = ParseRegex(arg.substr(strlen(kArgToReport)));
} else if (arg.find(kArgMisbehave) == 0) {
modeStr = arg.substr(strlen(kArgMisbehave));
useMisbehavingHandler = true;
} else if (arg.find(kArgHelp) == 0) {
return false;
} else if (arg.find(kArgSavePrintRequestDataTo) == 0) {
@ -105,6 +126,7 @@ void PrintHelp() {
<< kArgToBlock << "<regex> : Regular expression matching file and text content to block." << std::endl
<< kArgToWarn << "<regex> : Regular expression matching file and text content to warn about." << std::endl
<< kArgToReport << "<regex> : Regular expression matching file and text content to report." << std::endl
<< kArgMisbehave << "<mode> : Use 'misbehaving' agent in given mode for testing purposes." << std::endl
<< kArgHelp << " : prints this help message" << std::endl;
}
@ -115,9 +137,17 @@ int main(int argc, char* argv[]) {
}
// TODO: Add toBlock, toWarn, toReport to QueueingHandler
auto handler = use_queue
? std::make_unique<QueuingHandler>(num_threads, delay, save_print_data_path)
: std::make_unique<Handler>(delay, save_print_data_path, std::move(toBlock), std::move(toWarn), std::move(toReport));
auto handler =
useMisbehavingHandler
? MisbehavingHandler::Create(delay, modeStr)
: use_queue
? std::make_unique<QueuingHandler>(num_threads, delay, save_print_data_path)
: std::make_unique<Handler>(delay, save_print_data_path, std::move(toBlock), std::move(toWarn), std::move(toReport));
if (!handler) {
std::cout << "[Demo] Failed to construct handler." << std::endl;
return 1;
}
// Each agent uses a unique name to identify itself with Google Chrome.
content_analysis::sdk::ResultCode rc;

Просмотреть файл

@ -51,8 +51,8 @@ class Handler : public content_analysis::sdk::AgentEventHandler {
DumpEvent(stream, event.get());
bool success = true;
std::optional<ContentAnalysisResponse_Result_TriggeredRule_Action> caResponse =
ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK;
std::optional<content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action> caResponse =
content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK;
if (event->GetRequest().has_text_content()) {
caResponse = DecideCAResponse(
@ -80,16 +80,16 @@ class Handler : public content_analysis::sdk::AgentEventHandler {
stream << " Verdict: ";
if (caResponse) {
switch (caResponse.value()) {
case ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK:
case content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK:
stream << "BLOCK";
break;
case ContentAnalysisResponse_Result_TriggeredRule_Action_WARN:
case content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_WARN:
stream << "WARN";
break;
case ContentAnalysisResponse_Result_TriggeredRule_Action_REPORT_ONLY:
case content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_REPORT_ONLY:
stream << "REPORT_ONLY";
break;
case ContentAnalysisResponse_Result_TriggeredRule_Action_ACTION_UNSPECIFIED:
case content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_ACTION_UNSPECIFIED:
stream << "ACTION_UNSPECIFIED";
break;
default:
@ -332,27 +332,27 @@ class Handler : public content_analysis::sdk::AgentEventHandler {
return true;
}
std::optional<ContentAnalysisResponse_Result_TriggeredRule_Action>
std::optional<content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action>
DecideCAResponse(const std::string& content, std::stringstream& stream) {
for (auto& r : toBlock_) {
if (std::regex_search(content, r.second)) {
stream << "'" << content << "' matches BLOCK regex '"
<< r.first << "'" << std::endl;
return ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK;
return content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK;
}
}
for (auto& r : toWarn_) {
if (std::regex_search(content, r.second)) {
stream << "'" << content << "' matches WARN regex '"
<< r.first << "'" << std::endl;
return ContentAnalysisResponse_Result_TriggeredRule_Action_WARN;
return content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_WARN;
}
}
for (auto& r : toReport_) {
if (std::regex_search(content, r.second)) {
stream << "'" << content << "' matches REPORT_ONLY regex '"
<< r.first << "'" << std::endl;
return ContentAnalysisResponse_Result_TriggeredRule_Action_REPORT_ONLY;
return content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action_REPORT_ONLY;
}
}
stream << "'" << content << "' was ALLOWed\n";

495
third_party/content_analysis_sdk/demo/handler_misbehaving.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,495 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
#define CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
#include <time.h>
#include <algorithm>
#include <chrono>
#include <fstream>
#include <map>
#include <iostream>
#include <utility>
#include <vector>
#include <regex>
#include <windows.h>
#include "content_analysis/sdk/analysis.pb.h"
#include "content_analysis/sdk/analysis_agent.h"
#include "agent/src/event_win.h"
enum class Mode {
// Have to use a "Mode_" prefix to avoid preprocessing problems in StringToMode
#define AGENT_MODE(name) Mode_##name,
#include "modes.h"
#undef AGENT_MODE
};
extern std::map<std::string, Mode> sStringToMode;
extern std::map<Mode, std::string> sModeToString;
// Writes a string to the pipe. Returns ERROR_SUCCESS if successful, else
// returns GetLastError() of the write. This function does not return until
// the entire message has been sent (or an error occurs).
static DWORD WriteBigMessageToPipe(HANDLE pipe, const std::string& message) {
std::cout << "[demo] WriteBigMessageToPipe top, message size is "
<< message.size() << std::endl;
if (message.empty()) {
return ERROR_SUCCESS;
}
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = CreateEvent(/*securityAttr=*/nullptr,
/*manualReset=*/TRUE,
/*initialState=*/FALSE,
/*name=*/nullptr);
if (overlapped.hEvent == nullptr) {
return GetLastError();
}
DWORD err = ERROR_SUCCESS;
const char* cursor = message.data();
for (DWORD size = message.length(); size > 0;) {
std::cout << "[demo] WriteBigMessageToPipe top of loop, remaining size "
<< size << std::endl;
if (WriteFile(pipe, cursor, size, /*written=*/nullptr, &overlapped)) {
std::cout << "[demo] WriteBigMessageToPipe: success" << std::endl;
err = ERROR_SUCCESS;
break;
}
// If an I/O is not pending, return the error.
err = GetLastError();
if (err != ERROR_IO_PENDING) {
std::cout
<< "[demo] WriteBigMessageToPipe: returning error from WriteFile "
<< err << std::endl;
break;
}
DWORD written;
if (!GetOverlappedResult(pipe, &overlapped, &written, /*wait=*/TRUE)) {
err = GetLastError();
std::cout << "[demo] WriteBigMessageToPipe: returning error from "
"GetOverlappedREsult "
<< err << std::endl;
break;
}
// reset err for the next loop iteration
err = ERROR_SUCCESS;
std::cout << "[demo] WriteBigMessageToPipe: bottom of loop, wrote "
<< written << std::endl;
cursor += written;
size -= written;
}
CloseHandle(overlapped.hEvent);
return err;
}
// An AgentEventHandler that does various misbehaving things
class MisbehavingHandler final : public content_analysis::sdk::AgentEventHandler {
public:
using Event = content_analysis::sdk::ContentAnalysisEvent;
static
std::unique_ptr<AgentEventHandler> Create(unsigned long delay,
const std::string& modeStr) {
auto it = sStringToMode.find(modeStr);
if (it == sStringToMode.end()) {
std::cout << "\"" << modeStr << "\""
<< " is not a valid mode!" << std::endl;
return nullptr;
}
return std::unique_ptr<AgentEventHandler>(new MisbehavingHandler(delay, it->second));
}
private:
MisbehavingHandler(unsigned long delay, Mode mode) : delay_(delay), mode_(mode) {}
template <size_t N>
DWORD SendBytesOverPipe(const unsigned char (&bytes)[N],
const std::unique_ptr<Event>& event) {
content_analysis::sdk::ContentAnalysisEventWin* eventWin =
static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
event.get());
HANDLE pipe = eventWin->Pipe();
std::string s(reinterpret_cast<const char*>(bytes), N);
return WriteBigMessageToPipe(pipe, s);
}
// Analyzes one request from Google Chrome and responds back to the browser
// with either an allow or block verdict.
void AnalyzeContent(std::unique_ptr<Event> event) {
// An event represents one content analysis request and response triggered
// by a user action in Google Chrome. The agent determines whether the
// user is allowed to perform the action by examining event->GetRequest().
// The verdict, which can be "allow" or "block" is written into
// event->GetResponse().
std::cout << std::endl << "----------" << std::endl << std::endl;
DumpRequest(event->GetRequest());
std::cout << "Mode is " << sModeToString[mode_] << std::endl;
if (mode_ == Mode::Mode_largeResponse) {
for (size_t i = 0; i < 1000; ++i) {
content_analysis::sdk::ContentAnalysisResponse_Result* result =
event->GetResponse().add_results();
result->set_tag("someTag");
content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule*
triggeredRule = result->add_triggered_rules();
triggeredRule->set_rule_id("some_id");
triggeredRule->set_rule_name("some_name");
}
} else if (mode_ ==
Mode::Mode_invalidUtf8StringStartByteIsContinuationByte) {
// protobuf docs say
// "A string must always contain UTF-8 encoded text."
// So let's try something invalid
// Anything with bits 10xxxxxx is only a continuation code point
event->GetResponse().set_request_token("\x80\x41\x41\x41");
} else if (mode_ ==
Mode::Mode_invalidUtf8StringEndsInMiddleOfMultibyteSequence) {
// f0 byte indicates there should be 3 bytes following it, but here
// there are only 2
event->GetResponse().set_request_token("\x41\xf0\x90\x8d");
} else if (mode_ == Mode::Mode_invalidUtf8StringOverlongEncoding) {
// codepoint U+20AC, should be encoded in 3 bytes (E2 82 AC)
// instead of 4
event->GetResponse().set_request_token("\xf0\x82\x82\xac");
} else if (mode_ == Mode::Mode_invalidUtf8StringMultibyteSequenceTooShort) {
// f0 byte indicates there should be 3 bytes following it, but here
// there are only 2 (\x41 is not a continuation byte)
event->GetResponse().set_request_token("\xf0\x90\x8d\x41");
} else if (mode_ == Mode::Mode_invalidUtf8StringDecodesToInvalidCodePoint) {
// decodes to U+1FFFFF, but only up to U+10FFFF is a valid code point
event->GetResponse().set_request_token("\xf7\xbf\xbf\xbf");
} else if (mode_ == Mode::Mode_stringWithEmbeddedNull) {
event->GetResponse().set_request_token("\x41\x00\x41");
} else if (mode_ == Mode::Mode_zeroResults) {
event->GetResponse().clear_results();
} else if (mode_ == Mode::Mode_resultWithInvalidStatus) {
// This causes an assertion failure and the process exits
// So we just serialize this ourselves below
/*content_analysis::sdk::ContentAnalysisResponse_Result* result =
event->GetResponse().mutable_results(0);
result->set_status(
static_cast<
::content_analysis::sdk::ContentAnalysisResponse_Result_Status>(
100));*/
} else {
bool block = false;
if (event->GetRequest().has_text_content()) {
block = ShouldBlockRequest(event->GetRequest().text_content());
} else if (event->GetRequest().has_file_path()) {
block = ShouldBlockRequest(event->GetRequest().file_path());
}
if (block) {
auto rc = content_analysis::sdk::SetEventVerdictToBlock(event.get());
std::cout << " Verdict: block";
if (rc != content_analysis::sdk::ResultCode::OK) {
std::cout << " error: "
<< content_analysis::sdk::ResultCodeToString(rc)
<< std::endl;
std::cout << " " << event->DebugString() << std::endl;
}
std::cout << std::endl;
} else {
std::cout << " Verdict: allow" << std::endl;
}
}
std::cout << std::endl;
// If a delay is specified, wait that much.
if (delay_ > 0) {
std::cout << "[Demo] delaying request processing for " << delay_ << "s"
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(delay_));
}
if (mode_ == Mode::Mode_largeResponse) {
content_analysis::sdk::ContentAnalysisEventWin* eventWin =
static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
event.get());
HANDLE pipe = eventWin->Pipe();
std::cout << "largeResponse about to write" << std::endl;
DWORD result = WriteBigMessageToPipe(
pipe, eventWin->SerializeStringToSendToBrowser());
std::cout << "largeResponse done writing with error " << result
<< std::endl;
eventWin->SetResponseSent();
} else if (mode_ == Mode::Mode_resultWithInvalidStatus) {
content_analysis::sdk::ContentAnalysisEventWin* eventWin =
static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
event.get());
HANDLE pipe = eventWin->Pipe();
std::string serializedString = eventWin->SerializeStringToSendToBrowser();
// The last byte is the status value. Set it to 100
serializedString[serializedString.length() - 1] = 100;
WriteBigMessageToPipe(pipe, serializedString);
} else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfString) {
unsigned char bytes[5];
bytes[0] = 10; // field 1 (request_token), LEN encoding
bytes[1] = 13; // length 13
bytes[2] = 65; // "A"
bytes[3] = 66; // "B"
bytes[4] = 67; // "C"
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithInvalidWireType) {
unsigned char bytes[5];
bytes[0] = 15; // field 1 (request_token), "7" encoding (invalid value)
bytes[1] = 3; // length 3
bytes[2] = 65; // "A"
bytes[3] = 66; // "B"
bytes[4] = 67; // "C"
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithUnusedFieldNumber) {
unsigned char bytes[5];
bytes[0] = 82; // field 10 (this is invalid), LEN encoding
bytes[1] = 3; // length 3
bytes[2] = 65; // "A"
bytes[3] = 66; // "B"
bytes[4] = 67; // "C"
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithWrongStringWireType) {
unsigned char bytes[2];
bytes[0] = 10; // field 1 (request_token), VARINT encoding (but should be
// a string/LEN)
bytes[1] = 42; // value 42
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithZeroTag) {
unsigned char bytes[1];
// The protobuf deserialization code seems to handle this
// in a special case.
bytes[0] = 0;
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithZeroFieldButNonzeroWireType) {
// The protobuf deserialization code seems to handle this
// in a special case.
unsigned char bytes[5];
bytes[0] = 2; // field 0 (invalid), LEN encoding
bytes[1] = 3; // length 13
bytes[2] = 65; // "A"
bytes[3] = 66; // "B"
bytes[4] = 67; // "C"
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageWithGroupEnd) {
// GROUP_ENDs are obsolete and the deserialization code
// handles them in a special case.
unsigned char bytes[1];
bytes[0] = 12; // field 1 (request_token), GROUP_END encoding
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfVarint) {
unsigned char bytes[2];
bytes[0] = 16; // field 2 (status), VARINT encoding
bytes[1] = 128; // high bit is set, indicating there
// should be a byte after this
SendBytesOverPipe(bytes, event);
} else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfTag) {
unsigned char bytes[1];
bytes[0] = 128; // tag is actually encoded as a VARINT, so set the high
// bit, indicating there should be a byte after this
SendBytesOverPipe(bytes, event);
} else {
std::cout << "(misbehaving) Handler::AnalyzeContent() about to call "
"event->Send(), mode is "
<< sModeToString[mode_] << std::endl;
// Send the response back to Google Chrome.
auto rc = event->Send();
if (rc != content_analysis::sdk::ResultCode::OK) {
std::cout << "[Demo] Error sending response: "
<< content_analysis::sdk::ResultCodeToString(rc) << std::endl;
std::cout << event->DebugString() << std::endl;
}
}
}
private:
void OnBrowserConnected(
const content_analysis::sdk::BrowserInfo& info) override {
std::cout << std::endl << "==========" << std::endl;
std::cout << "Browser connected pid=" << info.pid << std::endl;
}
void OnBrowserDisconnected(
const content_analysis::sdk::BrowserInfo& info) override {
std::cout << std::endl
<< "Browser disconnected pid=" << info.pid << std::endl;
std::cout << "==========" << std::endl;
}
void OnAnalysisRequested(std::unique_ptr<Event> event) override {
// If the agent is capable of analyzing content in the background, the
// events may be handled in background threads. Having said that, a
// event should not be assumed to be thread safe, that is, it should not
// be accessed by more than one thread concurrently.
//
// In this example code, the event is handled synchronously.
AnalyzeContent(std::move(event));
}
void OnResponseAcknowledged(
const content_analysis::sdk::ContentAnalysisAcknowledgement& ack)
override {
const char* final_action = "<Unknown>";
if (ack.has_final_action()) {
switch (ack.final_action()) {
case content_analysis::sdk::ContentAnalysisAcknowledgement::
ACTION_UNSPECIFIED:
final_action = "<Unspecified>";
break;
case content_analysis::sdk::ContentAnalysisAcknowledgement::ALLOW:
final_action = "Allow";
break;
case content_analysis::sdk::ContentAnalysisAcknowledgement::REPORT_ONLY:
final_action = "Report only";
break;
case content_analysis::sdk::ContentAnalysisAcknowledgement::WARN:
final_action = "Warn";
break;
case content_analysis::sdk::ContentAnalysisAcknowledgement::BLOCK:
final_action = "Block";
break;
}
}
std::cout << "Ack: " << ack.request_token() << std::endl;
std::cout << " Final action: " << final_action << std::endl;
}
void OnCancelRequests(
const content_analysis::sdk::ContentAnalysisCancelRequests& cancel)
override {
std::cout << "Cancel: " << std::endl;
std::cout << " User action ID: " << cancel.user_action_id() << std::endl;
}
void OnInternalError(const char* context,
content_analysis::sdk::ResultCode error) override {
std::cout << std::endl
<< "*ERROR*: context=\"" << context << "\" "
<< content_analysis::sdk::ResultCodeToString(error) << std::endl;
}
void DumpRequest(
const content_analysis::sdk::ContentAnalysisRequest& request) {
std::string connector = "<Unknown>";
if (request.has_analysis_connector()) {
switch (request.analysis_connector()) {
case content_analysis::sdk::FILE_DOWNLOADED:
connector = "download";
break;
case content_analysis::sdk::FILE_ATTACHED:
connector = "attach";
break;
case content_analysis::sdk::BULK_DATA_ENTRY:
connector = "bulk-data-entry";
break;
case content_analysis::sdk::PRINT:
connector = "print";
break;
case content_analysis::sdk::FILE_TRANSFER:
connector = "file-transfer";
break;
default:
break;
}
}
std::string url =
request.has_request_data() && request.request_data().has_url()
? request.request_data().url()
: "<No URL>";
std::string tab_title =
request.has_request_data() && request.request_data().has_tab_title()
? request.request_data().tab_title()
: "<No tab title>";
std::string filename =
request.has_request_data() && request.request_data().has_filename()
? request.request_data().filename()
: "<No filename>";
std::string digest =
request.has_request_data() && request.request_data().has_digest()
? request.request_data().digest()
: "<No digest>";
std::string file_path =
request.has_file_path() ? request.file_path() : "<none>";
std::string text_content =
request.has_text_content() ? request.text_content() : "<none>";
std::string machine_user =
request.has_client_metadata() &&
request.client_metadata().has_browser() &&
request.client_metadata().browser().has_machine_user()
? request.client_metadata().browser().machine_user()
: "<No machine user>";
std::string email =
request.has_request_data() && request.request_data().has_email()
? request.request_data().email()
: "<No email>";
time_t t = request.expires_at();
std::string user_action_id = request.has_user_action_id()
? request.user_action_id()
: "<No user action id>";
std::cout << "Request: " << request.request_token() << std::endl;
std::cout << " User action ID: " << user_action_id << std::endl;
std::cout << " Expires at: " << ctime(&t); // Returned string includes \n.
std::cout << " Connector: " << connector << std::endl;
std::cout << " URL: " << url << std::endl;
std::cout << " Tab title: " << tab_title << std::endl;
std::cout << " Filename: " << filename << std::endl;
std::cout << " Digest: " << digest << std::endl;
std::cout << " Filepath: " << file_path << std::endl;
std::cout << " Text content: '" << text_content << "'" << std::endl;
std::cout << " Machine user: " << machine_user << std::endl;
std::cout << " Email: " << email << std::endl;
}
bool ReadContentFromFile(const std::string& file_path, std::string* content) {
std::ifstream file(file_path,
std::ios::in | std::ios::binary | std::ios::ate);
if (!file.is_open()) return false;
// Get file size. This example does not handle files larger than 1MB.
// Make sure content string can hold the contents of the file.
int size = file.tellg();
if (size > 1024 * 1024) return false;
content->resize(size + 1);
// Read file into string.
file.seekg(0, std::ios::beg);
file.read(&(*content)[0], size);
content->at(size) = 0;
return true;
}
bool ShouldBlockRequest(const std::string& content) {
// Determines if the request should be blocked. (not needed for the
// misbehaving agent)
std::cout << "'" << content << "' was not blocked\n";
return false;
}
unsigned long delay_;
Mode mode_;
};
#endif // CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_

25
third_party/content_analysis_sdk/demo/modes.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// #define AGENT_MODE(name) to do what you want and then #include this file
AGENT_MODE(largeResponse)
AGENT_MODE(invalidUtf8StringStartByteIsContinuationByte)
AGENT_MODE(invalidUtf8StringEndsInMiddleOfMultibyteSequence)
AGENT_MODE(invalidUtf8StringOverlongEncoding)
AGENT_MODE(invalidUtf8StringMultibyteSequenceTooShort)
AGENT_MODE(invalidUtf8StringDecodesToInvalidCodePoint)
AGENT_MODE(stringWithEmbeddedNull)
AGENT_MODE(zeroResults)
AGENT_MODE(resultWithInvalidStatus)
AGENT_MODE(messageTruncatedInMiddleOfString)
AGENT_MODE(messageWithInvalidWireType)
AGENT_MODE(messageWithUnusedFieldNumber)
AGENT_MODE(messageWithWrongStringWireType)
AGENT_MODE(messageWithZeroTag)
AGENT_MODE(messageWithZeroFieldButNonzeroWireType)
AGENT_MODE(messageWithGroupEnd)
AGENT_MODE(messageTruncatedInMiddleOfVarint)
AGENT_MODE(messageTruncatedInMiddleOfTag)

Просмотреть файл

@ -58,3 +58,5 @@ DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True
DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True
FINAL_LIBRARY = "xul"
TEST_DIRS += ["tests/gtest"]

Просмотреть файл

@ -0,0 +1,132 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "content_analysis/sdk/analysis_client.h"
#include "TestContentAnalysis.h"
#include <processenv.h>
#include <synchapi.h>
using namespace content_analysis::sdk;
MozAgentInfo LaunchAgentNormal(const wchar_t* aToBlock) {
nsString cmdLineArguments;
if (aToBlock && aToBlock[0] != 0) {
cmdLineArguments.Append(L" --toblock=.*");
cmdLineArguments.Append(aToBlock);
cmdLineArguments.Append(L".*");
}
cmdLineArguments.Append(L" --user");
cmdLineArguments.Append(L" --path=");
nsString pipeName;
GeneratePipeName(L"contentanalysissdk-gtest-", pipeName);
cmdLineArguments.Append(pipeName);
MozAgentInfo agentInfo;
LaunchAgentWithCommandLineArguments(cmdLineArguments, pipeName, agentInfo);
return agentInfo;
}
TEST(ContentAnalysis, TextShouldNotBeBlocked)
{
auto MozAgentInfo = LaunchAgentNormal(L"block");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("should succeed");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_STREQ("request token", response.request_token().c_str());
ASSERT_EQ(1, response.results().size());
ASSERT_EQ(ContentAnalysisResponse_Result_Status_SUCCESS,
response.results().Get(0).status());
ASSERT_EQ(0, response.results().Get(0).triggered_rules_size());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysis, TextShouldBeBlocked)
{
auto MozAgentInfo = LaunchAgentNormal(L"block");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("should be blocked");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_STREQ("request token", response.request_token().c_str());
ASSERT_EQ(1, response.results().size());
ASSERT_EQ(ContentAnalysisResponse_Result_Status_SUCCESS,
response.results().Get(0).status());
ASSERT_EQ(1, response.results().Get(0).triggered_rules_size());
ASSERT_EQ(ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK,
response.results().Get(0).triggered_rules(0).action());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysis, FileShouldNotBeBlocked)
{
auto MozAgentInfo = LaunchAgentNormal(L"block");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_file_path("..\\..\\_tests\\gtest\\allowedFile.txt");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_STREQ("request token", response.request_token().c_str());
ASSERT_EQ(1, response.results().size());
ASSERT_EQ(ContentAnalysisResponse_Result_Status_SUCCESS,
response.results().Get(0).status());
ASSERT_EQ(0, response.results().Get(0).triggered_rules_size());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysis, FileShouldBeBlocked)
{
auto MozAgentInfo = LaunchAgentNormal(L"block");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_file_path("..\\..\\_tests\\gtest\\blockedFile.txt");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_STREQ("request token", response.request_token().c_str());
ASSERT_EQ(1, response.results().size());
ASSERT_EQ(ContentAnalysisResponse_Result_Status_SUCCESS,
response.results().Get(0).status());
ASSERT_EQ(1, response.results().Get(0).triggered_rules_size());
ASSERT_EQ(ContentAnalysisResponse_Result_TriggeredRule_Action_BLOCK,
response.results().Get(0).triggered_rules(0).action());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}

Просмотреть файл

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_testcontentanalysis_h
#define mozilla_testcontentanalysis_h
#include <processthreadsapi.h>
#include "content_analysis/sdk/analysis_client.h"
#include "gtest/gtest.h"
#include "nsString.h"
struct MozAgentInfo {
PROCESS_INFORMATION processInfo;
std::unique_ptr<content_analysis::sdk::Client> client;
};
void GeneratePipeName(const wchar_t* prefix, nsString& pipeName);
void LaunchAgentWithCommandLineArguments(const nsString& cmdLineArguments,
const nsString& pipeName,
MozAgentInfo& agentInfo);
#endif

Просмотреть файл

@ -0,0 +1,416 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "content_analysis/sdk/analysis_client.h"
#include "TestContentAnalysis.h"
#include <processenv.h>
#include <synchapi.h>
#include <windows.h>
using namespace content_analysis::sdk;
namespace {
MozAgentInfo LaunchAgentMisbehaving(const wchar_t* mode) {
nsString cmdLineArguments;
cmdLineArguments.Append(L" --misbehave=");
cmdLineArguments.Append(mode);
cmdLineArguments.Append(L" --user");
cmdLineArguments.Append(L" --path=");
nsString pipeName;
GeneratePipeName(L"contentanalysissdk-gtest-", pipeName);
cmdLineArguments.Append(pipeName);
MozAgentInfo agentInfo;
LaunchAgentWithCommandLineArguments(cmdLineArguments, pipeName, agentInfo);
return agentInfo;
}
} // namespace
// Disabled for now
/*TEST(ContentAnalysisMisbehaving, LargeResponse)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"largeResponse");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_STREQ("request token", response.request_token().c_str());
ASSERT_EQ(1001, response.results().size());
BOOL terminateResult = ::TerminateProcess(MozAgentInfo.processInfo.hProcess,
0); ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}*/
TEST(ContentAnalysisMisbehaving, InvalidUtf8StringStartByteIsContinuationByte)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"invalidUtf8StringStartByteIsContinuationByte");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// The protobuf spec says that strings must be valid UTF-8. So it's OK if
// this gets mangled, just want to make sure it doesn't cause a crash
// or invalid memory access or something.
ASSERT_STREQ("\x80\x41\x41\x41", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving,
InvalidUtf8StringEndsInMiddleOfMultibyteSequence)
{
auto MozAgentInfo = LaunchAgentMisbehaving(
L"invalidUtf8StringEndsInMiddleOfMultibyteSequence");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// The protobuf spec says that strings must be valid UTF-8. So it's OK if
// this gets mangled, just want to make sure it doesn't cause a crash
// or invalid memory access or something.
ASSERT_STREQ("\x41\xf0\x90\x8d", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, InvalidUtf8StringMultibyteSequenceTooShort)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"invalidUtf8StringMultibyteSequenceTooShort");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// The protobuf spec says that strings must be valid UTF-8. So it's OK if
// this gets mangled, just want to make sure it doesn't cause a crash
// or invalid memory access or something.
ASSERT_STREQ("\xf0\x90\x8d\x41", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, InvalidUtf8StringDecodesToInvalidCodePoint)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"invalidUtf8StringDecodesToInvalidCodePoint");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// The protobuf spec says that strings must be valid UTF-8. So it's OK if
// this gets mangled, just want to make sure it doesn't cause a crash
// or invalid memory access or something.
ASSERT_STREQ("\xf7\xbf\xbf\xbf", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, InvalidUtf8StringOverlongEncoding)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"invalidUtf8StringOverlongEncoding");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// The protobuf spec says that strings must be valid UTF-8. So it's OK if
// this gets mangled, just want to make sure it doesn't cause a crash
// or invalid memory access or something.
ASSERT_STREQ("\xf0\x82\x82\xac", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, StringWithEmbeddedNull)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"stringWithEmbeddedNull");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
std::string expected("\x41\x00\x41");
ASSERT_EQ(expected, response.request_token());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, ZeroResults)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"zeroResults");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_EQ(0, response.results().size());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, ResultWithInvalidStatus)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"resultWithInvalidStatus");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
request.set_text_content("unused");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
ASSERT_EQ(1, response.results().size());
// protobuf will fail to read this because it's an invalid value.
// (and leave status at its default value of 0)
// just make sure we can get the value without throwing
ASSERT_GE(static_cast<int>(response.results(0).status()), 0);
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageTruncatedInMiddleOfString)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"messageTruncatedInMiddleOfString");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithInvalidWireType)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"messageWithInvalidWireType");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithUnusedFieldNumber)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"messageWithUnusedFieldNumber");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
ASSERT_EQ(0, MozAgentInfo.client->Send(request, &response));
// protobuf will read the value and store it in an unused section
// just make sure we can get a value without throwing
ASSERT_STREQ("", response.request_token().c_str());
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithWrongStringWireType)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"messageWithWrongStringWireType");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithZeroTag)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"messageWithZeroTag");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithZeroFieldButNonzeroWireType)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"messageWithZeroFieldButNonzeroWireType");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageWithGroupEnd)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"messageWithZeroFieldButNonzeroWireType");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageTruncatedInMiddleOfVarint)
{
auto MozAgentInfo =
LaunchAgentMisbehaving(L"messageTruncatedInMiddleOfVarint");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}
TEST(ContentAnalysisMisbehaving, MessageTruncatedInMiddleOfTag)
{
auto MozAgentInfo = LaunchAgentMisbehaving(L"messageTruncatedInMiddleOfTag");
// Exit the test early if the process failed to launch
ASSERT_NE(MozAgentInfo.processInfo.dwProcessId, 0UL);
ASSERT_NE(nullptr, MozAgentInfo.client.get());
ContentAnalysisRequest request;
request.set_request_token("request token");
ContentAnalysisResponse response;
// The response is an invalid serialization of protobuf, so this should fail
ASSERT_EQ(-1, MozAgentInfo.client->Send(request, &response));
BOOL terminateResult =
::TerminateProcess(MozAgentInfo.processInfo.hProcess, 0);
ASSERT_NE(FALSE, terminateResult)
<< "Failed to terminate content_analysis_sdk_agent process";
}

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "TestContentAnalysis.h"
#include <combaseapi.h>
#include <pathcch.h>
#include <shlwapi.h>
#include <rpc.h>
#include <windows.h>
void GeneratePipeName(const wchar_t* prefix, nsString& pipeName) {
pipeName = u""_ns;
pipeName.Append(prefix);
UUID uuid;
ASSERT_EQ(RPC_S_OK, UuidCreate(&uuid));
// 39 == length of a UUID string including braces and NUL.
wchar_t guidBuf[39] = {};
ASSERT_EQ(39, StringFromGUID2(uuid, guidBuf, 39));
// omit opening and closing braces (and trailing null)
pipeName.Append(&guidBuf[1], 36);
}
void LaunchAgentWithCommandLineArguments(const nsString& cmdLineArguments,
const nsString& pipeName,
MozAgentInfo& agentInfo) {
wchar_t progName[MAX_PATH] = {};
// content_analysis_sdk_agent.exe is either next to firefox.exe (for local
// builds), or in ../../tests/bin/ (for try/treeherder builds)
DWORD nameSize = ::GetModuleFileNameW(nullptr, progName, MAX_PATH);
ASSERT_NE(DWORD{0}, nameSize);
ASSERT_EQ(S_OK, PathCchRemoveFileSpec(progName, nameSize));
wchar_t normalizedPath[MAX_PATH] = {};
nsString test1 = nsString(progName) + u"\\content_analysis_sdk_agent.exe"_ns;
ASSERT_EQ(S_OK, PathCchCanonicalize(normalizedPath, MAX_PATH, test1.get()));
nsString agentPath;
if (::PathFileExistsW(normalizedPath)) {
agentPath = nsString(normalizedPath);
}
if (agentPath.IsEmpty()) {
nsString unNormalizedPath =
nsString(progName) +
u"\\..\\..\\tests\\bin\\content_analysis_sdk_agent.exe"_ns;
ASSERT_EQ(S_OK, PathCchCanonicalize(normalizedPath, MAX_PATH,
unNormalizedPath.get()));
if (::PathFileExistsW(normalizedPath)) {
agentPath = nsString(normalizedPath);
}
}
ASSERT_FALSE(agentPath.IsEmpty());
nsString localCmdLine = nsString(agentPath) + u" "_ns + cmdLineArguments;
STARTUPINFOW startupInfo = {sizeof(startupInfo)};
PROCESS_INFORMATION processInfo;
BOOL ok =
::CreateProcessW(nullptr, localCmdLine.get(), nullptr, nullptr, FALSE, 0,
nullptr, nullptr, &startupInfo, &processInfo);
// The documentation for CreateProcessW() says that any non-zero value is a
// success
if (!ok) {
// Show the last error
ASSERT_EQ(0UL, GetLastError())
<< "Failed to launch content_analysis_sdk_agent";
}
// Allow time for the agent to set up the pipe
::Sleep(2000);
content_analysis::sdk::Client::Config config;
config.name = NS_ConvertUTF16toUTF8(pipeName);
config.user_specific = true;
auto clientPtr = content_analysis::sdk::Client::Create(config);
ASSERT_NE(nullptr, clientPtr.get());
agentInfo.processInfo = processInfo;
agentInfo.client = std::move(clientPtr);
}

Просмотреть файл

@ -0,0 +1,40 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Program("content_analysis_sdk_agent")
DEFINES["UNICODE"] = True
DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True
DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True
SOURCES += [
"../../../../../../third_party/content_analysis_sdk/agent/src/agent_base.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/agent_utils_win.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/agent_win.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/event_base.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/event_win.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/scoped_print_handle_base.cc",
"../../../../../../third_party/content_analysis_sdk/agent/src/scoped_print_handle_win.cc",
"../../../../../../third_party/content_analysis_sdk/common/utils_win.cc",
"../../../../../../third_party/content_analysis_sdk/demo/agent.cc",
"../../../content_analysis/sdk/analysis.pb.cc",
]
LOCAL_INCLUDES += [
"../../../",
"../../../../../../third_party/content_analysis_sdk",
"../../../../../../third_party/content_analysis_sdk/agent/include/",
]
USE_LIBS += [
"mozglue",
"protobuf",
"zlib",
]
OS_LIBS += [
"advapi32",
]

Просмотреть файл

@ -0,0 +1 @@
allow me

Просмотреть файл

@ -0,0 +1 @@
block me

Просмотреть файл

@ -0,0 +1,23 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
LOCAL_INCLUDES += [
"../..",
"../../../../../third_party/content_analysis_sdk/browser/include/",
]
if CONFIG["OS_TARGET"] == "WINNT":
UNIFIED_SOURCES += [
"TestContentAnalysis.cpp",
"TestContentAnalysisMisbehaving.cpp",
"TestContentAnalysisUtils.cpp",
]
DIRS += ["agent"]
OS_LIBS += ["pathcch"]
TEST_HARNESS_FILES.gtest += ["allowedFile.txt", "blockedFile.txt"]
FINAL_LIBRARY = "xul-gtest"

Просмотреть файл

@ -4,6 +4,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Library('protobuf')
with Files('**'):
BUG_COMPONENT = ('Core', 'General')