use std::path::Path;
use oxc_ast::ast::*;
use oxc_ast_visit::Visit;
use crate::rules::{Issue, IssueCategory, IssueSource, Rule, RuleContext, Severity};
use crate::utils::offset_to_line_col;
pub struct NoPostmessageWildcard;
impl Rule for NoPostmessageWildcard {
fn name(&self) -> &str {
"no_postmessage_wildcard"
}
fn run(&self, ctx: &RuleContext<'_>) -> Vec<Issue> {
let mut visitor = Visitor {
source_text: ctx.source_text,
file_path: ctx.file_path,
issues: vec![],
};
visitor.visit_program(ctx.program);
visitor.issues
}
}
struct Visitor<'a> {
source_text: &'a str,
file_path: &'a Path,
issues: Vec<Issue>,
}
impl<'a, 'b> Visit<'b> for Visitor<'a> {
fn visit_call_expression(&mut self, expr: &CallExpression<'b>) {
let is_post_message = matches!(
&expr.callee,
Expression::StaticMemberExpression(m)
if m.property.name.as_str() == "postMessage"
);
if !is_post_message {
return;
}
let Some(second_arg) = expr.arguments.get(1) else {
return;
};
if let Some(Expression::StringLiteral(s)) = second_arg.as_expression() {
if s.value.as_str() == "*" {
let (line, col) = offset_to_line_col(self.source_text, expr.span.start);
self.issues.push(Issue {
rule: "no_postmessage_wildcard".into(),
message: "postMessage() uses wildcard origin \"*\" — \
specify the target origin to prevent data leaks \
to untrusted windows"
.into(),
file: self.file_path.to_path_buf(),
line,
column: col,
severity: Severity::High,
source: IssueSource::ReactPerfAnalyzer,
category: IssueCategory::Security,
});
}
}
}
}