Xiao Wang

155 papers A* 41Journal 91Unranked 22
YearRankTypeTitle / Venue / Authors
2025 J jnl
J. Cryptol.
Hongrui Cui, Xiao Wang, Kang Yang, Yu Yu
2025 J jnl
IACR Cryptol. ePrint Arch.
Zhenkai Hu, Haofei Liang, Xiao Wang, Xiang Xie, Kang Yang, Yu Yu, Wenhao Zhang
2025 J jnl
J. Cryptol.
Dung Bui, Haotian Chu, Geoffroy Couteau, Xiao Wang, Chenkai Weng, Kang Yang, Yu Yu
2025 conf
CRYPTO (4)
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2025 J jnl
IACR Cryptol. ePrint Arch.
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2025 J jnl
IACR Cryptol. ePrint Arch.
Wenhao Zhang, Hanlin Liu, Kang Yang, Wen-jie Lu, Yu Yu, Xiao Wang, Chenkai Weng
2025 conf
EUROCRYPT (7)
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2025 A* conf
CCS
Yunqing Sun, Hanlin Liu, Kang Yang, Yu Yu, Xiao Wang, Chenkai Weng
2025 J jnl
IACR Cryptol. ePrint Arch.
Yunqing Sun, Hanlin Liu, Kang Yang, Yu Yu, Xiao Wang, Chenkai Weng
2025 A* conf
ICML
Stephan Rabanser, Ali Shahin Shamsabadi, Olive Franzese, Xiao Wang, Adrian Weller, Nicolas Papernot
2025 J jnl
CoRR
Stephan Rabanser, Ali Shahin Shamsabadi, Olive Franzese, Xiao Wang, Adrian Weller, Nicolas Papernot
2025 A* conf
USENIX Security Symposium
Yuncong Hu, Pratyush Mishra, Xiao Wang, Jie Xie, Kang Yang, Yu Yu, Yuwen Zhang
2025 J jnl
IACR Cryptol. ePrint Arch.
Yuncong Hu, Pratyush Mishra, Xiao Wang, Jie Xie, Kang Yang, Yu Yu, Yuwen Zhang
2025 J jnl
IACR Cryptol. ePrint Arch.
Xiaojie Guo, Hanlin Liu, Zhicong Huang, Hongrui Cui, Wenhao Zhang, Cheng Hong, Xiao Wang, Kang Yang, Yu Yu
2025 J jnl
IACR Cryptol. ePrint Arch.
Wenhao Zhang, Xiao Wang, Chenkai Weng
2025 J jnl
Cryptogr. Commun.
Vladimir Kolesnikov, Stanislav Peceny, Ni Trieu, Xiao Wang
2025 A* conf
CCS
Gefei Tan, Adrià Gascón, Sarah Meiklejohn, Mariana Raykova, Xiao Wang, Ning Luo
2025 J jnl
IACR Cryptol. ePrint Arch.
Gefei Tan, Adrià Gascón, Sarah Meiklejohn, Mariana Raykova, Xiao Wang, Ning Luo
2025 conf
CSLAW
Peter K. Chan, Alyson Carrel, Mayank Varia, Xiao Wang
2025 J jnl
IACR Cryptol. ePrint Arch.
Zhengyi Li, Kang Yang, Jin Tan, Wen-jie Lu, Haoqi Wu, Xiao Wang, Yu Yu, Derun Zhao, Yancheng Zheng, Minyi Guo, Jingwen Leng
2025 A* conf
ICLR
Chongyang Gao, Lixu Wang, Kaize Ding, Chenkai Weng, Xiao Wang, Qi Zhu
2025 J jnl
Des. Codes Cryptogr.
Chun Guo, Xiao Wang, Kang Yang, Yu Yu
2025 A* conf
CCS
Olive Franzese, Congyu Fang, Radhika Garg, Xiao Wang, Somesh Jha, Nicolas Papernot, Adam Dziedzic
2025 J jnl
IACR Cryptol. ePrint Arch.
Olive Franzese, Congyu Fang, Radhika Garg, Somesh Jha, Nicolas Papernot, Xiao Wang, Adam Dziedzic
2025 J jnl
IACR Cryptol. ePrint Arch.
Hongrui Cui, Chun Guo, Xiaojie Guo, Xiao Wang, Kang Yang, Yu Yu
2025 A* conf
SP
Radhika Garg, Xiao Wang
2025 J jnl
IACR Cryptol. ePrint Arch.
Radhika Garg, Xiao Wang
2025 A* conf
CVPR
Lixu Wang, Bingqi Shang, Yi Li, Payal Mohapatra, Wei Dong, Xiao Wang, Qi Zhu
2025 J jnl
CoRR
Lixu Wang, Bingqi Shang, Yi Li, Payal Mohapatra, Wei Dong, Xiao Wang, Qi Zhu
2025 conf
PKC (5)
Qi Feng, Kang Yang, Kaiyi Zhang, Xiao Wang, Yu Yu, Xiang Xie
2024 J jnl
IACR Cryptol. ePrint Arch.
Hongrui Cui, Chun Guo, Xiao Wang, Chenkai Weng, Kang Yang, Yu Yu
2024 A* conf
CCS
Yunqing Sun, Jonathan Katz, Mariana Raykova, Phillipp Schoppmann, Xiao Wang
2024 A* conf
ICLR
Ali Shahin Shamsabadi, Gefei Tan, Tudor Cebere, Aurélien Bellet, Hamed Haddadi, Nicolas Papernot, Xiao Wang, Adrian Weller
2024 A* conf
SP
Wenhao Zhang, Xiaojie Guo, Kang Yang, Ruiyu Zhu, Yu Yu, Xiao Wang
2024 J jnl
IACR Cryptol. ePrint Arch.
Wenhao Zhang, Xiaojie Guo, Kang Yang, Ruiyu Zhu, Yu Yu, Xiao Wang
2024 J jnl
CoRR
Lixu Wang, Yang Zhao, Jiahua Dong, Ating Yin, Qinbin Li, Xiao Wang, Dusit Niyato, Qi Zhu
2024 J jnl
IACR Cryptol. ePrint Arch.
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2024 J jnl
IACR Cryptol. ePrint Arch.
Yunqing Sun, Jonathan Katz, Mariana Raykova, Phillipp Schoppmann, Xiao Wang
2024 J jnl
IACR Cryptol. ePrint Arch.
Ruiyang Li, Yiteng Sun, Chun Guo, François-Xavier Standaert, Weijia Wang, Xiao Wang
2024 A* conf
CCS
Ruiyang Li, Yiteng Sun, Chun Guo, François-Xavier Standaert, Weijia Wang, Xiao Wang
2024 A* conf
USENIX Security Symposium
Xiang Xie, Kang Yang, Xiao Wang, Yu Yu
2024 A* conf
NeurIPS
Zhengyi Li, Kang Yang, Jin Tan, Wen-jie Lu, Haoqi Wu, Xiao Wang, Yu Yu, Derun Zhao, Yancheng Zheng, Minyi Guo, Jingwen Leng
2024 J jnl
CoRR
Zhengyi Li, Kang Yang, Jin Tan, Wen-jie Lu, Haoqi Wu, Xiao Wang, Yu Yu, Derun Zhao, Yancheng Zheng, Minyi Guo, Jingwen Leng
2024 J jnl
IACR Cryptol. ePrint Arch.
Chun Guo, Xiao Wang, Kang Yang, Yu Yu
2024 J jnl
IEEE Trans. Parallel Distributed Syst.
Yuezhi Che, Dazhao Cheng, Xiao Wang, Rujia Wang
2024 A* conf
ICML
Yue Huang, Lichao Sun, Haoran Wang, Siyuan Wu, Qihui Zhang, Yuan Li, Chujie Gao, Yixin Huang, Wenhan Lyu, Yixuan Zhang, Xiner Li, Hanchi Sun, Zhengliang Liu, Yixin Liu, Yijue Wang, Zhikun Zhang, Bertie Vidgen, Bhavya Kailkhura, Caiming Xiong, Chaowei Xiao, Chunyuan Li, Eric P. Xing, Furong Huang, Hao Liu, Heng Ji, Hongyi Wang, Huan Zhang, Huaxiu Yao, Manolis Kellis, Marinka Zitnik, Meng Jiang, Mohit Bansal, James Zou, Jian Pei, Jian Liu, Jianfeng Gao, Jiawei Han, Jieyu Zhao, Jiliang Tang, Jindong Wang, Joaquin Vanschoren, John C. Mitchell, Kai Shu, Kaidi Xu, Kai-Wei Chang, Lifang He, Lifu Huang, Michael Backes, Neil Zhenqiang Gong, Philip S. Yu, Pin-Yu Chen, Quanquan Gu, Ran Xu, Rex Ying, Shuiwang Ji, Suman Jana, Tianlong Chen, Tianming Liu, Tianyi Zhou, William Wang, Xiang Li, Xiangliang Zhang, Xiao Wang, Xing Xie, Xun Chen, Xuyu Wang, Yan Liu, Yanfang Ye, Yinzhi Cao, Yong Chen, Yue Zhao
2024 J jnl
CoRR
Chongyang Gao, Lixu Wang, Chenkai Weng, Xiao Wang, Qi Zhu
2024 A* conf
SP
Radhika Garg, Kang Yang, Jonathan Katz, Xiao Wang
2024 J jnl
IACR Cryptol. ePrint Arch.
Qi Feng, Kang Yang, Kaiyi Zhang, Xiao Wang, Yu Yu, Xiang Xie, Debiao He
2024 conf
EUROCRYPT (6)
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2024 J jnl
IACR Cryptol. ePrint Arch.
Chun Guo, Xiao Wang, Xiang Xie, Yu Yu
2024 J jnl
IACR Cryptol. ePrint Arch.
Ruiyang Li, Chun Guo, Xiao Wang
2024 J jnl
CoRR
Lichao Sun, Yue Huang, Haoran Wang, Siyuan Wu, Qihui Zhang, Chujie Gao, Yixin Huang, Wenhan Lyu, Yixuan Zhang, Xiner Li, Zhengliang Liu, Yixin Liu, Yijue Wang, Zhikun Zhang, Bhavya Kailkhura, Caiming Xiong, Chaowei Xiao, Chunyuan Li, Eric P. Xing, Furong Huang, Hao Liu, Heng Ji, Hongyi Wang, Huan Zhang, Huaxiu Yao, Manolis Kellis, Marinka Zitnik, Meng Jiang, Mohit Bansal, James Zou, Jian Pei, Jian Liu, Jianfeng Gao, Jiawei Han, Jieyu Zhao, Jiliang Tang, Jindong Wang, John C. Mitchell, Kai Shu, Kaidi Xu, Kai-Wei Chang, Lifang He, Lifu Huang, Michael Backes, Neil Zhenqiang Gong, Philip S. Yu, Pin-Yu Chen, Quanquan Gu, Ran Xu, Rex Ying, Shuiwang Ji, Suman Jana, Tianlong Chen, Tianming Liu, Tianyi Zhou, William Wang, Xiang Li, Xiangliang Zhang, Xiao Wang, Xing Xie, Xun Chen, Xuyu Wang, Yan Liu, Yanfang Ye, Yinzhi Cao, Yue Zhao
2024 A* conf
USENIX Security Symposium
Daniel Luick, John C. Kolesar, Timos Antonopoulos, William R. Harris, James Parker, Ruzica Piskac, Eran Tromer, Xiao Wang, Ning Luo
2023 conf
EUROCRYPT (2)
Hongrui Cui, Xiao Wang, Kang Yang, Yu Yu
2023 J jnl
IACR Cryptol. ePrint Arch.
Hongrui Cui, Xiao Wang, Kang Yang, Yu Yu
2023 J jnl
IACR Cryptol. ePrint Arch.
Dung Bui, Haotian Chu, Geoffroy Couteau, Xiao Wang, Chenkai Weng, Kang Yang, Yu Yu
2023 A* conf
ICLR
Ali Shahin Shamsabadi, Sierra Calanda Wyllie, Nicholas Franzese, Natalie Dullerud, Sébastien Gambs, Nicolas Papernot, Xiao Wang, Adrian Weller
2023 J jnl
CoRR
Chenxi Liu, Lixu Wang, Lingjuan Lyu, Chen Sun, Xiao Wang, Qi Zhu
2023 A* conf
ICLR
Chenxi Liu, Lixu Wang, Lingjuan Lyu, Chen Sun, Xiao Wang, Qi Zhu
2023 J jnl
IEEE Trans. Inf. Forensics Secur.
Xiao Lan, Hongjian Jin, Hui Guo, Xiao Wang
2023 conf
CSCML
Vladimir Kolesnikov, Stanislav Peceny, Ni Trieu, Xiao Wang
2023 J jnl
IACR Cryptol. ePrint Arch.
Vladimir Kolesnikov, Stanislav Peceny, Ni Trieu, Xiao Wang
2023 J jnl
CoRR
Lixu Wang, Chenxi Liu, Junfeng Guo, Jiahua Dong, Xiao Wang, Heng Huang, Qi Zhu
2023 J jnl
CoRR
Yingzhou Lu, Minjie Shen, Yue Zhao, Chenhao Li, Fan Meng, Xiao Wang, David M. Herrington, Yue Wang, Tim Fu, Capucine Van Rechem
2023 conf
EUROCRYPT (1)
Xiaojie Guo, Kang Yang, Xiao Wang, Wenhao Zhang, Xiang Xie, Jiang Zhang, Zheli Liu
2023 J jnl
IACR Cryptol. ePrint Arch.
Xiang Xie, Kang Yang, Xiao Wang, Yu Yu
2023 A* conf
CCS
Yuyang Sang, Ning Luo, Samuel Judson, Ben Chaimberg, Timos Antonopoulos, Xiao Wang, Ruzica Piskac, Zhong Shao
2023 J jnl
IACR Cryptol. ePrint Arch.
Yuyang Sang, Ning Luo, Samuel Judson, Ben Chaimberg, Timos Antonopoulos, Xiao Wang, Ruzica Piskac, Zhong Shao
2023 J jnl
Proc. Priv. Enhancing Technol.
Adam Dziedzic, Christopher A. Choquette-Choo, Natalie Dullerud, Vinith M. Suriyakumar, Ali Shahin Shamsabadi, Muhammad Ahmad Kaleem, Somesh Jha, Nicolas Papernot, Xiao Wang
2023 J jnl
Proc. VLDB Endow.
Xiling Li, Gefei Tan, Xiao Wang, Jennie Rogers, Soamar Homsi
2023 A* conf
NeurIPS
Nicholas Franzese, Adam Dziedzic, Christopher A. Choquette-Choo, Mark R. Thomas, Muhammad Ahmad Kaleem, Stephan Rabanser, Congyu Fang, Somesh Jha, Nicolas Papernot, Xiao Wang
2023 J jnl
CoRR
Olive Franzese, Adam Dziedzic, Christopher A. Choquette-Choo, Mark R. Thomas, Muhammad Ahmad Kaleem, Stephan Rabanser, Congyu Fang, Somesh Jha, Nicolas Papernot, Xiao Wang
2023 J jnl
IACR Cryptol. ePrint Arch.
Radhika Garg, Kang Yang, Jonathan Katz, Xiao Wang
2023 J jnl
IACR Cryptol. ePrint Arch.
Carsten Baum, Samuel Dittmer, Peter Scholl, Xiao Wang
2023 J jnl
Des. Codes Cryptogr.
Carsten Baum, Samuel Dittmer, Peter Scholl, Xiao Wang
2023 J jnl
ACM Trans. Priv. Secur.
Chun Guo, Xiao Wang, Xiang Xie, Yu Yu
2023 J jnl
IACR Cryptol. ePrint Arch.
Xiaojie Guo, Kang Yang, Xiao Wang, Yu Yu, Zheli Liu
2023 J jnl
IACR Cryptol. ePrint Arch.
Daniel Luick, John C. Kolesar, Timos Antonopoulos, William R. Harris, James Parker, Ruzica Piskac, Eran Tromer, Xiao Wang, Ning Luo
2023 J jnl
Proc. VLDB Endow.
Xiling Li, Chenkai Weng, Yongxin Xu, Xiao Wang, Jennie Rogers
2022 A* conf
CCS
Chenkai Weng, Kang Yang, Zhaomin Yang, Xiang Xie, Xiao Wang
2022 J jnl
IACR Cryptol. ePrint Arch.
Chenkai Weng, Kang Yang, Zhaomin Yang, Xiang Xie, Xiao Wang
2022 A* conf
CVPR
Jiahua Dong, Lixu Wang, Zhen Fang, Gan Sun, Shichao Xu, Xiao Wang, Qi Zhu
2022 J jnl
CoRR
Jiahua Dong, Lixu Wang, Zhen Fang, Gan Sun, Shichao Xu, Xiao Wang, Qi Zhu
2022 J jnl
IACR Cryptol. ePrint Arch.
Xiaojie Guo, Kang Yang, Xiao Wang, Wenhao Zhang, Xiang Xie, Jiang Zhang, Zheli Liu
2022 J jnl
IACR Cryptol. ePrint Arch.
Kang Yang, Xiao Wang
2022 A* conf
ICLR
Lixu Wang, Shichao Xu, Ruiqi Xu, Xiao Wang, Qi Zhu
2022 conf
ASIACRYPT (3)
Kang Yang, Xiao Wang
2022 J jnl
CoRR
Adam Dziedzic, Christopher A. Choquette-Choo, Natalie Dullerud, Vinith Menon Suriyakumar, Ali Shahin Shamsabadi, Muhammad Ahmad Kaleem, Somesh Jha, Nicolas Papernot, Xiao Wang
2022 A* conf
CCS
Ning Luo, Timos Antonopoulos, William R. Harris, Ruzica Piskac, Eran Tromer, Xiao Wang
2022 J jnl
IACR Cryptol. ePrint Arch.
Ning Luo, Timos Antonopoulos, William R. Harris, Ruzica Piskac, Eran Tromer, Xiao Wang
2022 J jnl
IACR Cryptol. ePrint Arch.
Hanlin Liu, Xiao Wang, Kang Yang, Yu Yu
2022 conf
ASIACRYPT (2)
Ran Canetti, Pratik Sarkar, Xiao Wang
2022 J jnl
CoRR
Jennie Rogers, Elizabeth Adetoro, Johes Bater, Talia Canter, Dong Fu, Andrew Hamilton, Amro Hassan, Ashley Martinez, Erick Michalski, Vesna Mitrovic, Fred D. Rachman, Raj C. Shah, Matt Sterling, Kyra VanDoren, Theresa L. Walunas, Xiao Wang, Abel N. Kho
2022 A* conf
USENIX Security Symposium
Ning Luo, Samuel Judson, Timos Antonopoulos, Ruzica Piskac, Xiao Wang
2021 A* conf
AAAI
Lixu Wang, Shichao Xu, Xiao Wang, Qi Zhu
2021 A* conf
ICLR
Christopher A. Choquette-Choo, Natalie Dullerud, Adam Dziedzic, Yunxiang Zhang, Somesh Jha, Nicolas Papernot, Xiao Wang
2021 J jnl
CoRR
Christopher A. Choquette-Choo, Natalie Dullerud, Adam Dziedzic, Yunxiang Zhang, Somesh Jha, Nicolas Papernot, Xiao Wang
2021 A* conf
CCS
Nicholas Franzese, Jonathan Katz, Steve Lu, Rafail Ostrovsky, Xiao Wang, Chenkai Weng
2021 J jnl
IACR Cryptol. ePrint Arch.
Nicholas Franzese, Jonathan Katz, Steve Lu, Rafail Ostrovsky, Xiao Wang, Chenkai Weng
2021 A* conf
INFOCOM
Yunxiang Cai, Hongzi Zhu, Xiao Wang, Shan Chang, Jiangang Shen, Minyi Guo
2021 A* conf
USENIX Security Symposium
Chenkai Weng, Kang Yang, Xiang Xie, Jonathan Katz, Xiao Wang
2021 J jnl
IACR Cryptol. ePrint Arch.
Chenkai Weng, Kang Yang, Xiang Xie, Jonathan Katz, Xiao Wang
2021 J jnl
CoRR
Lixu Wang, Shichao Xu, Ruiqi Xu, Xiao Wang, Qi Zhu
2021 conf
SIGMOD Conference
Xi He, Jennie Rogers, Johes Bater, Ashwin Machanavajjhala, Chenghong Wang, Xiao Wang
2021 J jnl
IACR Trans. Symmetric Cryptol.
Chun Guo, François-Xavier Standaert, Weijia Wang, Xiao Wang, Yu Yu
2021 A* conf
CCS
Kang Yang, Pratik Sarkar, Chenkai Weng, Xiao Wang
2021 J jnl
IACR Cryptol. ePrint Arch.
Kang Yang, Pratik Sarkar, Chenkai Weng, Xiao Wang
2021 A* conf
SP
Chenkai Weng, Kang Yang, Jonathan Katz, Xiao Wang
2021 J jnl
IACR Cryptol. ePrint Arch.
Ning Luo, Samuel Judson, Timos Antonopoulos, Ruzica Piskac, Xiao Wang
2020 conf
CRYPTO (2)
Chun Guo, Jonathan Katz, Xiao Wang, Chenkai Weng, Yu Yu
2020 conf
Public Key Cryptography (2)
Ran Canetti, Pratik Sarkar, Xiao Wang
2020 J jnl
IACR Cryptol. ePrint Arch.
Ran Canetti, Pratik Sarkar, Xiao Wang
2020 conf
ASIACRYPT (3)
Ran Canetti, Pratik Sarkar, Xiao Wang
2020 J jnl
IACR Cryptol. ePrint Arch.
Ran Canetti, Pratik Sarkar, Xiao Wang
2020 A* conf
SP
Chun Guo, Jonathan Katz, Xiao Wang, Yu Yu
2020 J jnl
IACR Cryptol. ePrint Arch.
Chenkai Weng, Kang Yang, Jonathan Katz, Xiao Wang
2020 A* conf
CCS
Kang Yang, Chenkai Weng, Xiao Lan, Jiang Zhang, Xiao Wang
2020 J jnl
IACR Cryptol. ePrint Arch.
Kang Yang, Chenkai Weng, Xiao Lan, Jiang Zhang, Xiao Wang
2020 A* conf
CCS
Kang Yang, Xiao Wang, Jiang Zhang
2020 J jnl
Proc. VLDB Endow.
Johes Bater, Yongjoo Park, Xi He, Xiao Wang, Jennie Rogers
2020 J jnl
CoRR
Lixu Wang, Shichao Xu, Xiao Wang, Qi Zhu
2020 J jnl
IACR Cryptol. ePrint Arch.
Ran Canetti, Pratik Sarkar, Xiao Wang
2019 J jnl
IACR Cryptol. ePrint Arch.
Chun Guo, Jonathan Katz, Xiao Wang, Chenkai Weng, Yu Yu
2019 conf
EUROCRYPT (3)
Cheng Hong, Jonathan Katz, Vladimir Kolesnikov, Wen-jie Lu, Xiao Wang
2019 J jnl
CoRR
Lixu Wang, Shichao Xu, Xiao Wang, Qi Zhu
2019 J jnl
IACR Cryptol. ePrint Arch.
Chun Guo, Jonathan Katz, Xiao Wang, Yu Yu
2019 J jnl
IACR Cryptol. ePrint Arch.
Kang Yang, Xiao Wang, Jiang Zhang
2019 conf
Poly/DMAH@VLDB
Jennie Rogers, Johes Bater, Xi He, Ashwin Machanavajjhala, Madhav Suresh, Xiao Wang
2019 conf
ASIACRYPT (2)
Vladimir Kolesnikov, Mike Rosulek, Ni Trieu, Xiao Wang
2019 J jnl
IACR Cryptol. ePrint Arch.
Vladimir Kolesnikov, Mike Rosulek, Ni Trieu, Xiao Wang
2018
Xiao Wang
2018 J jnl
IACR Cryptol. ePrint Arch.
Cheng Hong, Jonathan Katz, Vladimir Kolesnikov, Wen-jie Lu, Xiao Wang
2018 A* conf
CCS
Jonathan Katz, Vladimir Kolesnikov, Xiao Wang
2018 J jnl
IACR Cryptol. ePrint Arch.
Jonathan Katz, Vladimir Kolesnikov, Xiao Wang
2018 conf
CRYPTO (3)
Jonathan Katz, Samuel Ranellucci, Mike Rosulek, Xiao Wang
2018 J jnl
IACR Cryptol. ePrint Arch.
Jonathan Katz, Samuel Ranellucci, Mike Rosulek, Xiao Wang
2018 conf
ASIACRYPT (3)
S. Dov Gordon, Samuel Ranellucci, Xiao Wang
2018 J jnl
IACR Cryptol. ePrint Arch.
S. Dov Gordon, Samuel Ranellucci, Xiao Wang
2018 conf
ASIACRYPT (3)
S. Dov Gordon, Jonathan Katz, Xiao Wang
2018 J jnl
IACR Cryptol. ePrint Arch.
Xiao Wang, S. Dov Gordon, Jonathan Katz
2017 J jnl
IACR Cryptol. ePrint Arch.
Jonathan Katz, Samuel Ranellucci, Xiao Wang
2017 J jnl
IACR Cryptol. ePrint Arch.
Jonathan Katz, Samuel Ranellucci, Xiao Wang
2017 A* conf
CCS
Xiao Wang, Samuel Ranellucci, Jonathan Katz
2017 conf
EUROCRYPT (3)
Xiao Wang, Alex J. Malozemoff, Jonathan Katz
2017 A* conf
CCS
Xiao Wang, Samuel Ranellucci, Jonathan Katz
2016 J jnl
IACR Cryptol. ePrint Arch.
Jonathan Katz, Alex J. Malozemoff, Xiao Wang
2016 J jnl
IACR Cryptol. ePrint Arch.
Xiao Wang, Alex J. Malozemoff, Jonathan Katz
2016 A* conf
IEEE Symposium on Security and Privacy
Samee Zahur, Xiao Wang, Mariana Raykova, Adrià Gascón, Jack Doerner, David Evans, Jonathan Katz
2016 conf
ESORICS (2)
Xiao Wang, S. Dov Gordon, Allen McIntosh, Jonathan Katz
2015 A* conf
CCS
Xiao Wang, T.-H. Hubert Chan, Elaine Shi
2015 J jnl
IACR Cryptol. ePrint Arch.
Xiao Wang, S. Dov Gordon, Allen McIntosh, Jonathan Katz
2014 J jnl
IACR Cryptol. ePrint Arch.
Xiao Wang, T.-H. Hubert Chan, Elaine Shi
2014 J jnl
IACR Cryptol. ePrint Arch.
Xiao Wang, Kartik Nayak, Chang Liu, Elaine Shi, Emil Stefanov, Yan Huang
2013 J jnl
IEEE Trans. Image Process.
Xiangsheng Huang, Zhen Lei, Mingyu Fan, Xiao Wang, Stan Z. Li
tests/unit/test_decompile_modules.py
← Index tests/unit/test_decompile_modules.py python
"""
Unit tests for --decompile-modules flag.

Tests that:
1. CLI argument parsing and validation works correctly
2. decompile_modules is threaded through the entire call chain
3. BinaryNinjaDecompiler.analyze_binary() gates extractions per selected modules
4. DecompileBinja.prepare_export_data() filters DB tables per selected modules
"""
import os
import pytest
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.unit]


