James Worrell

311 papers A* 58A 10B 48C 1Journal 149Unranked 40
YearRankTypeTitle / Venue / Authors
2026 A* conf
SODA
Rida Ait El Manssour, Mahsa Naraghi, Mahsa Shirmohammadi, James Worrell
2026 J jnl
Proc. ACM Program. Lang.
Rida Ait El Manssour, George Kenison, Mahsa Shirmohammadi, Anton Varonka, James Worrell
2026 A* conf
SODA
Piotr Bacik, Joël Ouaknine, James Worrell
2026 A conf
STACS
Piotr Bacik, Joël Ouaknine, David Purser, James Worrell
2025 conf
SOSA
Rida Ait El Manssour, Nikhil Balaji, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2025 J jnl
CoRR
Rida Ait El Manssour, Mahsa Naraghi, Mahsa Shirmohammadi, James Worrell
2025 conf
Principles of Formal Quantitative Analysis
Toghrul Karimov, Joris Nieuwveld, Joël Ouaknine, Mihir Vahanwala, James Worrell
2025 J jnl
CoRR
Emre Can Sertöz, Joël Ouaknine, James Worrell
2025 B conf
CONCUR
Emily Vlasman, Anto Nanah Ji, James Worrell, Franck van Breugel
2025 A* conf
LICS
Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2025 B conf
MFCS
Florian Luca, Joël Ouaknine, James Worrell
2025 J jnl
CoRR
Piotr Bacik, Joël Ouaknine, David Purser, James Worrell
2025 J jnl
CoRR
Piotr Bacik, Joël Ouaknine, James Worrell
2025 A* conf
SODA
Toghrul Karimov, Florian Luca, Joris Nieuwveld, Joël Ouaknine, James Worrell
2025 J jnl
J. ACM
Nathanaël Fijalkow, Engel Lefaucheux, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2025 J jnl
CoRR
George Kenison, Jakub Konieczny, Florian Luca, Andrew Scoones, Mahsa Shirmohammadi, James Worrell
2025 B conf
CSL
Andrew Scoones, Mahsa Shirmohammadi, James Worrell
2025 J jnl
CoRR
Amir M. Ben-Amram, Samir Genaim, Joël Ouaknine, James Worrell
2025 J jnl
Theor. Comput. Sci.
Valérie Berthé, Toghrul Karimov, Joris Nieuwveld, Joël Ouaknine, Mihir Vahanwala, James Worrell
2024 J jnl
CoRR
Rida Ait El Manssour, Nikhil Balaji, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2024 J jnl
CoRR
Rida Ait El Manssour, George Kenison, Mahsa Shirmohammadi, James Worrell
2024 J jnl
ACM SIGLOG News
Naoki Kobayashi, James Worrell
2024 ed.
FoSSaCS (1)
Naoki Kobayashi, James Worrell
2024 ed.
FoSSaCS (2)
Naoki Kobayashi, James Worrell
2024 J jnl
CoRR
Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2024 A conf
STACS
Julian D'Costa, Joël Ouaknine, James Worrell
2024 J jnl
Proc. ACM Program. Lang.
Alex Buna-Marginean, Vincent Cheval, Mahsa Shirmohammadi, James Worrell
2024 B conf
CASC
Bertrand Teguia Tabuguia, James Worrell
2024 J jnl
CoRR
Bertrand Teguia Tabuguia, James Worrell
2024 A* conf
ICALP
Pavol Kebis, Florian Luca, Joël Ouaknine, Andrew Scoones, James Worrell
2024 J jnl
CoRR
Pavol Kebis, Florian Luca, Joël Ouaknine, Andrew Scoones, James Worrell
2024 J jnl
CoRR
Rida Ait El Manssour, Vincent Cheval, Mahsa Shirmohammadi, James Worrell
2024 A* conf
LICS
Valérie Berthé, Toghrul Karimov, Joris Nieuwveld, Joël Ouaknine, Mihir Vahanwala, James Worrell
2024 J jnl
CoRR
Valérie Berthé, Toghrul Karimov, Joris Nieuwveld, Joël Ouaknine, Mihir Vahanwala, James Worrell
2024 J jnl
CoRR
Toghrul Karimov, Florian Luca, Joris Nieuwveld, Joël Ouaknine, James Worrell
2024 J jnl
Formal Methods Syst. Des.
Engel Lefaucheux, Joël Ouaknine, David Purser, James Worrell
2024 J jnl
CoRR
Andrew Scoones, Mahsa Shirmohammadi, James Worrell
2024 A* conf
ICALP
Quentin Guilmant, Engel Lefaucheux, Joël Ouaknine, James Worrell
2024 J jnl
CoRR
Quentin Guilmant, Engel Lefaucheux, Joël Ouaknine, James Worrell
2024 J jnl
CoRR
Florian Luca, Joël Ouaknine, James Worrell
2024 J jnl
CoRR
Toghrul Karimov, Joël Ouaknine, James Worrell
2023 J jnl
J. Comput. Syst. Sci.
Christel Baier, Stefan Kiefer, Joachim Klein, David Müller, James Worrell
2023 A* conf
LICS
Nikhil Balaji, Lorenzo Clemente, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2023 J jnl
CoRR
Nikhil Balaji, Lorenzo Clemente, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2023 J jnl
CoRR
Julian D'Costa, Joël Ouaknine, James Worrell
2023 J jnl
CoRR
Alex Buna-Marginean, Vincent Cheval, Mahsa Shirmohammadi, James Worrell
2023 J jnl
J. ACM
Ehud Hrushovski, Joël Ouaknine, Amaury Pouly, James Worrell
2023 J jnl
J. ACM
Ventsislav Chonev, Joël Ouaknine, James Worrell
2023 J jnl
Math. Comput.
Yuri Bilu, Florian Luca, Joris Nieuwveld, Joël Ouaknine, James Worrell
2023 A* conf
ICALP
George Kenison, Joris Nieuwveld, Joël Ouaknine, James Worrell
2023 J jnl
CoRR
Florian Luca, James Maynard, Armand Noubissie, Joël Ouaknine, James Worrell
2023 B conf
ISSAC
George Kenison, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2023 J jnl
CoRR
George Kenison, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2023 J jnl
CoRR
Valérie Berthé, Toghrul Karimov, Joël Ouaknine, Mihir Vahanwala, James Worrell
2023 A* conf
LICS
Toghrul Karimov, Edon Kelmendi, Joris Nieuwveld, Joël Ouaknine, James Worrell
2023 A* conf
ICALP
James Worrell
2023 J jnl
CoRR
Florian Luca, Joël Ouaknine, James Worrell
2022 B conf
MFCS
Florian Luca, Joël Ouaknine, James Worrell
2022 conf
FORMATS
Florian Luca, Joël Ouaknine, James Worrell
2022 B conf
MFCS
Julian D'Costa, Engel Lefaucheux, Eike Neumann, Joël Ouaknine, James Worrell
2022 J jnl
CoRR
Julian D'Costa, Engel Lefaucheux, Eike Neumann, Joël Ouaknine, James Worrell
2022 J jnl
Inf. Comput.
Martin Fränzle, Mahsa Shirmohammadi, Mani Swaminathan, James Worrell
2022 A* conf
LICS
Nikhil Balaji, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2022 J jnl
CoRR
Nikhil Balaji, Klara Nosan, Mahsa Shirmohammadi, James Worrell
2022 J jnl
ACM Trans. Comput. Log.
Shaull Almagor, Dmitry Chistikov, Joël Ouaknine, James Worrell
2022 B conf
ISSAC
Klara Nosan, Amaury Pouly, Sylvain Schmitz, Mahsa Shirmohammadi, James Worrell
2022 A* conf
LICS
Richard Lipton, Florian Luca, Joris Nieuwveld, Joël Ouaknine, David Purser, James Worrell
2022 B conf
CONCUR
Christel Baier, Florian Funke, Simon Jantsch, Toghrul Karimov, Engel Lefaucheux, Joël Ouaknine, David Purser, Markus A. Whiteland, James Worrell
2022 J jnl
CoRR
Nathanaël Fijalkow, Cristian Riveros, James Worrell
2022 J jnl
Inf. Comput.
Nathanaël Fijalkow, Cristian Riveros, James Worrell
2022 A* conf
IJCAI
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2022 J jnl
CoRR
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2022 B conf
MFCS
Yuri Bilu, Florian Luca, Joris Nieuwveld, Joël Ouaknine, David Purser, James Worrell
2022 J jnl
CoRR
Yuri Bilu, Florian Luca, Joris Nieuwveld, Joël Ouaknine, David Purser, James Worrell
2022 B conf
ISSAC
Klara Nosan, Amaury Pouly, Mahsa Shirmohammadi, James Worrell
2022 J jnl
CoRR
Klara Nosan, Amaury Pouly, Mahsa Shirmohammadi, James Worrell
2022 B conf
MFCS
Julian D'Costa, Toghrul Karimov, Rupak Majumdar, Joël Ouaknine, Mahmoud Salamati, James Worrell
2022 J jnl
CoRR
Julian D'Costa, Toghrul Karimov, Joël Ouaknine, Mahmoud Salamati, James Worrell
2022 conf
Principles of Systems Design
Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2022 J jnl
CoRR
Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2022 J jnl
Proc. ACM Program. Lang.
Toghrul Karimov, Engel Lefaucheux, Joël Ouaknine, David Purser, Anton Varonka, Markus A. Whiteland, James Worrell
2022 A* conf
NeurIPS
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2022 J jnl
CoRR
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2021 A* ed.
ICALP
Nikhil Bansal, Emanuela Merelli, James Worrell
2021 B conf
ISSAC
Nikhil Balaji, Sylvain Perifel, Mahsa Shirmohammadi, James Worrell
2021 J jnl
Proc. ACM Program. Lang.
Shaull Almagor, Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2021 A* conf
ICALP
Eike Neumann, Joël Ouaknine, James Worrell
2021 J jnl
Theory Comput. Syst.
Shaull Almagor, Joël Ouaknine, James Worrell
2021 B conf
MFCS
George Kenison, Oleksiy Klurman, Engel Lefaucheux, Florian Luca, Pieter Moree, Joël Ouaknine, Markus A. Whiteland, James Worrell
2021 B conf
MFCS
Julian D'Costa, Engel Lefaucheux, Eike Neumann, Joël Ouaknine, James Worrell
2021 J jnl
CoRR
Julian D'Costa, Engel Lefaucheux, Eike Neumann, Joël Ouaknine, James Worrell
2021 J jnl
CoRR
Klara Nosan, Amaury Pouly, Mahsa Shirmohammadi, James Worrell
2021 J jnl
J. Mach. Learn. Res.
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2021 conf
CAV (2)
Engel Lefaucheux, Joël Ouaknine, David Purser, James Worrell
2021 J jnl
CoRR
Engel Lefaucheux, Joël Ouaknine, David Purser, James Worrell
2021 B conf
CONCUR
Christel Baier, Florian Funke, Simon Jantsch, Toghrul Karimov, Engel Lefaucheux, Florian Luca, Joël Ouaknine, David Purser, Markus A. Whiteland, James Worrell
2021 J jnl
CoRR
Christel Baier, Florian Funke, Simon Jantsch, Engel Lefaucheux, Florian Luca, Joël Ouaknine, David Purser, Markus A. Whiteland, James Worrell
2021 B conf
MFCS
Julian D'Costa, Toghrul Karimov, Rupak Majumdar, Joël Ouaknine, Mahmoud Salamati, Sadegh Soudjani, James Worrell
2021 A* conf
LICS
Florian Luca, Joël Ouaknine, James Worrell
2021 J jnl
J. Comput. Syst. Sci.
Laure Daviaud, Marcin Jurdzinski, Ranko Lazic, Filip Mazowiecki, Guillermo A. Pérez, James Worrell
2020 B conf
CONCUR
Rupak Majumdar, Joël Ouaknine, Amaury Pouly, James Worrell
2020 B conf
CONCUR
Shaull Almagor, Nathann Cohen, Guillermo A. Pérez, Mahsa Shirmohammadi, James Worrell
2020 J jnl
CoRR
Nikhil Balaji, Sylvain Perifel, Mahsa Shirmohammadi, James Worrell
2020 J jnl
CoRR
Shaull Almagor, Toghrul Karimov, Edon Kelmendi, Joël Ouaknine, James Worrell
2020 J jnl
Inf. Process. Lett.
Martin Fränzle, Karin Quaas, Mahsa Shirmohammadi, James Worrell
2020 A conf
STACS
Julian D'Costa, Engel Lefaucheux, Joël Ouaknine, James Worrell
2020 A* conf
ICALP
Shaull Almagor, Edon Kelmendi, Joël Ouaknine, James Worrell
2020 J jnl
CoRR
Shaull Almagor, Edon Kelmendi, Joël Ouaknine, James Worrell
2020 B conf
MFCS
Toghrul Karimov, Joël Ouaknine, James Worrell
2020 J jnl
CoRR
Toghrul Karimov, Joël Ouaknine, James Worrell
2020 J jnl
CoRR
George Kenison, Oleksiy Klurman, Engel Lefaucheux, Florian Luca, Pieter Moree, Joël Ouaknine, Markus A. Whiteland, James Worrell
2020 B conf
CONCUR
Eike Neumann, Joël Ouaknine, James Worrell
2020 J jnl
CoRR
George Kenison, Richard Lipton, Joël Ouaknine, James Worrell
2020 B conf
ISSAC
George Kenison, Richard Lipton, Joël Ouaknine, James Worrell
2019 J jnl
CoRR
Rupak Majumdar, Joël Ouaknine, Amaury Pouly, James Worrell
2019 J jnl
Theory Comput. Syst.
Nathanaël Fijalkow, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2019 J jnl
CoRR
Shaull Almagor, Nathann Cohen, Guillermo A. Pérez, Mahsa Shirmohammadi, James Worrell
2019 J jnl
CoRR
Martin Fränzle, Karin Quaas, Mahsa Shirmohammadi, James Worrell
2019 A* conf
ICALP
Thomas Colcombet, Joël Ouaknine, Pavel Semukhin, James Worrell
2019 J jnl
CoRR
Mehran Hosseini, Joël Ouaknine, James Worrell
2019 J jnl
CoRR
Thomas Colcombet, Joël Ouaknine, Pavel Semukhin, James Worrell
2019 J jnl
J. ACM
Joël Ouaknine, Amaury Pouly, João Sousa Pinto, James Worrell
2019 A* conf
LICS
Florent Guépin, Christoph Haase, James Worrell
2019 J jnl
Log. Methods Comput. Sci.
Hsi-Ming Ho, Joël Ouaknine, James Worrell
2019 A* conf
NeurIPS
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2019 J jnl
CoRR
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2019 J jnl
Electron. Colloquium Comput. Complex.
Pascale Gourdeau, Varun Kanade, Marta Kwiatkowska, James Worrell
2019 B conf
SAS
Nathanaël Fijalkow, Engel Lefaucheux, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2019 J jnl
CoRR
Nathanaël Fijalkow, Engel Lefaucheux, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2019 conf
HSCC
Nathanaël Fijalkow, Joël Ouaknine, Amaury Pouly, João Sousa Pinto, James Worrell
2019 A* conf
ICALP
Mehran Hosseini, Joël Ouaknine, James Worrell
2019 A conf
STACS
Shaull Almagor, Joël Ouaknine, James Worrell
2019 J jnl
CoRR
Shaull Almagor, Joël Ouaknine, James Worrell
2018 B ed.
MFCS
Igor Potapov, Paul G. Spirakis, James Worrell
2018 A* conf
ICALP
Martin Fränzle, Mahsa Shirmohammadi, Mani Swaminathan, James Worrell
2018 J jnl
CoRR
Martin Fränzle, Mahsa Shirmohammadi, Mani Swaminathan, James Worrell
2018 B conf
CONCUR
Shaull Almagor, Brynmor Chapman, Mehran Hosseini, Joël Ouaknine, James Worrell
2018 J jnl
CoRR
Shaull Almagor, Brynmor Chapman, Mehran Hosseini, Joël Ouaknine, James Worrell
2018 J jnl
Log. Methods Comput. Sci.
Vojtech Forejt, Petr Jancar, Stefan Kiefer, James Worrell
2018 J jnl
Log. Methods Comput. Sci.
Antonia Lechner, Richard Mayr, Joël Ouaknine, Amaury Pouly, James Worrell
2018 ch.
Handbook of Model Checking
Patricia Bouyer, Uli Fahrenberg, Kim Guldstrand Larsen, Nicolas Markey, Joël Ouaknine, James Worrell
2018 A* conf
ICALP
Shaull Almagor, Dmitry Chistikov, Joël Ouaknine, James Worrell
2018 J jnl
CoRR
Shaull Almagor, Dmitry Chistikov, Joël Ouaknine, James Worrell
2018 J jnl
CoRR
Nathanaël Fijalkow, Joël Ouaknine, Amaury Pouly, João Sousa Pinto, James Worrell
2018 J jnl
CoRR
Hsi-Ming Ho, Joël Ouaknine, James Worrell
2018 A* conf
LICS
Ehud Hrushovski, Joël Ouaknine, Amaury Pouly, James Worrell
2018 J jnl
CoRR
Ehud Hrushovski, Joël Ouaknine, Amaury Pouly, James Worrell
2018 J jnl
Theor. Comput. Sci.
Joël Ouaknine, Igor Potapov, James Worrell
2018 J jnl
CoRR
Laure Daviaud, Marcin Jurdzinski, Ranko Lazic, Filip Mazowiecki, Guillermo A. Pérez, James Worrell
2018 A* conf
ICALP
Laure Daviaud, Marcin Jurdzinski, Ranko Lazic, Filip Mazowiecki, Guillermo A. Pérez, James Worrell
2017 J jnl
CoRR
Vojtech Forejt, Petr Jancar, Stefan Kiefer, James Worrell
2017 J jnl
Log. Methods Comput. Sci.
Stefan Kiefer, Ines Marusic, James Worrell
2017 J jnl
SIAM J. Appl. Algebra Geom.
Dmitry Chistikov, Stefan Kiefer, Ines Marusic, Mahsa Shirmohammadi, James Worrell
2017 A* conf
SODA
Dmitry Chistikov, Stefan Kiefer, Ines Marusic, Mahsa Shirmohammadi, James Worrell
2017 conf
HSCC
Joël Ouaknine, João Sousa Pinto, James Worrell
2017 A* conf
LICS
Michael Benedikt, Timothy Duff, Aditya Sharad, James Worrell
2017 B conf
CONCUR
Nathanaël Fijalkow, Cristian Riveros, James Worrell
2017 J jnl
CoRR
Karin Quaas, Mahsa Shirmohammadi, James Worrell
2017 A* conf
LICS
Karin Quaas, Mahsa Shirmohammadi, James Worrell
2017 A conf
STACS
Nathanaël Fijalkow, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2017 J jnl
CoRR
Nathanaël Fijalkow, Pierre Ohlmann, Joël Ouaknine, Amaury Pouly, James Worrell
2017 A* conf
ICALP
Shaull Almagor, Joël Ouaknine, James Worrell
2017 B conf
ISSAC
James Worrell
2017 conf
Models, Algorithms, Logics and Tools
Patricia Bouyer, François Laroussinie, Nicolas Markey, Joël Ouaknine, James Worrell
2016 J jnl
ACM Trans. Comput. Log.
Saguy Benaim, Michael Benedikt, Witold Charatonik, Emanuel Kieronski, Rastislav Lenhardt, Filip Mazowiecki, James Worrell
2016 conf
CAV (1)
Christel Baier, Stefan Kiefer, Joachim Klein, Sascha Klüppelholz, David Müller, James Worrell
2016 J jnl
CoRR
Christel Baier, Stefan Kiefer, Joachim Klein, Sascha Klüppelholz, David Müller, James Worrell
2016 B conf
CONCUR
Antonia Lechner, Richard Mayr, Joël Ouaknine, Amaury Pouly, James Worrell
2016 J jnl
CoRR
Antonia Lechner, Richard Mayr, Joël Ouaknine, Amaury Pouly, James Worrell
2016 J jnl
CoRR
Dmitry Chistikov, Stefan Kiefer, Ines Marusic, Mahsa Shirmohammadi, James Worrell
2016 A* conf
LICS
Ventsislav Chonev, Joël Ouaknine, James Worrell
2016 A* conf
ICALP
Dmitry Chistikov, Stefan Kiefer, Ines Marusic, Mahsa Shirmohammadi, James Worrell
2016 J jnl
CoRR
Dmitry Chistikov, Stefan Kiefer, Ines Marusic, Mahsa Shirmohammadi, James Worrell
2016 J jnl
J. ACM
Ventsislav Chonev, Joël Ouaknine, James Worrell
2016 A* conf
ICALP
Ventsislav Chonev, Joël Ouaknine, James Worrell
2016 A* conf
ICALP
Maria Bruna, Radu Grigore, Stefan Kiefer, Joël Ouaknine, James Worrell
2016 J jnl
Fundam. Informaticae
Christoph Haase, Joël Ouaknine, James Worrell
2016 A* conf
LICS
Joël Ouaknine, Amaury Pouly, João Sousa Pinto, James Worrell
2016 J jnl
CoRR
Joël Ouaknine, Amaury Pouly, João Sousa Pinto, James Worrell
2016 J jnl
CoRR
Shaull Almagor, Joël Ouaknine, James Worrell
2016 J jnl
ACM Trans. Comput. Log.
Ranko Lazic, Joël Ouaknine, James Worrell
2015 J jnl
J. Mach. Learn. Res.
Ines Marusic, James Worrell
2015 B conf
FoSSaCS
Stefan Kiefer, Ines Marusic, James Worrell
2015 A conf
STACS
Esther Galby, Joël Ouaknine, James Worrell
2015 A* conf
SODA
Joël Ouaknine, João Sousa Pinto, James Worrell
2015 J jnl
ACM SIGLOG News
Joël Ouaknine, James Worrell
2015 A* conf
LICS
Antonia Lechner, Joël Ouaknine, James Worrell
2015 J jnl
CoRR
Ventsislav Chonev, Joël Ouaknine, James Worrell
2015 J jnl
CoRR
Ventsislav Chonev, Joël Ouaknine, James Worrell
2015 J jnl
CoRR
Maria Bruna, Radu Grigore, Stefan Kiefer, Joël Ouaknine, James Worrell
2015 B conf
CONCUR
James Worrell
2015 conf
FSTTCS
James Worrell
2015 J jnl
Inf. Process. Lett.
S. Akshay, Timos Antonopoulos, Joël Ouaknine, James Worrell
2015 J jnl
CoRR
Joël Ouaknine, João Sousa Pinto, James Worrell
2015 A* conf
SODA
Ventsislav Chonev, Joël Ouaknine, James Worrell
2015 B conf
FoSSaCS
Timos Antonopoulos, Paul Hunter, Shahab Raza, James Worrell
2014 conf
MFCS (1)
Ines Marusic, James Worrell
2014 J jnl
CoRR
Ines Marusic, James Worrell
2014 J jnl
Inf. Comput.
Vojtech Forejt, Petr Jancar, Stefan Kiefer, James Worrell
2014 J jnl
CoRR
Michael Benedikt, Rastislav Lenhardt, James Worrell
2014 J jnl
CoRR
Joël Ouaknine, João Sousa Pinto, James Worrell
2014 conf
ICALP (2)
Joël Ouaknine, James Worrell
2014 B conf
RV
Hsi-Ming Ho, Joël Ouaknine, James Worrell
2014 A* conf
SODA
Joël Ouaknine, James Worrell
2014 ed.
RP
Joël Ouaknine, Igor Potapov, James Worrell
2014 conf
Horizons of the Mind
Franck van Breugel, James Worrell
2014 J jnl
CoRR
Ventsislav Chonev, Joël Ouaknine, James Worrell
2014 J jnl
CoRR
Timos Antonopoulos, Paul Hunter, Shahab Raza, James Worrell
2014 conf
ICALP (2)
Joël Ouaknine, James Worrell
2013 J jnl
Log. Methods Comput. Sci.
Joël Ouaknine, Hristina Palikareva, A. W. Roscoe, James Worrell
2013 J jnl
Theor. Comput. Sci.
Franck van Breugel, Claudio Hermida, Michael Makkai, James Worrell
2013 J jnl
Formal Methods Syst. Des.
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2013 conf
ICALP (2)
Saguy Benaim, Michael Benedikt, Witold Charatonik, Emanuel Kieronski, Rastislav Lenhardt, Filip Mazowiecki, James Worrell
2013 J jnl
CoRR
Saguy Benaim, Michael Benedikt, Rastislav Lenhardt, James Worrell
2013 J jnl
CoRR
Joël Ouaknine, James Worrell
2013 A* conf
LICS
Paul Hunter, Joël Ouaknine, James Worrell
2013 A conf
TACAS
Michael Benedikt, Rastislav Lenhardt, James Worrell
2013 J jnl
Log. Methods Comput. Sci.
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2013 J jnl
CoRR
James Worrell
2013 J jnl
CoRR
Joël Ouaknine, James Worrell
2013 conf
ICALP (2)
James Worrell
2013 J jnl
CoRR
Ventsislav Chonev, Joël Ouaknine, James Worrell
2013 A* conf
STOC
Ventsislav Chonev, Joël Ouaknine, James Worrell
2013 B conf
ATVA
Thomas Brihaye, Laurent Doyen, Gilles Geeraerts, Joël Ouaknine, Jean-François Raskin, James Worrell
2013 J jnl
Log. Methods Comput. Sci.
Michael Benedikt, Rastislav Lenhardt, James Worrell
2013 J jnl
CoRR
Joël Ouaknine, James Worrell
2013 B conf
MFCS
Ranko Lazic, Joël Ouaknine, James Worrell
2012 A* conf
CAV
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2012 J jnl
CoRR
Vojtech Forejt, Petr Jancar, Stefan Kiefer, James Worrell
2012 conf
FSTTCS
Vojtech Forejt, Petr Jancar, Stefan Kiefer, James Worrell
2012 B conf
FoSSaCS
Stefan Göller, Christoph Haase, Joël Ouaknine, James Worrell
2012 conf
RP
Joël Ouaknine, James Worrell
2012 J jnl
CoRR
Paul Hunter, Joël Ouaknine, James Worrell
2012 J jnl
Formal Aspects Comput.
Patricia Bouyer, Nicolas Markey, Joël Ouaknine, Philippe Schnoebelen, James Worrell
2012 B conf
FoSSaCS
Di Chen, Franck van Breugel, James Worrell
2012 B conf
FoSSaCS
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2012 A* conf
LICS
Daniel Bundala, Joël Ouaknine, James Worrell
2012 conf
RP
Christoph Haase, Joël Ouaknine, James Worrell
2012 A* conf
CAV
Philip J. Armstrong, Michael Goldsmith, Gavin Lowe, Joël Ouaknine, Hristina Palikareva, A. W. Roscoe, James Worrell
2012 J jnl
Formal Aspects Comput.
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2012 J jnl
CoRR
Thomas Brihaye, Laurent Doyen, Gilles Geeraerts, Joël Ouaknine, Jean-François Raskin, James Worrell
2012 conf
QEST
Michael Benedikt, Rastislav Lenhardt, James Worrell
2012 J jnl
CoRR
Paul Hunter, Joël Ouaknine, James Worrell
2011 A* conf
CAV
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2011 A* conf
CAV
Daniel Kroening, Joël Ouaknine, Ofer Strichman, Thomas Wahl, James Worrell
2011 J jnl
CoRR
Thomas Brihaye, Laurent Doyen, Gilles Geeraerts, Joël Ouaknine, Jean-François Raskin, James Worrell
2011 conf
ICALP (2)
Thomas Brihaye, Laurent Doyen, Gilles Geeraerts, Joël Ouaknine, Jean-François Raskin, James Worrell
2011 J jnl
CoRR
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, James Worrell, Lijun Zhang
2011 conf
ICALP (2)
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, James Worrell, Lijun Zhang
2011 J jnl
CoRR
Stefan Kiefer, Andrzej S. Murawski, Joël Ouaknine, Björn Wachter, James Worrell
2011 B conf
CONCUR
Joël Ouaknine, Hristina Palikareva, A. W. Roscoe, James Worrell
2011 B conf
CSL
Mark Jenkins, Joël Ouaknine, Alexander Rabinovich, James Worrell
2011 B conf
CONCUR
Byron Cook, Christoph Haase, Joël Ouaknine, Matthew J. Parkinson, James Worrell
2011 B conf
CONCUR
Michael Benedikt, Rastislav Lenhardt, James Worrell
2010 A* conf
LICS
Mark Jenkins, Joël Ouaknine, Alexander Rabinovich, James Worrell
2010 conf
FSTTCS
Paul Hunter, Patricia Bouyer, Nicolas Markey, Joël Ouaknine, James Worrell
2010 conf
ICALP (2)
Stefan Göller, Christoph Haase, Joël Ouaknine, James Worrell
2010 ch.
Reflections on the Work of C. A. R. Hoare
Christoph Haase, Joël Ouaknine, James Worrell
2010 conf
ICALP (2)
Joël Ouaknine, James Worrell
2009 B conf
CONCUR
Christoph Haase, Stephan Kreutzer, Joël Ouaknine, James Worrell
2009 B conf
CONCUR
Joël Ouaknine, Alexander Rabinovich, James Worrell
2008 J jnl
CoRR
Franck van Breugel, Babita Sharma, James Worrell
2008 J jnl
Log. Methods Comput. Sci.
Franck van Breugel, Babita Sharma, James Worrell
2008 conf
QEST
James Worrell
2008 J jnl
Fundam. Informaticae
Ranko Lazic, Thomas Christopher Newcomb, Joël Ouaknine, A. W. Roscoe, James Worrell
2008 A conf
TACAS
Axel Legay, Andrzej S. Murawski, Joël Ouaknine, James Worrell
2008 conf
ICALP (2)
Patricia Bouyer, Nicolas Markey, Joël Ouaknine, James Worrell
2008 J jnl
CoRR
Patricia Bouyer, Nicolas Markey, Joël Ouaknine, Philippe Schnoebelen, James Worrell
2008 A conf
STACS
Patricia Bouyer, Nicolas Markey, Joël Ouaknine, Philippe Schnoebelen, James Worrell
2008 C conf
TIME
James Worrell
2008 conf
FORMATS
Joël Ouaknine, James Worrell
2008 J jnl
Fundam. Informaticae
Parosh Aziz Abdulla, Johann Deneux, Joël Ouaknine, Karin Quaas, James Worrell
2007 B conf
FoSSaCS
Franck van Breugel, Babita Sharma, James Worrell
2007 conf
Computation, Meaning, and Logic
Michael W. Mislove, Dusko Pavlovic, James Worrell
2007 conf
ICATPN
Ranko Lazic, Thomas Christopher Newcomb, Joël Ouaknine, A. W. Roscoe, James Worrell
2007 J jnl
CoRR
Joël Ouaknine, James Worrell
2007 J jnl
Log. Methods Comput. Sci.
Joël Ouaknine, James Worrell
2007 J jnl
Theor. Comput. Sci.
Franck van Breugel, Claudio Hermida, Michael Makkai, James Worrell
2007 A* conf
LICS
Patricia Bouyer, Nicolas Markey, Joël Ouaknine, James Worrell
2007 conf
FORMATS
Sara Adams, Joël Ouaknine, James Worrell
2007 conf
FSEN
Parosh Aziz Abdulla, Joël Ouaknine, Karin Quaas, James Worrell
2006 J jnl
Theor. Comput. Sci.
Franck van Breugel, James Worrell
2006 B conf
FoSSaCS
Joël Ouaknine, James Worrell
2006 A conf
TACAS
Joël Ouaknine, James Worrell
2006 conf
AMAST
Dusko Pavlovic, Michael W. Mislove, James Worrell
2005 J jnl
Theor. Comput. Sci.
Franck van Breugel, James Worrell
2005 J jnl
Math. Struct. Comput. Sci.
James Worrell
2005 A* conf
ICALP
Franck van Breugel, Claudio Hermida, Michael Makkai, James Worrell
2005 A* conf
ICALP
Parosh Aziz Abdulla, Johann Deneux, Joël Ouaknine, James Worrell
2005 J jnl
Theor. Comput. Sci.
Franck van Breugel, Michael W. Mislove, Joël Ouaknine, James Worrell
2005 A* conf
LICS
Joël Ouaknine, James Worrell
2005 J jnl
Theor. Comput. Sci.
James Worrell
2004 B conf
FoSSaCS
Michael W. Mislove, Joël Ouaknine, Dusko Pavlovic, James Worrell
2004 J jnl
Theor. Comput. Sci.
Keye Martin, Michael W. Mislove, James Worrell
2004 A* conf
LICS
Joël Ouaknine, James Worrell
2003 B conf
FoSSaCS
Franck van Breugel, Michael W. Mislove, Joël Ouaknine, James Worrell
2003 conf
EXPRESS
Michael W. Mislove, Joël Ouaknine, James Worrell
2003 A* conf
LICS
Joël Ouaknine, James Worrell
2003 J jnl
Nord. J. Comput.
Joël Ouaknine, James Worrell
2003 conf
HSCC
Joël Ouaknine, James Worrell
2002 conf
CMCS
James Worrell
2002 A* conf
ICALP
Keye Martin, Michael W. Mislove, James Worrell
2002 A* conf
ICALP
Franck van Breugel, Steven Shalit, James Worrell
2002 conf
EXPRESS
Joël Ouaknine, James Worrell
2001 B conf
CONCUR
Franck van Breugel, James Worrell
2001 J jnl
Theor. Comput. Sci.
Peter T. Johnstone, John Power, Toru Tsujishita, Hiroshi Watanabe, James Worrell
2001 A* conf
ICALP
Franck van Breugel, James Worrell
2000 conf
CMCS
James Worrell
1999 conf
CMCS
James Worrell
1998 A* conf
LICS
Peter T. Johnstone, John Power, Toru Tsujishita, Hiroshi Watanabe, James Worrell
1998 conf
CMCS
James Worrell
redb/extractors/yara.py
← Index redb/extractors/yara.py python
"""
YARA Extractor - Scans binary files with YARA rules and stores matches in ClickHouse.

This extractor uses the yara-x library to scan samples against a collection of
YARA rules located in the 'yara/' folder at the project root.

Supports both:
- Pre-compiled rules (.yarac) for faster loading
- Source rules (.yar/.yara) compiled on-the-fly

Schema Design:
- yara_rules: Rule metadata stored once per unique rule (deduplicated by rule_id)
- yara_matches: Sample-rule matches with binary sha256 for efficiency
"""
import inspect
import json
import os
import re
import threading
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

