diff --git a/crates/oxc_linter/src/rules/unicorn/require_post_message_target_origin.rs b/crates/oxc_linter/src/rules/unicorn/require_post_message_target_origin.rs index e058db3a8e76f..b35e46e1fd186 100644 --- a/crates/oxc_linter/src/rules/unicorn/require_post_message_target_origin.rs +++ b/crates/oxc_linter/src/rules/unicorn/require_post_message_target_origin.rs @@ -62,6 +62,9 @@ impl Rule for RequirePostMessageTargetOrigin { _ => return, }; if matches!(member_expr.static_property_name(), Some(name) if name == "postMessage") { + if is_message_port_expression(member_expr.object()) { + return; + } let span = call_expr.arguments[0].span(); ctx.diagnostic_with_suggestion( require_post_message_target_origin_diagnostic(Span::new(span.end, span.end)), @@ -79,6 +82,37 @@ impl Rule for RequirePostMessageTargetOrigin { } } +fn is_message_port_expression(expr: &Expression<'_>) -> bool { + let mut current_expr = expr.without_parentheses(); + loop { + if let Expression::Identifier(ident) = current_expr + && matches!(ident.name.as_str(), "port" | "port1" | "port2" | "messagePort") + { + return true; + } + + let Some(member_expr) = current_expr.get_member_expr() else { + return false; + }; + + if member_expr.static_property_name().is_some_and(|name| matches!(name, "port1" | "port2")) { + return true; + } + + if member_expr.is_computed() + && member_expr.object().without_parentheses().get_member_expr().is_some_and( + |object_member| { + object_member.static_property_name().is_some_and(|name| name == "ports") + }, + ) + { + return true; + } + + current_expr = member_expr.object().without_parentheses(); + } +} + #[test] fn test() { use crate::tester::Tester; @@ -101,6 +135,10 @@ fn test() { "window.c.postMessage?.(message)", "window.a.b?.postMessage(message)", "window?.a?.b?.postMessage(message)", + "event.ports[0].postMessage(message)", + "channel.port1.postMessage(message)", + "channel['port2'].postMessage(message)", + "event?.ports[0].postMessage(message)", ]; let fail = vec![