# ============================================================================
# CLI Argument Parsing Tests
# ============================================================================

class TestDecompileModulesCliArgument:
    """Tests for --decompile-modules CLI argument parsing in start.py."""

    def _parse_args(self, args_list):
        """Helper to parse CLI args without running main()."""
        import argparse

        parser = argparse.ArgumentParser()
        input_group = parser.add_mutually_exclusive_group(required=True)
        input_group.add_argument("--path")
        input_group.add_argument("--s3", action="store_true")
        input_group.add_argument("--s3-solo", metavar="S3_KEY")
        input_group.add_argument("--nomad-job", action="store_true")
        input_group.add_argument("--date", metavar="YYYY-MM-DD")
        input_group.add_argument("--range", nargs=2)
        input_group.add_argument("--analyzed", action="store_true")

        parser.add_argument("--repo")
        parser.add_argument("--index_prefix", default="redb")
        parser.add_argument("-d", "--decompile", action="store_true")
        parser.add_argument("-m", "--modules", default="all")
        parser.add_argument("--decompile-modules", default="all")
        parser.add_argument("--force", action="store_true")
        parser.add_argument("--dry-run", action="store_true")

        return parser.parse_args(args_list)

    def test_decompile_modules_default_all(self):
        """--decompile-modules defaults to 'all'."""
        args = self._parse_args(["--path", "/tmp/test", "--repo", "test"])
        assert args.decompile_modules == "all"

    def test_decompile_modules_single(self):
        """--decompile-modules accepts a single module."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "strings"
        ])
        assert args.decompile_modules == "strings"

    def test_decompile_modules_comma_separated(self):
        """--decompile-modules accepts comma-separated modules."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "cfg,llil,strings"
        ])
        assert args.decompile_modules == "cfg,llil,strings"

    def test_decompile_modules_with_decompile_flag(self):
        """--decompile-modules works with -d flag."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "disassembly"
        ])
        assert args.decompile is True
        assert args.decompile_modules == "disassembly"


# ============================================================================
# Validation Tests
# ============================================================================

class TestDecompileModulesValidation:
    """Tests for --decompile-modules validation logic in start.py."""

    def test_valid_modules_set_parsing(self):
        """Valid comma-separated modules are parsed into a set."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,llil,strings"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == set()
        assert modules_set == {"cfg", "llil", "strings"}

    def test_invalid_module_detected(self):
        """Invalid module names are detected."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,invalid_module"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == {"invalid_module"}

    def test_all_keyword_as_set(self):
        """'all' is parsed into {'all'} set."""
        raw = "all"
        if raw == "all":
            modules_set = {"all"}
        assert modules_set == {"all"}

    def test_whitespace_handling(self):
        """Whitespace around module names is stripped."""
        raw = " cfg , llil , strings "
        modules_set = {m.strip() for m in raw.split(",")}
        assert modules_set == {"cfg", "llil", "strings"}


# ============================================================================
# Ingestor Parameter Storage Tests
# ============================================================================

class TestIngestorDecompileModules:
    """Tests that Ingestor stores decompile_modules correctly."""

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_stores_decompile_modules(self, mock_datetime, mock_mp):
        """Ingestor stores the decompile_modules set."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules={"cfg", "strings"}
        )
        assert ingestor.decompile_modules == {"cfg", "strings"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor defaults decompile_modules to {'all'} when not provided."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb"
        )
        assert ingestor.decompile_modules == {"all"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_none_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor converts None decompile_modules to {'all'}."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules=None
        )
        assert ingestor.decompile_modules == {"all"}


