pyrograph 0.1.0

GPU-accelerated taint analysis for supply chain malware detection
Documentation
use super::*;

impl JsParser {
    pub(super) fn eval_tagged_template(&mut self, tagged: &TaggedTpl) -> NodeId {
        let tag = self.eval_expr(&tagged.tag);
        let call_name = self
            .graph
            .node(tag)
            .and_then(|n| n.alias.as_ref())
            .cloned()
            .unwrap_or_else(|| self.name(tag));
        let id = self.graph.add_node(NodeKind::Call, call_name.clone(), None);
        if let Some(node) = self.graph.node_mut(id) {
            node.alias = Some(call_name);
        }
        self.graph.add_edge(tag, id, EdgeKind::Call);
        self.graph.add_edge(tag, id, EdgeKind::Assignment);
        for expr in &tagged.tpl.exprs {
            let value = self.eval_expr(expr);
            self.graph.add_edge(value, id, EdgeKind::Argument);
        }
        id
    }

    pub(super) fn eval_array_literal(&mut self, array: &ArrayLit) -> NodeId {
        let id = self.graph.add_node(NodeKind::Variable, "<array>".into(), None);
        let element_any = self.graph.add_node(NodeKind::Variable, "<array>[]".into(), None);
        let mut bindings = vec![("[]".to_string(), element_any)];
        for (index, element) in array.elems.iter().enumerate() {
            let Some(element) = element else {
                continue;
            };
            let value = self.eval_expr(&element.expr);
            let element_node = self
                .graph
                .add_node(NodeKind::Variable, format!("<array>[{}]", index), None);
            self.flow(value, element_node);
            self.flow(element_node, element_any);
            self.flow(element_node, id);
            bindings.push((format!("[{}]", index), element_node));
        }
        self.flow(element_any, id);
        self.composite_bindings.insert(id, bindings);
        id
    }

    pub(super) fn eval_object_literal(&mut self, object: &ObjectLit) -> NodeId {
        let id = self.graph.add_node(NodeKind::Variable, "<object>".into(), None);
        let mut bindings = Vec::new();
        for prop in &object.props {
            let value = match prop {
                PropOrSpread::Prop(prop) => match &**prop {
                    Prop::KeyValue(kv) => {
                        let value = self.eval_expr(&kv.value);
                        if let Some(key) = prop_name(&kv.key) {
                            let prop_node = self.graph.add_node(
                                NodeKind::Variable,
                                format!("<object>.{}", key),
                                None,
                            );
                            self.flow(value, prop_node);
                            self.flow(prop_node, id);
                            bindings.push((format!(".{}", key), prop_node));
                            if let Some(children) = self.composite_bindings.get(&value).cloned() {
                                for (suffix, node_id) in children {
                                    bindings.push((format!(".{}{}", key, suffix), node_id));
                                }
                            }
                            continue;
                        }
                        value
                    }
                    Prop::Shorthand(i) => {
                        let value = self.eval_expr(&Expr::Ident(i.clone()));
                        let prop_node = self.graph.add_node(
                            NodeKind::Variable,
                            format!("<object>.{}", i.sym),
                            None,
                        );
                        self.flow(value, prop_node);
                        self.flow(prop_node, id);
                        bindings.push((format!(".{}", i.sym), prop_node));
                        if let Some(children) = self.composite_bindings.get(&value).cloned() {
                            for (suffix, node_id) in children {
                                bindings.push((format!(".{}{}", i.sym, suffix), node_id));
                            }
                        }
                        continue;
                    }
                    Prop::Assign(a) => {
                        let value = self.eval_expr(&a.value);
                        let prop_node = self.graph.add_node(
                            NodeKind::Variable,
                            format!("<object>.{}", a.key.sym),
                            None,
                        );
                        self.flow(value, prop_node);
                        self.flow(prop_node, id);
                        bindings.push((format!(".{}", a.key.sym), prop_node));
                        if let Some(children) = self.composite_bindings.get(&value).cloned() {
                            for (suffix, node_id) in children {
                                bindings.push((format!(".{}{}", a.key.sym, suffix), node_id));
                            }
                        }
                        continue;
                    }
                    Prop::Method(m) => {
                        let value = self.eval_fn(&FnExpr {
                            ident: prop_ident(&m.key),
                            function: m.function.clone(),
                        });
                        if let Some(key) = prop_name(&m.key) {
                            let prop_node = self.graph.add_node(
                                NodeKind::Variable,
                                format!("<object>.{}", key),
                                None,
                            );
                            self.flow(value, prop_node);
                            self.flow(prop_node, id);
                            bindings.push((format!(".{}", key), prop_node));
                            continue;
                        }
                        value
                    }
                    Prop::Getter(g) => {
                        let name = prop_ident(&g.key)
                            .map(|i| i.sym.to_string())
                            .unwrap_or_else(|| "<getter>".to_string());
                        let getter = self.graph.add_node(NodeKind::Variable, name, None);
                        let prev_fn = self.current_function_node.replace(getter);
                        if let Some(body) = &g.body {
                            self.scopes.push(Scope::new());
                            for stmt in &body.stmts {
                                self.visit_stmt(stmt);
                            }
                            self.scopes.pop();
                        }
                        self.current_function_node = prev_fn;
                        if let Some(key) = prop_name(&g.key) {
                            let prop_node = self.graph.add_node(
                                NodeKind::Variable,
                                format!("<object>.{}", key),
                                None,
                            );
                            self.flow(getter, prop_node);
                            self.flow(prop_node, id);
                            bindings.push((format!(".{}", key), prop_node));
                            continue;
                        }
                        getter
                    }
                    Prop::Setter(s) => {
                        let name = prop_ident(&s.key)
                            .map(|i| i.sym.to_string())
                            .unwrap_or_else(|| "<setter>".to_string());
                        let setter = self.graph.add_node(NodeKind::Variable, name, None);
                        self.scopes.push(Scope::new());
                        self.bind_pat(&s.param);
                        if let Some(body) = &s.body {
                            for stmt in &body.stmts {
                                self.visit_stmt(stmt);
                            }
                        }
                        self.scopes.pop();
                        if let Some(key) = prop_name(&s.key) {
                            let prop_node = self.graph.add_node(
                                NodeKind::Variable,
                                format!("<object>.{}", key),
                                None,
                            );
                            self.flow(setter, prop_node);
                            self.flow(prop_node, id);
                            bindings.push((format!(".{}", key), prop_node));
                            continue;
                        }
                        setter
                    }
                },
                PropOrSpread::Spread(s) => self.eval_expr(&s.expr),
            };
            self.flow(value, id);
        }
        self.composite_bindings.insert(id, bindings);
        id
    }