import xxhash
import yara_x

from redb.extractors.enum import Tag
from redb.extractors.extractor import Extractor
from redb.models.dataclasses import YaraMatch, YaraRule


# Default compiled rules filename (can be overridden via YARA_COMPILED_RULES env var)
COMPILED_RULES_FILENAME = os.getenv("YARA_COMPILED_RULES", "compiled_rules.yarac")

# Default source collection name
DEFAULT_SOURCE_COLLECTION = os.getenv("YARA_SOURCE_COLLECTION", "default")


def canonicalize_rule(rule_text: str) -> str:
    """
    Canonicalize YARA rule text for consistent hashing.

    Strips comments and normalizes whitespace, but excludes metadata
    so rules with same logic but different metadata get the same ID.
    """
    # Strip single-line comments
    text = re.sub(r'//.*$', '', rule_text, flags=re.MULTILINE)
    # Strip multi-line comments
    text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
    # Strip meta section (keep only strings/condition)
    text = re.sub(r'meta\s*:\s*[^}]+(?=strings|condition|})', '', text, flags=re.DOTALL)
    # Normalize whitespace
    text = ' '.join(text.split())
    return text


def generate_rule_id(rule_text: str) -> int:
    """
    Generate a unique rule_id from canonicalized rule content.

    Returns:
        UInt64 hash of the canonical rule content
    """
    canonical = canonicalize_rule(rule_text)
    return xxhash.xxh64(canonical.encode('utf-8')).intdigest()