# ============================================================================
# Function Chain Threading Tests
# ============================================================================

class TestDecompileModulesThreading:
    """Tests that decompile_modules is correctly threaded through the call chain."""

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_passes_decompile_modules(self, mock_is_binary, mock_process):
        """_process_file_internal passes decompile_modules to process_binary_file."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "strings"}

        _process_file_internal(
            "/tmp/test.bin", False, "redb", logger,
            decompile_modules=modules
        )

        mock_process.assert_called_once()
        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_default_decompile_modules_none(self, mock_is_binary, mock_process):
        """_process_file_internal defaults decompile_modules to None."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()

        _process_file_internal("/tmp/test.bin", False, "redb", logger)

        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] is None

    @patch('redb.workers._process_file_internal')
    def test_process_file_passes_decompile_modules(self, mock_internal):
        """process_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"llil", "disassembly"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()):
            process_file(
                "/tmp/test.bin", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers._process_file_internal')
    def test_process_s3_file_passes_decompile_modules(self, mock_internal):
        """process_s3_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_s3_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"cfg"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()), \
             patch('redb.workers.download_s3_object', return_value="/tmp/downloaded.bin"):
            process_s3_file(
                "bucket", "key", "/tmp", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    def test_process_zip_passes_decompile_modules(self, mock_process):
        """process_zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"strings", "decompilation"}

        import tempfile
        import zipfile

        with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
            tmp_path = tmp.name

        try:
            with zipfile.ZipFile(tmp_path, 'w') as zf:
                zf.writestr("test.bin", b"\x00" * 100)

            process_zip_file(
                tmp_path, False, "redb", logger, "all",
                decompile_modules=modules
            )

            mock_process.assert_called_once()
            call_kwargs = mock_process.call_args[1]
            assert call_kwargs["decompile_modules"] == modules
        finally:
            os.unlink(tmp_path)

    @patch('redb.workers.process_binary_file')
    def test_process_7zip_passes_decompile_modules(self, mock_process):
        """process_7zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_7zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "llil"}

        with patch('redb.workers.py7zr') as mock_py7zr:
            mock_zf = Mock()
            mock_py7zr.SevenZipFile.return_value.__enter__ = Mock(return_value=mock_zf)
            mock_py7zr.SevenZipFile.return_value.__exit__ = Mock(return_value=False)

            def fake_extractall(path):
                dest = os.path.join(path, "test.bin")
                with open(dest, "wb") as f:
                    f.write(b"\x00" * 100)

            mock_zf.extractall = fake_extractall

            process_7zip_file(
                "/tmp/test.7z", False, "redb", logger, "all",
                decompile_modules=modules
            )

            if mock_process.called:
                call_kwargs = mock_process.call_args[1]
                assert call_kwargs["decompile_modules"] == modules

    def test_worker_unpacks_decompile_modules(self):
        """worker() correctly unpacks decompile_modules from args tuple."""
        from redb.workers import worker

        modules = {"strings", "cfg"}

        with patch('redb.workers.process_file') as mock_pf, \
             patch('redb.workers.signal'), \
             patch('redb.workers.gc'):
            mock_pf.return_value = (Mock(), "pebin")

            args = (
                "/tmp/test.bin",  # filepath
                True,             # decompile
                "redb",           # index_prefix
                "/tmp/log",       # log_file
                1,                # file_number
                10,               # total_files
                "all",            # selected_modules
                False,            # dry_run
                False,            # yara_scan
                False,            # with_yara
                False,            # force
                modules,          # decompile_modules
            )

            worker(args)

            mock_pf.assert_called_once()
            call_kwargs = mock_pf.call_args[1]
            assert call_kwargs["decompile_modules"] == modules


