use crate::handlers::pxcredentials_intelligence;
use crate::modules::pxconstants::*;
use crate::modules::pxutils::{get_risk_headers, set_json_int, set_json_str};
use crate::px_debug;
use crate::pxconfig::PXConfig;
use crate::pxcontext::{
BlockReason, CallReason, CookieOrigin, PXContext, PassReason, S2sErrorReason,
};
use fastly::Request;
use serde_json::json;
fn post_activity_async(
activity_type: &str,
details: &mut serde_json::value::Value,
ctx: &PXContext,
conf: &PXConfig,
) {
let cookie_origin = match ctx.cookie_origin {
Some(CookieOrigin::Cookie) => "cookie",
Some(CookieOrigin::Header) => "header",
None => "",
};
set_json_str!(details, "cookie_origin"; cookie_origin);
set_json_str!(details, "module_version"; PX_MODULE_VERSION);
set_json_int!(details, "risk_rtt"; ctx.risk_rtt.unwrap_or(0));
set_json_int!(details, "enforcer_start_time"; ctx.enforcer_start_time
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
);
details["is_sensitive_route"] = json!(ctx.is_sensitive_route);
set_json_str!(details, "http_method"; ctx.http_method);
set_json_str!(details, "http_version"; ctx.http_version);
set_json_str!(details, "custom_param1"; ctx.custom_params.custom_param1);
set_json_str!(details, "custom_param2"; ctx.custom_params.custom_param2);
set_json_str!(details, "custom_param3"; ctx.custom_params.custom_param3);
set_json_str!(details, "custom_param4"; ctx.custom_params.custom_param4);
set_json_str!(details, "custom_param5"; ctx.custom_params.custom_param5);
set_json_str!(details, "custom_param6"; ctx.custom_params.custom_param6);
set_json_str!(details, "custom_param7"; ctx.custom_params.custom_param7);
set_json_str!(details, "custom_param8"; ctx.custom_params.custom_param8);
set_json_str!(details, "custom_param9"; ctx.custom_params.custom_param9);
set_json_str!(details, "custom_param10"; ctx.custom_params.custom_param10);
set_json_str!(details, "risk_mode"; ctx.risk_mode);
if let Some(ref s2s_call_reason) = ctx.s2s_call_reason {
if s2s_call_reason != &CallReason::None {
set_json_str!(details, "s2s_call_reason"; s2s_call_reason);
}
}
if !ctx.request_cookie_names.is_empty() {
set_json_str!(details, "request_cookie_names"; ctx.request_cookie_names.join(","));
}
set_json_str!(details, "cross_tab_session"; ctx.pxcts_cookie.as_deref().unwrap_or_default());
if let Some(app_user_id) = &ctx.app_user_id {
set_json_str!(details, "app_user_id"; app_user_id);
}
if let Some(jwt_fields) = &ctx.jwt_additional_fields {
details["jwt_additional_fields"] = serde_json::Value::Object(jwt_fields.clone());
}
if let Some(additional_risk_info) = &ctx.additional_risk_info {
details["additional_risk_info"] = json!(additional_risk_info);
}
if let Some(additional_token_info) = &ctx.additional_token_info {
details["additional_token_info"] = json!(additional_token_info);
}
if let Some(orig_cookie_vid) = &ctx.orig_cookie_vid {
details["orig_cookie_vid"] = json!(orig_cookie_vid);
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
pxcredentials_intelligence::apply_ci_fields_to_details(details, ctx, true, true);
if let Some(at) = &ctx.agentic_trust_data {
if let Some(v) = &at.mcp_method {
set_json_str!(details, "mcp_method"; v);
}
if let Some(v) = &at.mcp_tool_name {
set_json_str!(details, "mcp_tool_name"; v);
}
if let Some(v) = &at.mcp_tool_argument_keys {
set_json_str!(details, "mcp_tool_argument_keys"; v);
}
if let Some(v) = &at.mcp_session_id {
set_json_str!(details, "mcp_session_id"; v);
}
if let Some(v) = &at.mcp_http_method {
set_json_str!(details, "mcp_http_method"; v);
}
}
let mut request_body = json!({
"timestamp": timestamp,
"type": activity_type,
"headers": get_risk_headers(&ctx.headers, &conf.sensitive_headers),
"socket_ip": ctx.ip,
"px_app_id": conf.app_id,
"url": ctx.full_url,
"pxhd": ctx.get_pxhd().unwrap_or_default(),
"details": details
});
set_json_str!(&mut request_body, "vid"; ctx.vid.as_deref().unwrap_or_default());
if !ctx.graphql_extracted_items.is_empty() {
let mut graphql_items = vec![];
for item in &ctx.graphql_extracted_items {
let graphql_item = json!({
"type": item.op_type,
"name": item.name,
"sensitive": item.sensitive,
"variables": item.variables,
"keywords": item.keywords
});
graphql_items.push(graphql_item);
}
if let Some(details_obj) = request_body.get_mut("details") {
if let Ok(graphql_ops) = serde_json::to_value(graphql_items) {
details_obj["graphql_operations"] = graphql_ops;
}
}
}
let body = request_body.to_string();
px_debug!("activity body: {}", body);
let body_bytes = body.as_bytes();
let url = format!("https://{}{}", conf.human_collector_host, ACTIVITY_API);
let req = Request::post(url)
.with_header("Authorization", format!("Bearer {}", conf.auth_token))
.with_header("Content-Type", APPLICATION_JSON)
.with_body(body_bytes)
.send_async(&conf.human_collector_backend);
match req {
Ok(r) => {
r.poll();
}
Err(e) => px_debug!("Error sending Activity: {}", e),
}
}
pub fn send_page_requested_activity(ctx: &PXContext, conf: &PXConfig) {
let mut details = json!({
"client_uuid": ctx.uuid.as_deref().unwrap_or_default(),
"pass_reason": ctx
.pass_reason
.as_ref()
.map_or_else(|| PassReason::None.to_string(), ToString::to_string),
"request_id": ctx.request_id.to_string(),
});
if let Some(cookie_json) = &ctx.cookie_json {
set_json_str!(&mut details, "px_cookie"; cookie_json);
}
if let Some(reason) = &ctx.s2s_error_reason {
if reason != &S2sErrorReason::None {
set_json_str!(&mut details, "s2s_error_reason"; reason);
}
}
if let Some(status) = ctx.s2s_error_http_status {
set_json_int!(&mut details, "s2s_error_http_status"; status);
}
set_json_str!(&mut details, "s2s_error_message"; ctx.s2s_error_message.as_deref().unwrap_or_default());
post_activity_async("page_requested", &mut details, ctx, conf);
}
pub fn send_block_activity(ctx: &PXContext, conf: &PXConfig) {
let simulated_block = ctx.is_simulated_block;
let mut details = json!({
"client_uuid": ctx.uuid.as_deref().unwrap_or_default(),
"block_reason": ctx
.block_reason
.as_ref()
.map_or_else(|| BlockReason::None.to_string(), ToString::to_string),
"block_action": ctx.block_action.as_deref().unwrap_or("c"),
"block_score": ctx.score.unwrap_or(0),
"simulated_block": simulated_block,
"request_id": ctx.request_id.to_string(),
});
post_activity_async("block", &mut details, ctx, conf);
}