def sha256_hex_to_binary(hex_str: str) -> bytes:
    """Convert SHA256 hex string to binary (32 bytes)."""
    return bytes.fromhex(hex_str)


def sha256_binary_to_hex(binary: bytes) -> str:
    """Convert SHA256 binary (32 bytes) to hex string."""
    return binary.hex()


def parse_yara_rules(file_content: str) -> List[Dict[str, Any]]:
    """
    Parse individual YARA rules from file content.

    Handles files with multiple rules and extracts:
    - rule_name: The rule identifier
    - rule_tags: List of tags (from 'rule Name : tag1 tag2 {')
    - rule_meta: Dict of metadata key-value pairs
    - rule_text: Full rule source code

    Args:
        file_content: Raw content of a .yar/.yara file

    Returns:
        List of dicts, each containing rule_name, rule_tags, rule_meta, rule_text
    """
    rules = []

    # Pattern to match rule declarations: rule Name or rule Name : tags
    # We need to find rule boundaries by tracking braces
    rule_pattern = re.compile(
        r'(?:^|\n)\s*((?:private\s+|global\s+)*rule\s+(\w+)\s*(?::\s*([^{]*))?\s*\{)',
        re.MULTILINE
    )

    matches = list(rule_pattern.finditer(file_content))

    for i, match in enumerate(matches):
        rule_start = match.start(1)  # Start of 'rule ...'
        rule_name = match.group(2)
        tags_str = match.group(3) or ""
        rule_tags = [t.strip() for t in tags_str.split() if t.strip()]

        # Find the matching closing brace by counting braces
        brace_count = 0
        rule_end = match.end()
        in_string = False
        escape_next = False

        for j, char in enumerate(file_content[match.end() - 1:], start=match.end() - 1):
            if escape_next:
                escape_next = False
                continue
            if char == '\\':
                escape_next = True
                continue
            if char == '"' and not escape_next:
                in_string = not in_string
                continue
            if in_string:
                continue
            if char == '{':
                brace_count += 1
            elif char == '}':
                brace_count -= 1
                if brace_count == 0:
                    rule_end = j + 1
                    break

        rule_text = file_content[rule_start:rule_end].strip()

        # Extract metadata from rule
        meta = {}
        meta_match = re.search(
            r'meta\s*:\s*(.*?)(?=strings\s*:|condition\s*:|$)',
            rule_text,
            re.DOTALL
        )
        if meta_match:
            meta_text = meta_match.group(1)
            for line in meta_text.strip().split('\n'):
                if '=' in line:
                    key, _, value = line.partition('=')
                    key = key.strip()
                    value = value.strip().strip('"\'')
                    if key and not key.startswith('//'):
                        meta[key] = value

        rules.append({
            'rule_name': rule_name,
            'rule_tags': rule_tags,
            'rule_meta': meta,
            'rule_text': rule_text,
        })

    return rules