# ============================================================================
# BinaryNinjaDecompiler._module_selected() Tests
# ============================================================================

class TestModuleSelected:
    """Tests for BinaryNinjaDecompiler._module_selected() helper."""

    def _make_decompiler(self, decompile_modules):
        """Create a minimal BinaryNinjaDecompiler-like object for testing _module_selected."""

        class FakeDecompiler:
            def __init__(self, modules):
                self.decompile_modules = modules

            def _module_selected(self, module_name):
                return "all" in self.decompile_modules or module_name in self.decompile_modules

        return FakeDecompiler(decompile_modules)

    def test_all_selects_everything(self):
        d = self._make_decompiler({"all"})
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is True
        assert d._module_selected("disassembly") is True
        assert d._module_selected("cfg") is True
        assert d._module_selected("llil") is True

    def test_specific_module_selected(self):
        d = self._make_decompiler({"cfg", "strings"})
        assert d._module_selected("cfg") is True
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is False
        assert d._module_selected("disassembly") is False
        assert d._module_selected("llil") is False

    def test_empty_set_selects_nothing(self):
        d = self._make_decompiler(set())
        assert d._module_selected("strings") is False
        assert d._module_selected("decompilation") is False

    def test_single_module(self):
        d = self._make_decompiler({"llil"})
        assert d._module_selected("llil") is True
        assert d._module_selected("cfg") is False
        assert d._module_selected("strings") is False


