@@ -571,7 +571,11 @@ fn l7_protocol_log_summary(
571571 }
572572
573573 if let Some ( info) = jsonrpc_info {
574- return format ! ( " rule_methods={}" , rule_method_names_for_log( info) ) ;
574+ return format ! (
575+ " rule_methods={} tools={}" ,
576+ rule_method_names_for_log( info) ,
577+ tool_names_for_log( info)
578+ ) ;
575579 }
576580
577581 String :: new ( )
@@ -1428,8 +1432,9 @@ pub(crate) fn jsonrpc_log_message(
14281432 reason : & str ,
14291433) -> String {
14301434 let rule_methods = rule_method_names_for_log ( info) ;
1435+ let tools = tool_names_for_log ( info) ;
14311436 format ! (
1432- "JSONRPC_L7_REQUEST decision={decision} http_method={http_method} endpoint={endpoint} rule_methods={rule_methods } policy_version={policy_version} reason={reason}"
1437+ "JSONRPC_L7_REQUEST decision={decision} rule_methods={rule_methods} tools={tools} http_method={http_method} endpoint={endpoint } policy_version={policy_version} reason={reason}"
14331438 )
14341439}
14351440
@@ -1444,6 +1449,20 @@ pub(crate) fn rule_method_names_for_log(info: &crate::l7::jsonrpc::JsonRpcReques
14441449 . join ( "," )
14451450}
14461451
1452+ pub ( crate ) fn tool_names_for_log ( info : & crate :: l7:: jsonrpc:: JsonRpcRequestInfo ) -> String {
1453+ let tools = info
1454+ . calls
1455+ . iter ( )
1456+ . filter_map ( |call| call. tool . as_deref ( ) )
1457+ . map ( sanitize_log_token)
1458+ . collect :: < Vec < _ > > ( ) ;
1459+ if tools. is_empty ( ) {
1460+ "-" . to_string ( )
1461+ } else {
1462+ tools. join ( "," )
1463+ }
1464+ }
1465+
14471466fn sanitize_log_token ( value : & str ) -> String {
14481467 value
14491468 . chars ( )
@@ -2538,7 +2557,6 @@ network_policies:
25382557
25392558 let ( allowed, reason) = evaluate_l7_request ( & tunnel_engine, & ctx, & request) . unwrap ( ) ;
25402559 assert ! ( allowed, "{reason}" ) ;
2541-
25422560 request. jsonrpc = Some ( crate :: l7:: jsonrpc:: parse_jsonrpc_body (
25432561 br#"{"jsonrpc":"2.0","id":1,"method":"reports.search","params":["ignored",{"nested":true}]}"# ,
25442562 crate :: l7:: jsonrpc:: JsonRpcInspectionMode :: JsonRpc ,
@@ -2604,6 +2622,17 @@ network_policies:
26042622
26052623 let ( allowed, reason) = evaluate_l7_request ( & tunnel_engine, & ctx, & request) . unwrap ( ) ;
26062624 assert ! ( allowed, "{reason}" ) ;
2625+ let allowed_info = request. jsonrpc . as_ref ( ) . expect ( "parsed MCP request" ) ;
2626+ let allowed_message = jsonrpc_log_message (
2627+ "allow" ,
2628+ "POST" ,
2629+ "api.example.test:443/mcp" ,
2630+ allowed_info,
2631+ 42 ,
2632+ & reason,
2633+ ) ;
2634+ assert ! ( allowed_message. contains( "rule_methods=tools/call" ) ) ;
2635+ assert ! ( allowed_message. contains( "tools=read_status" ) ) ;
26072636
26082637 request. jsonrpc = Some ( crate :: l7:: jsonrpc:: parse_jsonrpc_body (
26092638 br#"{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"delete_resource","arguments":{"scope":"workspace/main"}}}"# ,
@@ -2625,6 +2654,17 @@ network_policies:
26252654 reason. contains( "deny rule" ) ,
26262655 "deny reason should identify policy denial: {reason}"
26272656 ) ;
2657+ let denied_message = jsonrpc_log_message (
2658+ "deny" ,
2659+ "POST" ,
2660+ "api.example.test:443/mcp" ,
2661+ parsed,
2662+ 42 ,
2663+ & reason,
2664+ ) ;
2665+ assert ! ( denied_message. contains( "rule_methods=tools/call" ) ) ;
2666+ assert ! ( denied_message. contains( "tools=delete_resource" ) ) ;
2667+ assert ! ( !denied_message. contains( "workspace/main" ) ) ;
26282668 }
26292669
26302670 #[ test]
@@ -2644,6 +2684,7 @@ network_policies:
26442684
26452685 assert ! ( message. contains( "endpoint=jsonrpc.example.com:443/rpc" ) ) ;
26462686 assert ! ( message. contains( "rule_methods=reports.archive" ) ) ;
2687+ assert ! ( message. contains( "tools=-" ) ) ;
26472688 assert ! ( message. contains( "policy_version=42" ) ) ;
26482689 assert ! ( !message. contains( "delete_resource" ) ) ;
26492690 assert ! ( !message. contains( "secret-scope" ) ) ;
0 commit comments