class YaraBatchInsertBuffer:
    """
    Thread-safe buffer for batching YARA match inserts.

    Collects matches from multiple samples and flushes to ClickHouse
    when batch_size is reached or flush_interval expires.
    """

    def __init__(
        self,
        exporter,
        index_prefix: str,
        batch_size: int = 1000,
        flush_interval: int = 30,
    ):
        self.exporter = exporter
        self.index_prefix = index_prefix
        self.batch_size = batch_size
        self.flush_interval = flush_interval

        self.matches_buffer: List[List] = []
        self.rules_buffer: Dict[int, List] = {}  # rule_id -> rule_data (deduplicated)
        self.lock = threading.Lock()
        self.last_flush = time.time()

        # Start background flush timer
        self._stop_timer = False
        self._timer_thread = threading.Thread(target=self._flush_timer, daemon=True)
        self._timer_thread.start()

    def add(self, sha256_binary: bytes, matches: List[Tuple], rules: Dict[int, List]) -> None:
        """
        Add matches and rules to buffer.

        Args:
            sha256_binary: Binary SHA256 (32 bytes)
            matches: List of match tuples (sha256_binary, rule_id, rule_name, scan_date, match_strings)
            rules: Dict of rule_id -> rule_data tuples
        """
        with self.lock:
            self.matches_buffer.extend(matches)
            # Merge rules (deduplicated by rule_id)
            for rule_id, rule_data in rules.items():
                if rule_id not in self.rules_buffer:
                    self.rules_buffer[rule_id] = rule_data

            if len(self.matches_buffer) >= self.batch_size:
                self._flush_locked()

    def _flush_locked(self) -> None:
        """Flush buffer (must hold lock)."""
        if not self.matches_buffer:
            return

        try:
            # Insert rules first (deduplicated)
            if self.rules_buffer:
                rules_data = list(self.rules_buffer.values())
                self.exporter.batch_insert(
                    table='yara_rules',
                    rows=rules_data,
                    column_names=[
                        'rule_id', 'rule_name', 'source_collection',
                        'ingested_at', 'rule_text', 'rule_meta', 'rule_tags'
                    ],
                    column_type_names=[
                        'UInt64', 'String', 'LowCardinality(String)',
                        "DateTime64(3, 'UTC')", 'String', 'JSON', 'Array(LowCardinality(String))'
                    ]
                )

            # Insert matches
            self.exporter.batch_insert(
                table='yara_matches',
                rows=self.matches_buffer,
                column_names=[
                    'sha256', 'rule_id', 'rule_name',
                    'scan_date', 'match_strings'
                ],
                column_type_names=[
                    'FixedString(32)', 'UInt64', 'LowCardinality(String)',
                    "DateTime64(3, 'UTC')", 'Array(String)'
                ]
            )

            print(f"[YARA] Flushed {len(self.matches_buffer)} matches and {len(self.rules_buffer)} rules")

        except Exception as e:
            print(f"[YARA] Error flushing batch: {e}")

        # Clear buffers
        self.matches_buffer = []
        self.rules_buffer = {}
        self.last_flush = time.time()

    def flush(self) -> None:
        """Public flush (acquires lock)."""
        with self.lock:
            self._flush_locked()

    def _flush_timer(self) -> None:
        """Background timer for periodic flushes."""
        while not self._stop_timer:
            time.sleep(5)  # Check every 5 seconds
            with self.lock:
                if time.time() - self.last_flush > self.flush_interval and self.matches_buffer:
                    self._flush_locked()

    def stop(self) -> None:
        """Stop the background timer and flush remaining data."""
        self._stop_timer = True
        self.flush()