# ============================================================================
# analyze_binary() Selective Execution Tests
# ============================================================================

class TestAnalyzeBinaryGating:
    """Tests that analyze_binary() gates extraction per decompile_modules.

    Uses a FakeDecompiler to test the gating logic without importing binaryninja.
    """

    def _make_analyze_binary_runner(self, decompile_modules):
        """Create a test harness that simulates the gating logic of analyze_binary()."""
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        return {
            "run_strings": run_strings,
            "run_decompilation": run_decompilation,
            "run_disassembly": run_disassembly,
            "run_llil": run_llil,
            "run_cfg": run_cfg,
            "need_per_function": need_per_function,
        }

    def test_all_runs_everything(self):
        r = self._make_analyze_binary_runner({"all"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is True
        assert r["run_disassembly"] is True
        assert r["run_llil"] is True
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_strings_only_skips_per_function_loop(self):
        """Selecting only 'strings' should skip the per-function loop entirely."""
        r = self._make_analyze_binary_runner({"strings"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["run_llil"] is False
        assert r["run_cfg"] is False
        assert r["need_per_function"] is False

    def test_cfg_only_runs_cfg_and_disassembly_in_loop(self):
        """Selecting only 'cfg' should run the per-function loop but skip decompilation/llil/strings."""
        r = self._make_analyze_binary_runner({"cfg"})
        assert r["run_strings"] is False
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False  # Not selected (but backbone runs anyway in real code)
        assert r["run_llil"] is False
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_decompilation_only(self):
        r = self._make_analyze_binary_runner({"decompilation"})
        assert r["run_decompilation"] is True
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True

    def test_multiple_modules(self):
        r = self._make_analyze_binary_runner({"cfg", "llil", "strings"})
        assert r["run_strings"] is True
        assert r["run_cfg"] is True
        assert r["run_llil"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["need_per_function"] is True

    def test_disassembly_only(self):
        r = self._make_analyze_binary_runner({"disassembly"})
        assert r["run_disassembly"] is True
        assert r["run_decompilation"] is False
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True


# ============================================================================
# prepare_export_data() Table Filtering Tests
# ============================================================================

class TestPrepareExportDataFiltering:
    """Tests that prepare_export_data() only includes tables for selected modules.

    Simulates the filtering logic from DecompileBinja.prepare_export_data()
    without importing binaryninja.
    """

    def _get_export_tables(self, decompile_modules):
        """Simulate the table selection logic from prepare_export_data()."""
        run_all = "all" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules

        tables = set()
        if run_decompilation:
            tables.update(["decompiled_content", "decompiled_refs"])
        if run_disassembly:
            tables.update(["disassembled_content", "disassembled_refs", "function_similarity_metrics"])
        if run_llil:
            tables.update(["llil_content", "llil_refs"])
        if run_cfg:
            tables.update(["cfg_functions"])
        if run_strings:
            tables.update(["strings_raw"])
        # Errors always included if per-function loop ran
        if run_decompilation or run_disassembly or run_llil or run_cfg:
            tables.add("function_analysis_errors")

        return tables

    def test_all_includes_all_tables(self):
        tables = self._get_export_tables({"all"})
        assert "decompiled_content" in tables
        assert "decompiled_refs" in tables
        assert "disassembled_content" in tables
        assert "disassembled_refs" in tables
        assert "function_similarity_metrics" in tables
        assert "llil_content" in tables
        assert "llil_refs" in tables
        assert "cfg_functions" in tables
        assert "strings_raw" in tables
        assert "function_analysis_errors" in tables

    def test_strings_only_includes_only_strings(self):
        tables = self._get_export_tables({"strings"})
        assert tables == {"strings_raw"}

    def test_cfg_only_includes_cfg_and_errors(self):
        tables = self._get_export_tables({"cfg"})
        assert tables == {"cfg_functions", "function_analysis_errors"}

    def test_decompilation_only_tables(self):
        tables = self._get_export_tables({"decompilation"})
        assert tables == {"decompiled_content", "decompiled_refs", "function_analysis_errors"}

    def test_disassembly_only_tables(self):
        tables = self._get_export_tables({"disassembly"})
        assert tables == {
            "disassembled_content", "disassembled_refs",
            "function_similarity_metrics", "function_analysis_errors"
        }

    def test_llil_only_tables(self):
        tables = self._get_export_tables({"llil"})
        assert tables == {"llil_content", "llil_refs", "function_analysis_errors"}

    def test_combined_cfg_strings_tables(self):
        tables = self._get_export_tables({"cfg", "strings"})
        assert tables == {"cfg_functions", "strings_raw", "function_analysis_errors"}

    def test_no_errors_table_for_strings_only(self):
        """Error table should NOT be included when only strings is selected (no per-function loop)."""
        tables = self._get_export_tables({"strings"})
        assert "function_analysis_errors" not in tables


# ============================================================================
# DecompileBinja Parameter Acceptance Tests
# ============================================================================

class TestDecompileBinjaDecompileModules:
    """Tests that DecompileBinja accepts and stores decompile_modules.

    Since DecompileBinja imports binaryninja (which requires blake3 etc),
    we test the default/init logic in isolation.
    """

    def test_default_decompile_modules(self):
        """decompile_modules defaults to {'all'} when None."""
        modules = None
        result = modules or {"all"}
        assert result == {"all"}

    def test_explicit_decompile_modules(self):
        """Explicit decompile_modules is preserved."""
        modules = {"cfg", "llil"}
        result = modules or {"all"}
        assert result == {"cfg", "llil"}

    def test_empty_set_becomes_all(self):
        """Empty set is falsy and defaults to {'all'}."""
        modules = set()
        result = modules or {"all"}
        assert result == {"all"}


# ============================================================================
# Integration-Style: analyze_binary() Per-Function Gating (Logic Tests)
# ============================================================================

class TestAnalyzeBinaryPerFunctionGating:
    """Test that the per-function extraction gating produces correct results structures.

    These test the logic patterns from analyze_binary() using mock data,
    verifying that:
    - Unselected modules produce empty result lists
    - Selected modules produce populated result lists
    - Disassembly always runs when any per-function module is selected
    - Cross-linkage None handling works when counterparts are skipped
    """

    def _simulate_per_function_results(self, decompile_modules, num_functions=3):
        """Simulate the per-function extraction loop from analyze_binary().

        Returns results dict with the same structure as analyze_binary().
        """
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        results = {
            "decompiled": [],
            "disassembled": [],
            "cfg": [],
            "llil": [],
            "errors": [],
            "strings": [],
        }

        if run_strings:
            results["strings"] = [{"string": f"str_{i}"} for i in range(5)]

        if not need_per_function:
            return results

        for i in range(num_functions):
            hlil_json = {"decompiled_function_hash": f"hlil_{i}"} if run_decompilation else None
            disass_json = {"disassembled_function_hash": f"disass_{i}", "disassembled_function_name": f"func_{i}", "disassembled_function_address": i * 100}  # always
            cfg_json = {"function_address": i * 100, "cyclomatic_complexity": 5} if run_cfg else None
            lowlevel_json = {"sha256_llil": f"llil_{i}", "tlsh_llil": "def", "minhash": [1, 2]} if run_llil else None

            # Simulate cross-linkage (same pattern as real code)
            if hlil_json and disass_json:
                hlil_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["decompiled_function_hash"] = hlil_json["decompiled_function_hash"]
                results["decompiled"].append(hlil_json)
                if run_disassembly:
                    results["disassembled"].append(disass_json)
            elif hlil_json:
                hlil_json["disassembled_function_hash"] = None
                results["decompiled"].append(hlil_json)
            elif disass_json:
                disass_json["decompiled_function_hash"] = None
                if run_disassembly:
                    results["disassembled"].append(disass_json)

            if lowlevel_json and disass_json:
                lowlevel_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["tlsh_llil"] = lowlevel_json.get("tlsh_llil")
                disass_json["minhash"] = lowlevel_json.get("minhash")
            elif lowlevel_json:
                lowlevel_json["disassembled_function_hash"] = None
            elif disass_json:
                disass_json["tlsh_llil"] = None
                disass_json["minhash"] = None

            if lowlevel_json:
                results["llil"].append(lowlevel_json)

            if cfg_json and disass_json:
                cfg_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["cyclomatic_complexity"] = cfg_json.get("cyclomatic_complexity")
                results["cfg"].append(cfg_json)
            elif cfg_json:
                cfg_json["disassembled_function_hash"] = None
                results["cfg"].append(cfg_json)

            if disass_json and "cyclomatic_complexity" not in disass_json:
                disass_json["cyclomatic_complexity"] = None

        return results

    def test_all_populates_everything(self):
        r = self._simulate_per_function_results({"all"}, 3)
        assert len(r["decompiled"]) == 3
        assert len(r["disassembled"]) == 3
        assert len(r["cfg"]) == 3
        assert len(r["llil"]) == 3
        assert len(r["strings"]) == 5

    def test_strings_only_populates_strings(self):
        r = self._simulate_per_function_results({"strings"}, 3)
        assert len(r["strings"]) == 5
        assert len(r["decompiled"]) == 0
        assert len(r["disassembled"]) == 0
        assert len(r["cfg"]) == 0
        assert len(r["llil"]) == 0

    def test_cfg_only_populates_cfg_no_disassembly_export(self):
        """With only 'cfg' selected, cfg list is populated but disassembled is empty
        (disassembly runs internally but not exported)."""
        r = self._simulate_per_function_results({"cfg"}, 3)
        assert len(r["cfg"]) == 3
        assert len(r["disassembled"]) == 0  # Not selected for export
        assert len(r["decompiled"]) == 0
        assert len(r["llil"]) == 0
        assert len(r["strings"]) == 0

    def test_cfg_has_disassembled_function_hash_linkage(self):
        """CFG entries get disassembled_function_hash from backbone disassembly."""
        r = self._simulate_per_function_results({"cfg"}, 2)
        for cfg in r["cfg"]:
            assert cfg["disassembled_function_hash"] is not None
            assert cfg["disassembled_function_hash"].startswith("disass_")

    def test_decompilation_only_has_none_disassembly_linkage_when_no_disasm(self):
        """When only decompilation selected, disassembled still runs (backbone)
        so linkage is still available."""
        r = self._simulate_per_function_results({"decompilation"}, 2)
        assert len(r["decompiled"]) == 2
        # Disassembly runs as backbone, so linkage hash is present
        for d in r["decompiled"]:
            assert d["disassembled_function_hash"] is not None

    def test_disassembly_without_llil_has_none_fuzzy_hashes(self):
        """When LLIL is not selected, disassembly records have None fuzzy hashes."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["tlsh_llil"] is None
            assert d["minhash"] is None

    def test_disassembly_without_decompilation_has_none_decompiled_hash(self):
        """When decompilation is not selected, disassembly has None decompiled_function_hash."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is None

    def test_disassembly_without_cfg_has_none_cyclomatic(self):
        """When CFG is not selected, disassembly has None cyclomatic_complexity."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["cyclomatic_complexity"] is None

    def test_all_linkage_populated(self):
        """When all modules run, all cross-linkage fields are populated."""
        r = self._simulate_per_function_results({"all"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is not None
            assert d["tlsh_llil"] is not None
            assert d["cyclomatic_complexity"] is not None
        for h in r["decompiled"]:
            assert h["disassembled_function_hash"] is not None
        for c in r["cfg"]:
            assert c["disassembled_function_hash"] is not None
        for ll in r["llil"]:
            assert ll["disassembled_function_hash"] is not None


# ============================================================================
# IOC Extraction Gating by decompile_modules
# ============================================================================

class TestIOCExtractionGating:
    """Tests that IOC extraction only runs when its data sources are present.

    IOCExtractorFromResults consumes in-memory analysis_results['strings']
    and analysis_results['decompiled']. It runs automatically (implicit
    dependency) when 'decompilation' or 'strings' modules produce data.
    It is NOT a standalone selectable module.
    """

    @staticmethod
    def _is_ioc_relevant(decompile_modules):
        """Replicate the gating logic from workers.py."""
        return (
            not decompile_modules
            or "all" in decompile_modules
            or "decompilation" in decompile_modules
            or "strings" in decompile_modules
        )

    def test_all_triggers_ioc(self):
        assert self._is_ioc_relevant({"all"}) is True

    def test_decompilation_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation"}) is True

    def test_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"strings"}) is True

    def test_decompilation_and_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation", "strings"}) is True

    def test_cfg_only_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg"}) is False

    def test_disassembly_only_skips_ioc(self):
        assert self._is_ioc_relevant({"disassembly"}) is False

    def test_llil_only_skips_ioc(self):
        assert self._is_ioc_relevant({"llil"}) is False

    def test_cfg_and_disassembly_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "disassembly"}) is False

    def test_cfg_and_llil_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "llil"}) is False

    def test_cfg_and_decompilation_triggers_ioc(self):
        """If decompilation is included alongside cfg, IOC should run."""
        assert self._is_ioc_relevant({"cfg", "decompilation"}) is True

    def test_disassembly_and_strings_triggers_ioc(self):
        """If strings is included alongside disassembly, IOC should run."""
        assert self._is_ioc_relevant({"disassembly", "strings"}) is True

    def test_none_modules_triggers_ioc(self):
        """When decompile_modules is None (default), IOC should run."""
        assert self._is_ioc_relevant(None) is True

    def test_empty_set_triggers_ioc(self):
        """When decompile_modules is empty set, IOC should run (falsy)."""
        assert self._is_ioc_relevant(set()) is True