// Copyright 2022 The Chromium Authors. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_ANALYSIS_AGENT_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_AGENT_H_ #define CONTENT_ANALYSIS_AGENT_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_AGENT_H_ #include #include #include "content_analysis/sdk/analysis.pb.h" #include "content_analysis/sdk/result_codes.h" // This is the main include file for code using Content Analysis Connector // Agent SDK. No other include is needed. // // An agent begins by creating an instance of Agent using the factory // function Agent::Create(). This instance should live as long as the agent // intends to receive content analysis requests from Google Chrome. // // Agent::Create() must be passed an object that implements the // AgentEventHandler interface. Methods on this interface will be called // at the approriate time to handle specific events. The events are: // // - A Google Chrome browser has started or stopped. This events contains // information about the browser such as process id and executable path. // - A request to analyze content. The agent reads and analyses the // request in to determine a verdict: allow or block. When the verdict is // known the response is sent back to Google Chrome. // - An acknowledgement that Google Chrome has properly received the agent's // verdict. // // The agent is not required to serialize event handling. That is, content // analysis events can be analyze in the background and the response does // not need to be sent before OnAnalysisRequested() returns. // // Google Chrome thottles the number of requests sent to the agent to 5 // current requests at a time but this is subject to change. namespace content_analysis { namespace sdk { // Represents information about one instance of a Google Chrome browser // process that is connected to the agent. struct BrowserInfo { unsigned long pid = 0; // Process ID of Google Chrome browser process. std::string binary_path; // The full path to the process's main binary. }; // Represents one content analysis request as generated by a given user action // in Google Chrome. // // The agent should retrieve information about the content analysis request // using the GetRequest() method. The agent should analyze the request and // update the response, returned by GetResponse(), with a verdict (allow or // block). Once the verdict is set the response can be sent back to Google // Chrome by calling Send(). // // The default verdict is to allow the requested user action. If the final // verdict is to allow then the agent does not need to update the response and // can simply call Send(). // // If the final verdict should be to block, the agent should first update the // response by calling SetEventVerdictToBlock() before calling Send(). // // This class is not thread safe. However, it may be passed to another thread // as long as the agent properly serializses access to the event. // // See the demo directory for an example of how to use this class. class ContentAnalysisEvent { public: virtual ~ContentAnalysisEvent() = default; // Prepares the event for graceful shutdown. Upon return calls to all // other methods of this class will fail. virtual ResultCode Close() = 0; // Retrives information about the browser that generated this content // analysis event. virtual const BrowserInfo& GetBrowserInfo() const = 0; // Retrieves a read-only reference to the content analysis request received // from Google Chrome. virtual const ContentAnalysisRequest& GetRequest() const = 0; // Retrieves a writable reference to the content analysis response that will // be sent to Google Chrome as the verdict for the request of this event. // The agent may modify this response in place before calling Send(). virtual ContentAnalysisResponse& GetResponse() = 0; // Send the verdict to Google Chrome. Once this method is called further // changes to the response are ignored. virtual ResultCode Send() = 0; // Returns a string containing internal state of the object that is useful // for debugging. virtual std::string DebugString() const = 0; protected: ContentAnalysisEvent() = default; ContentAnalysisEvent(const ContentAnalysisEvent& rhs) = delete; ContentAnalysisEvent(ContentAnalysisEvent&& rhs) = delete; ContentAnalysisEvent& operator=(const ContentAnalysisEvent& rhs) = delete; ContentAnalysisEvent& operator=(ContentAnalysisEvent&& rhs) = delete; }; // Agents should implement this interface in order to handle events as needed. // // OnBrowserConnected() and OnBrowserDisonnected() notify the agent when // instances of Google Chome start and stop. The agent may perform any one-time // actions as required for these events. The default action is to do nothing // for both events. If the agent does not need perform any special actions // these methods do not need to be overridden. // // OnAnalysisRequested() notifies the agent of a new content analysis request // from Google Chrome. The agent should perform the analysis and respond to // the event. It is not required for the agent complete the analysis and // respond to before this callback returns. The agent may pass the // ContentAnalysisEvent to a background task and respond when ready. This // callback has no default action and agents must override it. // // OnResponseAcknowledged() notifies the agent that Google Chrome has received // the content analysis response and how it has handled it. class AgentEventHandler { public: AgentEventHandler() = default; virtual ~AgentEventHandler() = default; // Called when a new Google Chrome browser instance connects to the agent. // This is always called before the first OnAnalysisRequested() from that // browser. virtual void OnBrowserConnected(const BrowserInfo& info) {} // Called when a Google Chrome browser instance disconnects from the agent. // The agent will no longer receive new content analysis requests from this // browser. virtual void OnBrowserDisconnected(const BrowserInfo& info) {} // Called when a Google Chrome browser requests a content analysis. virtual void OnAnalysisRequested( std::unique_ptr event) = 0; // Called when a Google Chrome browser acknowledges the content analysis // response from the agent. The default action is to do nothing. // If the agent does not need perform any special actions this methods does // not need to be overridden. virtual void OnResponseAcknowledged( const ContentAnalysisAcknowledgement& ack) {} // Called when a Google Chrome browser asks the agent to cancels one or // more content analysis requests. This happens when the user presses the // Cancel button in the in-progress dialog. This is expected to be a best // effort only; agents may choose to ignore this message or possibly only // cancel a subset of requests with the given user action id. // // The default action is to do nothing. If the agent does not need perform // any special actions this methods does not need to be overridden. virtual void OnCancelRequests( const ContentAnalysisCancelRequests& cancel) {} // Called whenever the Agent implementation detects an error. `context` // is a string that provide a hint to the handler as to where the error // happened in the agent. `error` represent the actual error detected. virtual void OnInternalError(const char* context, ResultCode error) {} }; // Represents an agent that can perform content analysis for the Google Chrome // browser. This class holds the server endpoint that Google Chrome connects // to when content analysis is required. // // Agent instances should outlive all ContentAnalysisEvent instances created // with it. Agent instances are not thread safe except for Stop() which can be // called from any thread to shutdown the agent. Outstanding // ContentAnalysisEvents created from this agent may or may not still complete. // // See the demo directory for an example of how to use this class. class Agent { public: // Configuration options where creating an agent. `name` is used to create // a channel between the agent and Google Chrome. struct Config { // Used to create a channel between the agent and Google Chrome. Both must // use the same name to properly rendezvous with each other. The channel // is platform specific. std::string name; // Set to true if there is a different agent instance per OS user. Defaults // to false. bool user_specific = false; }; // Creates a new agent instance. If successful, an agent is returned. // Otherwise a nullptr is returned and `rc` contains the reason for the // failure. static std::unique_ptr Create( Config config, std::unique_ptr handler, ResultCode* rc); virtual ~Agent() = default; // Returns the configuration parameters used to create the agent. virtual const Config& GetConfig() const = 0; // Handles events triggered on this agent and calls the coresponding // callbacks in the AgentEventHandler. This method is blocking and returns // when Stop() is called or if an error occurs. virtual ResultCode HandleEvents() = 0; // Prepares the agent for graceful shutdown. Any function blocked on // HandleEvents() will return. It is safe to call this method from any // thread. virtual ResultCode Stop() = 0; // Returns a string containing internal state of the object that is useful // for debugging. virtual std::string DebugString() const = 0; protected: Agent() = default; Agent(const Agent& rhs) = delete; Agent(Agent&& rhs) = delete; Agent& operator=(const Agent& rhs) = delete; Agent& operator=(Agent&& rhs) = delete; }; // Update the tag or status of `response`. This function assumes that the // response contains only one Result. If one already exists it is updated // otherwise a new Result is created. // // The response contained within ContentAnalysisEvent has already been updated. // This function is useful only when create a new instance of // ContentAnalysisResponse. // // If `tag` is not empty it will replace the result's tag. // If `status` is not STATUS_UNKNOWN it will will replace the result's status. ResultCode UpdateResponse(ContentAnalysisResponse& response, const std::string& tag, ContentAnalysisResponse::Result::Status status); // Sets the response verdict of an event to `action`. This is a convenience // function that is equivalent to the following: // // auto result = event->GetResponse().mutable_results(0); // auto rule = result->mutable_triggered_rules(0); // rule->set_action(action); // // This function assumes the event's response has already been initialized // using UpdateResponse(). ResultCode SetEventVerdictTo( ContentAnalysisEvent* event, ContentAnalysisResponse::Result::TriggeredRule::Action action); // Sets the reponse verdict of an event to "block". This is a convenience // function that is equivalent to the following: // // SetEventVerdictTo(event, // ContentAnalysisResponse::Result::TriggeredRule::BLOCK); ResultCode SetEventVerdictToBlock(ContentAnalysisEvent* event); // Helper class to handle the lifetime and access of print data. class ScopedPrintHandle { public: virtual ~ScopedPrintHandle() = default; virtual const char* data() = 0; virtual size_t size() = 0; protected: ScopedPrintHandle() = default; ScopedPrintHandle(const ScopedPrintHandle&) = delete; ScopedPrintHandle& operator=(const ScopedPrintHandle&) = delete; ScopedPrintHandle(ScopedPrintHandle&&) = default; ScopedPrintHandle& operator=(ScopedPrintHandle&&) = default; }; // Returns a `ScopedPrintHandle` initialized from the request's print data // if it exists. std::unique_ptr CreateScopedPrintHandle(const ContentAnalysisRequest& request, int64_t browser_pid); } // namespace sdk } // namespace content_analysis #endif // CONTENT_ANALYSIS_AGENT_INCLUDE_CONTENT_ANALYSIS_SDK_ANALYSIS_AGENT_H_