class YaraExtractor(Extractor):
    """
    Extractor that scans binary files with YARA rules.

    The YARA rules are loaded from the 'yara/' folder at the project root.
    Each matching rule is stored in ClickHouse with its metadata.

    Supports pre-compiled rules for faster loading:
    - If 'yara/compiled_rules.yarac' exists, it will be loaded directly
    - Otherwise, all .yar/.yara files are compiled and cached in memory
    - Use YaraExtractor.compile_and_save() to pre-compile rules
    """

    # Class-level cache for compiled YARA rules
    _compiled_rules = None
    _rules_path = None

    def __init__(
        self,
        filepath: str,
        log: Any,
        exporters: Optional[List] = None,
        index_prefix: Optional[str] = None,
        known_benign: bool = False,
        known_malicious: bool = False,
    ):
        super().__init__(
            filepath,
            log,
            exporters,
            index_prefix,
            known_benign,
            known_malicious,
        )
        self.matches: List[YaraMatch] = []
        self.rules: Dict[str, YaraRule] = {}  # rule_name -> YaraRule (deduplicated)

    @classmethod
    def get_yara_rules_path(cls) -> Path:
        """Get the path to the YARA rules directory."""
        # Default to 'yara/' in project root
        project_root = Path(__file__).parent.parent.parent
        default_path = project_root / "yara"

        # Allow override via environment variable
        rules_path = os.getenv("YARA_RULES_PATH", str(default_path))
        return Path(rules_path)

    @classmethod
    def get_compiled_rules_path(cls) -> Path:
        """Get the path to the pre-compiled rules file."""
        return cls.get_yara_rules_path() / COMPILED_RULES_FILENAME

    @classmethod
    def load_compiled_rules(cls) -> Optional[yara_x.Rules]:
        """
        Load pre-compiled YARA rules from .yarac file.

        Returns:
            Compiled Rules object or None if file doesn't exist
        """
        compiled_path = cls.get_compiled_rules_path()
        if not compiled_path.exists():
            return None

        try:
            with open(compiled_path, "rb") as f:
                rules = yara_x.Rules.deserialize_from(f)
            # Only log in debug mode (non-prod) to avoid spamming in bulk processing
            if os.getenv("SERVER_ENV") != "prod":
                print(f"[DEBUG] Loaded pre-compiled YARA rules from {compiled_path}")
            return rules
        except Exception as e:
            print(f"[WARNING] Failed to load compiled rules from {compiled_path}: {e}")
            return None

    @classmethod
    def compile_rules_from_source(cls) -> Optional[yara_x.Rules]:
        """
        Compile all YARA rules from source .yar/.yara files.

        Returns:
            Compiled Rules object or None if no rules found
        """
        rules_path = cls.get_yara_rules_path()

        if not rules_path.exists():
            return None

        # Find all .yar and .yara files recursively
        rule_files = []
        for ext in ["*.yar", "*.yara"]:
            rule_files.extend(rules_path.rglob(ext))

        if not rule_files:
            return None

        print(f"[INFO] Compiling {len(rule_files)} YARA rule files from {rules_path}")

        # Compile all rules using yara-x compiler
        compiler = yara_x.Compiler()
        compiled_count = 0
        failed_count = 0

        for rule_file in rule_files:
            try:
                with open(rule_file, "r", encoding="utf-8") as f:
                    rule_content = f.read()
                # Use the relative path from rules_path as namespace
                namespace = str(rule_file.relative_to(rules_path).parent)
                if namespace == ".":
                    namespace = "default"
                compiler.new_namespace(namespace)
                compiler.add_source(rule_content)
                compiled_count += 1
            except Exception as e:
                # Log warning but continue with other rules
                print(f"[WARNING] Failed to compile YARA rule {rule_file}: {e}")
                failed_count += 1
                continue

        try:
            rules = compiler.build()
            print(f"[INFO] Successfully compiled {compiled_count} rule files ({failed_count} failed)")
            return rules
        except Exception as e:
            print(f"[ERROR] Failed to build YARA rules: {e}")
            return None

    @classmethod
    def compile_rules(cls, force_reload: bool = False) -> Optional[yara_x.Rules]:
        """
        Get compiled YARA rules, loading from cache, .yarac file, or compiling from source.

        Priority:
        1. Return cached rules if available
        2. Load pre-compiled .yarac file if it exists
        3. Compile from source .yar/.yara files

        Args:
            force_reload: If True, ignore cache and reload rules

        Returns:
            Compiled YARA rules or None if no rules found
        """
        rules_path = cls.get_yara_rules_path()

        # Return cached rules if available and path hasn't changed
        if (
            cls._compiled_rules is not None
            and cls._rules_path == rules_path
            and not force_reload
        ):
            return cls._compiled_rules

        # Try loading pre-compiled rules first
        rules = cls.load_compiled_rules()

        # If no pre-compiled rules, compile from source
        if rules is None:
            rules = cls.compile_rules_from_source()

        # Cache the rules
        if rules is not None:
            cls._compiled_rules = rules
            cls._rules_path = rules_path

        return rules

    @classmethod
    def compile_and_save(cls, output_path: Optional[Path] = None) -> bool:
        """
        Compile all YARA rules from source and save to a .yarac file.

        This is useful for pre-compiling rules for faster loading in production.

        Args:
            output_path: Path to save compiled rules (default: yara/compiled_rules.yarac)

        Returns:
            True if successful, False otherwise
        """
        if output_path is None:
            output_path = cls.get_compiled_rules_path()

        # Force compile from source
        rules = cls.compile_rules_from_source()
        if rules is None:
            print("[ERROR] No rules to compile")
            return False

        try:
            # Ensure parent directory exists
            output_path.parent.mkdir(parents=True, exist_ok=True)

            with open(output_path, "wb") as f:
                rules.serialize_into(f)

            print(f"[INFO] Saved compiled YARA rules to {output_path}")
            return True
        except Exception as e:
            print(f"[ERROR] Failed to save compiled rules: {e}")
            return False

    def _extract_yara_matches(self) -> Tuple[List[YaraMatch], Dict[str, YaraRule]]:
        """
        Scan the binary with compiled YARA rules.

        Returns:
            Tuple of (matches list, rules dict) for each matching rule
        """
        self.log.debug(inspect.currentframe().f_code.co_name)

        compiled_rules = self.compile_rules()
        if compiled_rules is None:
            self.log.warning("No YARA rules found or compiled")
            return [], {}

        matches = []
        rules = {}

        try:
            # Scan the binary data
            scan_results = compiled_rules.scan(self.binary)

            # Process each matching rule
            for rule in scan_results.matching_rules:
                rule_name = rule.identifier

                # Extract rule metadata and store rule (deduplicated by name)
                if rule_name not in rules:
                    meta = {}
                    for identifier, value in rule.metadata:
                        meta[identifier] = str(value)
                    rules[rule_name] = YaraRule(
                        rule_name=rule_name,
                        rule_tags=list(rule.tags),
                        rule_meta=meta,
                    )

                # Extract matched string identifiers
                matched_strings = []
                for pattern in rule.patterns:
                    for match in pattern.matches:
                        matched_strings.append(pattern.identifier)

                # Remove duplicates from matched strings
                matched_strings = list(set(matched_strings))

                yara_match = YaraMatch(
                    rule_name=rule_name,
                    match_strings=matched_strings,
                )
                matches.append(yara_match)

                self.log.debug(
                    f"YARA match: {rule_name} (tags: {list(rule.tags)})"
                )

        except Exception as e:
            self.log.error(f"Error scanning with YARA: {e}")
            return [], {}

        return matches, rules

    def extract(self) -> Optional[List[YaraMatch]]:
        """
        Extract YARA matches from the binary.

        Returns:
            List of YaraMatch objects or None on error
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        try:
            self.matches, self.rules = self._extract_yara_matches()
            return self.matches if self.matches else None
        except Exception as e:
            self.log.error(f"Error extracting YARA matches: {e}")
            return None

    def tag(self) -> str:
        """Return the tag for this extractor."""
        return Tag.YARA.value

    def get_clickhouse_table(self) -> str:
        """Return the ClickHouse table name for YARA matches."""
        return "yara_matches"

    def get_clickhouse_tables(self) -> Dict[str, str]:
        """Return all ClickHouse table names for multi-table support."""
        return {
            'rules': 'yara_rules',
            'matches': 'yara_matches',
        }

    def prepare_export_data(self, exporter_type: str) -> Any:
        """
        Prepare data for export to the specified exporter type.

        Uses optimized normalized schema with two tables:
        - yara_rules: Rule metadata with rule_id (UInt64 hash)
        - yara_matches: Matches with binary sha256 (32 bytes) and rule_id

        Args:
            exporter_type: The type of exporter (e.g., 'ClickHouseExporter')

        Returns:
            Data formatted for the specified exporter
        """

        if exporter_type == "ClickHouseExporter":
            if not self.matches:
                return None

            current_time = datetime.now(timezone.utc)
            source_collection = DEFAULT_SOURCE_COLLECTION

            # Convert sha256 hex to binary (32 bytes)
            sha256_binary = sha256_hex_to_binary(self.sha256)

            # Build rules data from self.rules (deduplicated by rule_id)
            rules_data = {}  # rule_id -> rule_data
            for rule_name, rule in self.rules.items():
                rule_content = f"rule {rule_name} {{ }}"  # Simplified for now
                rule_id = generate_rule_id(rule_content)
                if rule_id not in rules_data:
                    rules_data[rule_id] = [
                        rule_id,
                        rule.rule_name,
                        source_collection,
                        current_time,  # ingested_at
                        "",  # rule_text (empty for now, populated during sync)
                        json.dumps(rule.rule_meta),
                        rule.rule_tags,
                    ]

            # Build matches data
            matches_data = []
            for match in self.matches:
                rule_content = f"rule {match.rule_name} {{ }}"
                rule_id = generate_rule_id(rule_content)

                matches_data.append([
                    sha256_binary,  # Binary sha256 (32 bytes)
                    rule_id,
                    match.rule_name,
                    current_time,  # scan_date
                    match.match_strings,
                ])

            return {
                'multi_table': True,
                'rules': {
                    'table': 'yara_rules',
                    'data': list(rules_data.values()),
                    'column_names': [
                        'rule_id',
                        'rule_name',
                        'source_collection',
                        'ingested_at',
                        'rule_text',
                        'rule_meta',
                        'rule_tags',
                    ],
                    'column_type_names': [
                        'UInt64',
                        'String',
                        'LowCardinality(String)',
                        "DateTime64(3, 'UTC')",
                        'String',
                        'JSON',
                        'Array(LowCardinality(String))',
                    ],
                },
                'matches': {
                    'table': 'yara_matches',
                    'data': matches_data,
                    'column_names': [
                        'sha256',
                        'rule_id',
                        'rule_name',
                        'scan_date',
                        'match_strings',
                    ],
                    'column_type_names': [
                        'FixedString(32)',  # Binary sha256
                        'UInt64',
                        'LowCardinality(String)',
                        "DateTime64(3, 'UTC')",
                        'Array(String)',
                    ],
                },
            }

        return None


def sync_rules_to_db(source_collection: str = None) -> bool:
    """
    Sync all YARA rules from source files to the database.

    This ensures all rules exist in yara_rules table before scanning begins.
    Should be run before batch scanning or in container initialization.

    Args:
        source_collection: Source collection name (default from env)

    Returns:
        True if successful, False otherwise
    """
    from redb import settings

    source_collection = source_collection or DEFAULT_SOURCE_COLLECTION
    rules_path = YaraExtractor.get_yara_rules_path()

    if not rules_path.exists():
        print(f"[ERROR] Rules path does not exist: {rules_path}")
        return False

    # Find all rule files
    rule_files = []
    for ext in ["*.yar", "*.yara"]:
        rule_files.extend(rules_path.rglob(ext))

    if not rule_files:
        print(f"[INFO] No rule files found in {rules_path}")
        return True

    print(f"[INFO] Syncing {len(rule_files)} rule files to database...")

    current_time = datetime.now(timezone.utc)
    rules_data = []
    total_rules = 0

    for rule_file in rule_files:
        try:
            with open(rule_file, "r", encoding="utf-8") as f:
                file_content = f.read()

            # Parse individual rules from file (handles multiple rules per file)
            parsed_rules = parse_yara_rules(file_content)

            for rule in parsed_rules:
                rule_id = generate_rule_id(rule['rule_text'])

                rules_data.append([
                    rule_id,
                    rule['rule_name'],
                    source_collection,
                    current_time,
                    rule['rule_text'],
                    json.dumps(rule['rule_meta']),
                    rule['rule_tags'],
                ])
                total_rules += 1

        except Exception as e:
            print(f"[WARNING] Failed to parse rule file {rule_file}: {e}")
            continue

    print(f"[INFO] Parsed {total_rules} individual rules from {len(rule_files)} files")

    if not rules_data:
        print("[INFO] No rules to sync")
        return True

    # Insert rules to database
    try:
        client = settings.create_clickhouse_client()
        table = "yara_rules"

        client.insert(
            table,
            rules_data,
            column_names=[
                'rule_id', 'rule_name', 'source_collection',
                'ingested_at', 'rule_text', 'rule_meta', 'rule_tags'
            ],
            column_type_names=[
                'UInt64', 'String', 'LowCardinality(String)',
                "DateTime64(3, 'UTC')", 'String', 'JSON', 'Array(LowCardinality(String))'
            ]
        )

        print(f"[INFO] Successfully synced {len(rules_data)} rules to {table}")
        client.close()
        return True

    except Exception as e:
        print(f"[ERROR] Failed to sync rules to database: {e}")
        return False


# CLI utility for pre-compiling rules and syncing
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="YARA Rules Management Utility")
    parser.add_argument(
        "--compile",
        action="store_true",
        help="Compile all YARA rules and save to .yarac file",
    )
    parser.add_argument(
        "--sync-rules",
        action="store_true",
        help="Sync all YARA rules to the database (run before batch scanning)",
    )
    parser.add_argument(
        "--output",
        type=str,
        help="Output path for compiled rules (default: yara/compiled_rules.yarac)",
    )
    parser.add_argument(
        "--rules-path",
        type=str,
        help="Path to YARA rules directory (default: yara/)",
    )
    parser.add_argument(
        "--source-collection",
        type=str,
        help="Source collection name for rules (default: from YARA_SOURCE_COLLECTION env)",
    )

    args = parser.parse_args()

    if args.rules_path:
        os.environ["YARA_RULES_PATH"] = args.rules_path

    if args.compile:
        output_path = Path(args.output) if args.output else None
        success = YaraExtractor.compile_and_save(output_path)
        if not success:
            exit(1)

    if args.sync_rules:
        success = sync_rules_to_db(args.source_collection)
        if not success:
            exit(1)

    if not args.compile and not args.sync_rules:
        parser.print_help()