    pub(crate) fn eval_fn(&mut self, f: &FnExpr) -> NodeId {
        let name = f
            .ident
            .as_ref()
            .map(|i| i.sym.to_string())
            .unwrap_or_else(|| "<anon>".to_string());
        let id = self.graph.add_node(NodeKind::Variable, name, None);
        let prev_fn = self.current_function_node.replace(id);
        self.scopes.push(Scope::new());
        let pending_sources = self
            .pending_callback_param_sources
            .take()
            .unwrap_or_default();
        let mut params = Vec::new();
        for (index, param) in f.function.params.iter().enumerate() {
            let source = pending_sources.get(index).copied().flatten();
            params.extend(self.bind_pat_with_source(&param.pat, source));
        }
        self.last_fn_params = Some(params.clone());
        let is_outermost = self.fn_expr_depth == 0;
        self.fn_expr_depth += 1;
        if is_outermost {
            self.fn_expr_params_at_depth_0 = Some(params.clone());
        }
        self.node_to_params.insert(id, params.clone());
        if let Some(promise_node) = self.pending_promise_node {
            if let Some(resolve_param) = params.first() {
                self.promise_resolvers.push((*resolve_param, promise_node));
            }
            if let Some(reject_param) = params.get(1) {
                self.promise_resolvers.push((*reject_param, promise_node));
            }
            self.pending_promise_node = None;
        }
        if let Some(body) = &f.function.body {
            for stmt in &body.stmts {
                self.visit_stmt(stmt);
            }
        }
        self.fn_expr_depth -= 1;
        self.scopes.pop();
        self.current_function_node = prev_fn;
        id
    }

    pub(crate) fn eval_arrow(&mut self, a: &ArrowExpr) -> NodeId {
        let id = self.graph.add_node(NodeKind::Variable, "<arrow>".into(), None);
        let prev_fn = self.current_function_node.replace(id);
        self.scopes.push(Scope::new());
        let pending_sources = self
            .pending_callback_param_sources
            .take()
            .unwrap_or_default();
        let mut params = Vec::new();
        for (index, param) in a.params.iter().enumerate() {
            let source = pending_sources.get(index).copied().flatten();
            params.extend(self.bind_pat_with_source(param, source));
        }
        self.last_fn_params = Some(params.clone());
        let is_outermost = self.fn_expr_depth == 0;
        self.fn_expr_depth += 1;
        if is_outermost {
            self.fn_expr_params_at_depth_0 = Some(params.clone());
        }
        self.node_to_params.insert(id, params.clone());
        if let Some(promise_node) = self.pending_promise_node {
            if let Some(resolve_param) = params.first() {
                self.promise_resolvers.push((*resolve_param, promise_node));
            }
            if let Some(reject_param) = params.get(1) {
                self.promise_resolvers.push((*reject_param, promise_node));
            }
            self.pending_promise_node = None;
        }
        match &*a.body {
            BlockStmtOrExpr::BlockStmt(b) => {
                for stmt in &b.stmts {
                    self.visit_stmt(stmt);
                }
            }
            BlockStmtOrExpr::Expr(e) => {
                let result = self.eval_expr(e);
                self.graph.add_edge(result, id, EdgeKind::Return);
            }
        }
        self.fn_expr_depth -= 1;
        self.scopes.pop();
        self.current_function_node = prev_fn;
        id
    }
}