Han Jiang

88 papers A* 4A 3B 7C 2Misc 1Journal 45Unranked 26
YearRankTypeTitle / Venue / Authors
2026 J jnl
Comput. Phys. Commun.
Shimin Guo, Han Jiang, Pengrui Zhou, Liquan Mei
2026 J jnl
CoRR
Negar Halakou, Juan F. Gutierrez, Ye Sun, Han Jiang, Xueming Wu, Yilun Song, Andres Gomez
2026 J jnl
Comput. Educ.
Jingjing Zhang, Han Jiang, Yicheng Huang, Ning Ma
2026 conf
CNML
Han Jiang, Xuanqing Geng, Jiawei Wang, Yukai Fu, Xianshuang Li, Yujie Zhang, Chengang Fu, Jiahao Bi, Tangyao Xie, Xiaolong Pan, Xinying Li, Xiangjun Xin
2026 J jnl
Comput. Geosci.
Samuel C. Boone, Ling Chung, Noel Faux, Usha Nattala, Thomas Church, Chenghao Jiang, Malcolm McMillan, Sean Jones, David Liu, Han Jiang, Krista A. Ehinger, Tom Drummond, Barry Kohn, Andrew Gleadow
2026 J jnl
CoRR
Han Jiang, Yao Xiao, Rachel Hurley, Shichao Liu
2026 conf
CHI Extended Abstracts
Willem van der Maden, Wesley Hanwen Deng, Yu Lu Liu, Han Jiang, Valerie Chen, Haotian Li, Juho Kim, Q. Vera Liao, Wei Xu, Motahhare Eslami, Ziang Xiao
2025 J jnl
Comput. Networks
Shi Wang, Dayan Cao, Xiaoying Zhu, Han Jiang, Mingyu Wang
2025 J jnl
IEEE Robotics Autom. Lett.
Qiqi Zhou, Hui Ding, Teng Chen, Luxin Man, Han Jiang, Guoteng Zhang, Bin Li, Xuewen Rong, Yibin Li
2025 B conf
ICTAI
Yawen Cao, Le Yin, Han Jiang, Zihan Xu
2025 conf
VTC2025-Fall
Berihu B. Weldemichael, Han Jiang, Yetneberk Zenebe Melesew, Ahmad Y. Alhusenat, Lei Lei
2025 J jnl
IEEE Trans. Multim.
Han Jiang, Chaofan Chen, Xiaoshan Yang, Changsheng Xu
2025 J jnl
J. Comput. Methods Sci. Eng.
Han Jiang
2025 conf
BIBM
Wei Zhang, Yu Lu, Han Jiang, Xiujuan Zhao, Qianying Liu, Bingding Huang, Zhaoshun Zhang, Zhicheng Dong, Liyilei Su
2025 J jnl
Virtual Real. Intell. Hardw.
Weimin Shi, Yuan Xiong, Qianwen Wang, Han Jiang, Zhong Zhou
2025 conf
ROBIO
Deran Kong, Yong Fan, Ronghua Zhao, Han Jiang, Dayu Liu
2025 conf
ICIG (1)
Shaobo Xie, Han Jiang, Chenlin Zhao, Xiaoshan Yang
2025 J jnl
CoRR
Yifei Xia, Shuchen Weng, Siqi Yang, Jingqi Liu, Chengxuan Zhu, Minggui Teng, Zijian Jia, Han Jiang, Boxin Shi
2025 J jnl
Comput. Methods Programs Biomed.
Ang Li, Junqing Lin, Lili Lin, Jianqiang Ye, Zhongyou Ji, Han Jiang
2025 J jnl
IEEE Trans. Multim.
Han Jiang, Xiaoshan Yang, Chaofan Chen, Changsheng Xu
2025 conf
ICSCA
Zhipeng Yu, Haonan Qin, Han Jiang, Pengfei Zhang
2025 conf
MICCAI (3)
Yaxuan Li, Han Jiang, Yifei Ma, Shihua Qin, Jonghye Woo, Fangxu Xing
2025 J jnl
CoRR
Yaxuan Li, Han Jiang, Yifei Ma, Shihua Qin, Fangxu Xing
2025 A* conf
CVPR
Shuchen Weng, Haojie Zheng, Peixuan Zhang, Yuchen Hong, Han Jiang, Si Li, Boxin Shi
2025 J jnl
IEEE Trans. Veh. Technol.
Han Jiang, Jifa Zhang, Mingqian Liu, Nan Zhao, Arumugam Nallanathan, Yonghui Li
2024 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Junwen Duan, Huai Guo, Han Jiang, Fei Guo, Jian-Xin Wang
2024 J jnl
CoRR
Shu Yang, Jiayuan Su, Han Jiang, Mengdi Li, Keyuan Cheng, Muhammad Asif Ali, Lijie Hu, Di Wang
2024 J jnl
IEEE Trans. Geosci. Remote. Sens.
Gang Zheng, Yinfei Zhou, Bin Liu, Lizhang Zhou, Han Jiang, Xuanwei Wan, Peng Chen
2024 J jnl
CoRR
Han Jiang, Haosen Sun, Ruoxuan Li, Chi-Keung Tang, Yu-Wing Tai
2024 conf
ICIC (3)
Xue Kang, Yan-Ni Han, Wen Zhang, Han Jiang, Yi Xiang, Shu-Guang Liu
2024 conf
CASE
Shufei Li, Zuoxu Wang, Zhijie Yan, Yiping Gao, Han Jiang, Pai Zheng
2024 B conf
ICCCN
Jiang Han, Hequn Xian, Dalin Yang, Han Jiang
2024 A* conf
EMNLP
Han Jiang, Junwen Duan, Zhe Qu, Jianxin Wang
2024 J jnl
CoRR
Han Jiang, Junwen Duan, Zhe Qu, Jianxin Wang
2024 B conf
IJCNN
Xingfu Li, Bangchao Wang, Hongyan Wan, Yuanbang Li, Han Jiang, Yang Deng, Zhiyuan Zou
2024 J jnl
BMC Medical Imaging
Han Jiang, Ziqiang Li, Nan Meng, Yu Luo, Pengyang Feng, Fangfang Fu, Yang Yang, Jianmin Yuan, Zhe Wang, Meiyun Wang
2024 J jnl
J. Control. Decis.
Rongsheng Xia, Han Jiang, Lei Su
2024 J jnl
J. Comput. Phys.
Han Jiang, Craig A. Schroeder
2024 conf
DSC
Shuang Zhou, Zhigang Liu, Jian Liu, Han Jiang
2024 A conf
IROS
Han Jiang, Yanchun Chang, Liying Yang, Yuqing He
2024 conf
ICXR
Han Jiang, Ming Li, Yang Gao, Peiru Li
2024 J jnl
CoRR
Shuyue Wang, Wenjun Zhou, Han Jiang, Shuo Wang, Ren Zheng
2024 J jnl
Kybernetes
Wentao Zhan, Minghui Jiang, Xueping Wang, Da Huo, Han Jiang
2024 J jnl
CoRR
Shuchen Weng, Haojie Zheng, Peixuan Zhang, Yuchen Hong, Han Jiang, Si Li, Boxin Shi
2023 conf
ICIRA (1)
Jikang Zhong, Peng Li, Xu Liu, Pinhui Zhao, Han Jiang, Liying Yang, Decai Li, Chunguang Bu, Yuqing He
2023 J jnl
IEEE Trans. Cloud Comput.
Ting Yang, Han Jiang, Yucheng Hou, Yinan Geng
2023 J jnl
IEEE Robotics Autom. Lett.
Han Jiang, Yanchun Chang, Liying Yang, Xu Liu, Yuqing He
2023 conf
CLEF (Working Notes)
Huan Ren, Han Jiang, Wang Luo, Meng Meng, Tianzhu Zhang
2023 J jnl
Remote. Sens.
Wenzhao Li, Han Jiang, Dongfeng Li, Philip B. Bedient, Zheng N. Fang
2023 A conf
ICME
Han Jiang, Xiaoshan Yang, Chaofan Chen, Changsheng Xu
2023 A conf
BMVC
Ke Wu, Han Jiang, Li Kuang, Yixuan Wang, Huaiqian Ye, Yuanbo He
2023 Misc conf
ICASSP
Junwen Duan, Han Jiang, Ying Yu
2023 J jnl
CoRR
Junwen Duan, Han Jiang, Ying Yu
2023 J jnl
CoRR
Han Jiang, Ruoxuan Li, Haosen Sun, Yu-Wing Tai, Chi-Keung Tang
2023 J jnl
Virtual Real. Intell. Hardw.
Han Jiang, Wenjia Sun, Hanfei Guo, Jiayuan Zeng, Xin Xue, Shuai Li
2023 J jnl
CoRR
Ran Shen, Gang Sun, Hao Shen, Yiling Li, Liangfeng Jin, Han Jiang
2023 J jnl
Robotics Auton. Syst.
Han Jiang, Teng Chen, Jingxuan Cao, Jian Bi, Guanglin Lu, Guoteng Zhang, Xuewen Rong, Yibin Li
2023 J jnl
J. Field Robotics
Guanglin Lu, Teng Chen, Xuewen Rong, Guoteng Zhang, Jian Bi, Jingxuan Cao, Han Jiang, Yibin Li
2023 J jnl
CoRR
Han Jiang, Junwen Duan, Zhe Qu, Jianxin Wang
2022 conf
ISBRA
Ying Yu, Junwen Duan, Han Jiang, Jian-Xin Wang
2022 conf
ROBIO
Han Jiang, Teng Chen, Jingxuan Cao, Jian Bi, Guanglin Lu, Guoteng Zhang, Xuewen Rong, Yibin Li
2022 J jnl
Syst.
Wentao Zhan, Xue-Ping Wang, Ming-Hui Jiang, Han Jiang, Da Huo, Yun-Tao Liu
2021 conf
ICAC
Wanting Wang, Yuanping Xu, Zhijie Xu, Chaolong Zhang, Tukun Li, Jie Wang, Han Jiang
2021 J jnl
Remote. Sens.
Han Jiang, Yueting Zhang, Jiayi Guo, Fangfang Li, Yuxin Hu, Bin Lei, Chibiao Ding
2021 conf
ChineseCSCW (2)
Lei Wan, Bin Dai, Han Jiang, Weixian Luan, Fan Ye, Xianjun Zhu
2021 J jnl
Neuroinformatics
Hua Zhang, Jiajie Mo, Han Jiang, Zhuyun Li, Wenhan Hu, Chao Zhang, Yao Wang, Xiu Wang, Chang Liu, Baotian Zhao, Jianguo Zhang, Kai Zhang
2021 B conf
IWCMC
Jun Zhou, Han Jiang
2020 conf
ISBI
Hexin Dong, Fei Yu, Han Jiang, Hua Zhang, Bin Dong, Quanzheng Li, Li Zhang
2020 conf
ACAI
Chuchu Yu, Xinmei Yang, Han Jiang
2020 conf
MICCAI (4)
Qi Liu, Han Jiang, Tao Liu, Zihao Liu, Sicheng Li, Wujie Wen, Yiyu Shi
2020 C conf
ISCAS
Haonan Zhang, Han Jiang, Shiyuan Wang
2020 J jnl
Neurocomputing
Han Jiang, Yanrong Guo
2020 J jnl
J. Intell. Fuzzy Syst.
Junyan Shi, Han Jiang
2020 B conf
IWCMC
Lin Ma, Han Jiang, Danyang Qin, Xuezhi Tan
2019 J jnl
IEICE Trans. Inf. Syst.
Wei Bai, Yuli Zhang, Meng Wang, Jin Chen, Han Jiang, Zhan Gao, Donglin Jiao
2019 J jnl
Comput. Geosci.
Han Jiang, Hugh Daigle, Xiao Tian, Michael J. Pyrcz, Chris Griffith, Boyang Zhang
2019 B conf
ICPADS
Hao Ji, Ziyang He, Zhouze He, Han Jiang, Shaotang Cai, Ting Yang
2019 C conf
ICIS
Jiahao Cai, Xin Feng, Han Jiang, Minyong Shi
2018 conf
ICGSP
Han Jiang, Xinmei Yang, Yaobin Li
2017 J jnl
计算机科学
Shuxian Wen, Shaowen Li, Xiu Jin, Liu Zhao, Han Jiang
2015 conf
ICIA
Wei Zhuang, Han Jiang, Chengju Liu, Shu-tong He
2014 J jnl
Comput. Math. Methods Medicine
Yin Wang, Han Jiang
2014 conf
CCIS
Xu Han, Han Jiang, Yanfeng Jiang, Heng Gu, Siyue Zhang, Tao Lv, Cong Wang
2014 A* conf
KDD
Wayne Xin Zhao, Yanwei Guo, Yulan He, Han Jiang, Yuexin Wu, Xiaoming Li
2013 B conf
IJCNLP
Rui Yan, Han Jiang, Mirella Lapata, Shou-De Lin, Xueqiang Lv, Xiaoming Li
2013 A* conf
IJCAI
Rui Yan, Han Jiang, Mirella Lapata, Shou-De Lin, Xueqiang Lv, Xiaoming Li
2011 conf
CHINACOM
Han Jiang, Minjian Zhao, Jie Zhong, Yunlong Cai, Hao Wu
2011 conf
WAIM
Liang Kong, Rui Yan, Han Jiang, Yan Zhang, Yan Gao, Li Fu
start.py
← Index start.py python
"""
# By repository (existing behavior, now uses repository_upload_sessions)
python start.py --s3 --repo bazaar --index_prefix redb

# By repository with notes filter
python start.py --s3 --repo vx-itw --s3-notes "ITW.0138" --index_prefix redb

# By single date (all repo samples first seen on Jan 15, 2025)
python start.py --date 2025-01-15 --index_prefix redb

# By date with repository filter
python start.py --date 2025-01-15 --repo bazaar --index_prefix redb

# By date range (inclusive)
python start.py --range 2025-01-01 2025-01-31 --index_prefix redb

# By date range with repository and notes filters
python start.py --range 2025-01-01 2025-01-31 --repo malshare --s3-notes "batch1" --index_prefix redb

# By filetype (magika) standalone - process all ELF samples across all repos
python start.py --s3 --magika elf --index_prefix redb

# By filetype with repository filter
python start.py --s3 --repo bazaar --magika elf --index_prefix redb

# By filetype with date range - process only PE samples in date range
python start.py --range 2025-01-01 2025-01-31 --magika pebin --index_prefix redb
"""

import argparse
import os
import sys
from datetime import datetime, timedelta
from redb.ingestor import *

"""
        # General modules
        'BasicPropertiesExtractor': BasicPropertiesExtractor,
        'HashExtractor': HashExtractor,
        'DIEExtractor': DIEExtractor,
        'CAPAExtractor': CAPAExtractor,
        'StringsExtractor': StringsExtractor,
        # PE modules
        'PEFeaturesExtractor': PEFeaturesExtractor,
        'PEImportExtractor': PEImportExtractor,
        'PEResourceExtractor': PEResourceExtractor,
        'PEOverlayExtractor': PEOverlayExtractor,
        'PESectionExtractor': PESectionExtractor,
        'PESignatureExtractor': PESignatureExtractor,
        'PEExtraFindings': PEExtraFindings,
        'PEInconstistencyTestsExtractor': PEInconstistencyTestsExtractor,
        'PEDotNetExtractor': PEDotNetExtractor,
"""


def main():
    # # path = "/mnt/samples/consilience/malware/test/test-unzipped/0242d90dc48a8931bad72ddbdba34bdd568fd30610dfe049c84968d425088c71/"
    # path = "/Users/p4c0/_samples/test-unzipped/0242d90dc48a8931bad72ddbdba34bdd568fd30610dfe049c84968d425088c71" #Stuxnet
    # # path = "/Volumes/backup/consilience/malware/test/test-unzipped/rhpv-673f91a2085358e3266f466845366f30cf741060edeb31e9a93e2c92033bba28"
    # # path = "/mnt/samples/consilience/malware/test/test-redb/"
    # # path = "/mnt/samples/consilience/malware/malpedia-pe/9bc81280113473de9ebfe54f689b4440287c37fff562e070d3a28f5269cadcf0_dump7_0x00400000"
    # # path = "/mnt/samples/consilience/malware/test/test-unzipped/379251974ebcd5c397f92ca45bb9620d"
    # path = "0242d90dc48a8931bad72ddbdba34bdd568fd30610dfe049c84968d425088c71" # rich header, UPX packer
    # # #path = "d8637bdbcfc9112fcb1f0167b398e771" #dotnet
    # path = "/Users/p4c0/_samples/test-unzipped/sig-8e035beb02a411f8a9e92d4cf184ad34f52bbd0a81a50c222cdd4706e4e45104" #code signed, protector use case for sections
    # path = "/Users/p4c0/_samples/test-unzipped/vsinfo-39d8ad95b0323c37bd3134ab93ac4af44c66a1a8443a41c1ac02cec19bb2816a"
    # # path = "/Volumes/backup/consilience/malware/_sorted_samples/vx-apt/pebin/69e679daaaff3832c39671bf2b813b5530a70fb763d381f9a6e22e3bc493c8a9.7z"
    # # path = "test_files/hello"
    # path = "/Volumes/backup/consilience/malware/test/test-re2db/"
    # # path = "/Users/p4c0/_samples/HEUR-Trojan-PSW.MSIL.Maslog.gen-0c9ae5cd740c1da7060b92ddb33f3a3893e361aad45a2accc64d43bd9a1a4106"
    # # path = "/Users/p4c0/_samples/test-unzipped/"
    # # # path = "/Volumes/backup/consilience/malware/_sorted_samples/vx-apt/pebin/7156bd8056c4b6b4e179a64370067d3f7a7cce0044f1352d41f3c2c73038d273.7z"
    # decompile = False
    # repo = "test-fixing"
    # index_prefix = "test4"
    # selected_modules = "all"
    # exporter_types = ['ClickHouse']

    # parser = argparse.ArgumentParser(
    #     description="Process binary files in a given path."
    # )
    # parser.add_argument("path", 
    #     help="File path, directory path, or path to a .txt file containing a list of files to process (one per line)")
    # parser.add_argument(
    #     "--repo", help="Repository name for sample source, used for loggfile name"
    # )
    # parser.add_argument("--index_prefix", help="Index prefix for ElasticSearch")
    # parser.add_argument(
    #     "-d",
    #     "--decompile",
    #     action="store_true",
    #     help="Optional flag, if set it will run ONLY the decompiler on the binary files",
    # )
    # parser.add_argument(
    #     "-m",
    #     "--modules",
    #     help="Comma-separated list of modules to run (e.g., 'BasicPropertiesExtractor,HashExtractor') or 'all' for all modules",
    #     default="all",
    # )
    # args = parser.parse_args()
    # path = args.path
    # index_prefix = args.index_prefix
    # decompile = args.decompile
    # repo = args.repo
    # selected_modules = args.modules

    # print(f"Decompile flag: {decompile}")
    # print(f"Repo: {repo}")
    # print(f"Selected modules: {selected_modules}")
    
    # if path.endswith('.txt'):
    #     print(f"Reading file list from: {path}")

    # Ingestor(path, decompile, repo, index_prefix, selected_modules).ingest()

    parser = argparse.ArgumentParser(
        description="Process binary files from local paths or S3 storage."
    )
    
    # Create a mutually exclusive group for input sources
    # Not required because --analyzed can be used standalone
    input_group = parser.add_mutually_exclusive_group(required=False)
    input_group.add_argument(
        "--path", 
        help="File path, directory path, or path to a .txt file containing a list of files to process (one per line)")
    input_group.add_argument(
        "--s3",
        action="store_true",
        help="Use S3 mode to fetch files from repository specified by --repo")
    input_group.add_argument(
        "--s3-solo",
        metavar="S3_KEY",
        help="Process a single S3 file by providing the S3 key (e.g., 09/f7/09f7d02a....zip)")
    input_group.add_argument(
        "--nomad-job",
        action="store_true",
        help="Run as Nomad job using environment variables for job parameters")
    input_group.add_argument(
        "--date",
        metavar="YYYY-MM-DD",
        help="Process samples first seen on a specific date (from repository_upload_sessions only)")
    input_group.add_argument(
        "--range",
        nargs=2,
        metavar=("START_DATE", "END_DATE"),
        help="Process samples first seen in a date range (inclusive, from repository_upload_sessions only). Format: YYYY-MM-DD YYYY-MM-DD")
    parser.add_argument(
        "--analyzed",
        action="store_true",
        help="Filter to samples already in the database (from basic_properties). "
             "Can be used standalone or combined with --range/--date to partition large runs")

    parser.add_argument(
        "--repo",
        required=False,
        help="Repository name for sample source, used for logging and S3 filtering (optional for --date/--range modes)")
    
    parser.add_argument(
        "--s3-notes",
        help="Optional filter for S3 files based on notes field (S3 mode only)")

    parser.add_argument(
        "--magika",
        help="Filter by filetype_magika (e.g., 'elf', 'pebin'). Overrides SUPPORTED_FORMATS env var. Can combine with --repo, --date, --range")

    parser.add_argument(
        "--index_prefix", default="redb",
        help="Index prefix for database (default: redb)")
    
    parser.add_argument(
        "-d",
        "--decompile",
        action="store_true",
        help="Optional flag, if set it will run ONLY the decompiler on the binary files",
    )

    parser.add_argument(
        "-y",
        "--yara",
        action="store_true",
        help="Optional flag, if set it will run ONLY the YARA scanner on the binary files",
    )

    parser.add_argument(
        "--with-yara",
        action="store_true",
        help="Add YARA scanning to feature extraction (runs both features and YARA)",
    )

    parser.add_argument(
        "-m",
        "--modules",
        help="Comma-separated list of modules to run (e.g., 'BasicPropertiesExtractor,HashExtractor') or 'all' for all modules",
        default="all",
    )

    parser.add_argument(
        "--decompile-modules",
        help="Comma-separated list of decompiler sub-modules to run when using -d/--decompile. "
             "Available: decompilation, disassembly, cfg, llil, strings, or 'all' (default: all)",
        default="all",
    )
    
    parser.add_argument(
        "--force",
        action="store_true",
        help="Force reprocessing of samples already in the database (bypasses deduplication check)",
    )

    parser.add_argument(
        "--rerun",
        action="store_true",
        help="Re-run decompiler modules on already-disassembled samples only. "
             "Queries code_binja_disassembled_functions_references instead of basic_properties. "
             "Requires --analyzed and --decompile.",
    )

    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print results instead of uploading to database (useful for testing)",
    )
    
    args = parser.parse_args()

    # Extract arguments
    path = args.path
    index_prefix = args.index_prefix
    decompile = args.decompile
    yara_scan = args.yara
    with_yara = args.with_yara
    repo = args.repo
    selected_modules = args.modules
    decompile_modules = args.decompile_modules
    s3_mode = args.s3
    s3_notes = args.s3_notes
    magika_filter = args.magika
    dry_run = args.dry_run
    force = args.force
    rerun = args.rerun
    s3_solo = args.s3_solo
    s3_key = args.s3_solo if args.s3_solo else None
    nomad_job = args.nomad_job
    analyzed = args.analyzed
    date_filter = args.date
    date_range = args.range

    # Validate that at least one input source is provided
    has_input = any([path, s3_mode, s3_solo, nomad_job, date_filter, date_range, analyzed])
    if not has_input:
        print("ERROR: Must specify an input source: --path, --s3, --s3-solo, --nomad-job, --date, --range, or --analyzed")
        sys.exit(1)

    # Validate --analyzed combinations
    if analyzed and any([path, s3_mode, s3_solo, nomad_job]):
        print("ERROR: --analyzed cannot be combined with --path, --s3, --s3-solo, or --nomad-job")
        sys.exit(1)

    # Validate flag combinations
    if yara_scan and with_yara:
        print("ERROR: Cannot use both --yara and --with-yara")
        sys.exit(1)
    if decompile and with_yara:
        print("ERROR: --with-yara only works with feature extraction, not decompile")
        sys.exit(1)

    # Parse and validate --decompile-modules
    VALID_DECOMPILE_MODULES = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
    if decompile_modules == "all":
        decompile_modules_set = {"all"}
    else:
        decompile_modules_set = {m.strip() for m in decompile_modules.split(",")}
        invalid = decompile_modules_set - VALID_DECOMPILE_MODULES
        if invalid:
            print(f"ERROR: Invalid decompile module(s): {', '.join(sorted(invalid))}")
            print(f"Available: {', '.join(sorted(VALID_DECOMPILE_MODULES - {'all'}))}")
            sys.exit(1)

    if not decompile and decompile_modules != "all":
        print("ERROR: --decompile-modules requires -d/--decompile flag")
        sys.exit(1)

    if rerun and not analyzed:
        print("ERROR: --rerun requires --analyzed flag")
        sys.exit(1)
    if rerun and not decompile:
        print("ERROR: --rerun requires -d/--decompile flag")
        sys.exit(1)

    if rerun and force:
        print("ERROR: --rerun and --force are mutually exclusive. "
              "--rerun targets already-disassembled samples, --force targets all analyzed samples.")
        sys.exit(1)

    # Validate and parse date arguments
    start_date = None
    end_date = None

    if date_filter:
        # Single date mode: process samples from that day
        try:
            parsed_date = datetime.strptime(date_filter, "%Y-%m-%d")
            start_date = date_filter
            # End date is the next day (exclusive)
            end_date = (parsed_date + timedelta(days=1)).strftime("%Y-%m-%d")
        except ValueError:
            print(f"ERROR: Invalid date format '{date_filter}'. Use YYYY-MM-DD")
            sys.exit(1)

    if date_range:
        # Date range mode: process samples between start and end dates
        try:
            start_date = date_range[0]
            datetime.strptime(start_date, "%Y-%m-%d")  # Validate format
            parsed_end = datetime.strptime(date_range[1], "%Y-%m-%d")
            # End date is the day after the provided end date (to make it inclusive)
            end_date = (parsed_end + timedelta(days=1)).strftime("%Y-%m-%d")
        except ValueError:
            print(f"ERROR: Invalid date format in range '{date_range}'. Use YYYY-MM-DD YYYY-MM-DD")
            sys.exit(1)

    # Validate required parameters based on mode
    if nomad_job or s3_solo:
        # For nomad job and s3-solo modes, set default repo if not provided (used for log filename)
        if not repo:
            repo = "s3-solo" if s3_solo else "nomad-worker"
    elif analyzed:
        # For analyzed mode, repo is optional (used for logging only)
        if not repo:
            repo = "analyzed"
    elif date_filter or date_range:
        # For date/range modes, repo is optional (used for filtering within date range)
        # Set a default repo name for logging if not provided
        if not repo:
            repo = "date-range"
    elif s3_mode and magika_filter and not repo:
        # For S3 mode with magika filter, repo is optional (query all repos for that filetype)
        repo = "all-repos"
    elif not s3_mode and path:
        # For local mode, repo is required
        if not repo:
            print("ERROR: --repo is required for local mode")
            sys.exit(1)
    elif s3_mode and not repo:
        # For S3 mode without magika filter, repo is required
        print("ERROR: --repo is required for S3 catalog mode (or use --magika to query all repos)")
        sys.exit(1)

    print(f"Decompile flag: {decompile}")
    if decompile and decompile_modules != "all":
        print(f"Decompile modules: {', '.join(sorted(decompile_modules_set))}")
    print(f"YARA scan flag: {yara_scan}")
    print(f"With YARA flag: {with_yara}")
    print(f"Repo: {repo}")
    print(f"Selected modules: {selected_modules}")
    print(f"Dry run mode: {dry_run}")
    print(f"Force reprocessing: {force}")
    print(f"S3 solo mode: {s3_solo}")
    print(f"Nomad job mode: {nomad_job}")
    if magika_filter:
        print(f"Magika filter: {magika_filter}")
    if start_date:
        print(f"Date filter: {start_date} to {end_date}")
    if rerun:
        print(f"Rerun mode: targeting already-disassembled samples from code_binja_disassembled_functions_references")
    if analyzed:
        print(f"Analyzed mode: processing already-analyzed samples from basic_properties")

    if analyzed and not (date_filter or date_range):
        # Analyzed mode (standalone): process samples already in basic_properties via S3
        print(f"Processing already-analyzed samples from {index_prefix}_basic_properties")
        if magika_filter:
            print(f"Filetype filter: {magika_filter}")
        Ingestor(
            path=None,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo or "analyzed",
            index_prefix=index_prefix,
            selected_modules=selected_modules,
            s3_mode=True,
            magika_filter=magika_filter,
            dry_run=dry_run,
            force=force,
            analyzed=True,
            decompile_modules=decompile_modules_set,
            rerun=rerun,
        ).ingest()

    elif date_filter or date_range:
        # Date-based S3 mode
        print(f"Date-based S3 mode enabled")
        if analyzed:
            print(f"Filtered to already-analyzed samples in {index_prefix}_basic_properties")
        if repo and repo != "date-range":
            print(f"Repository filter: {repo}")
        if s3_notes:
            print(f"Notes filter: {s3_notes}")
        Ingestor(
            path=None,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo,
            index_prefix=index_prefix,
            selected_modules=selected_modules,
            s3_mode=True,
            s3_notes=s3_notes,
            magika_filter=magika_filter,
            dry_run=dry_run,
            force=force,
            start_date=start_date,
            end_date=end_date,
            analyzed=analyzed,
            decompile_modules=decompile_modules_set,
        ).ingest()

    elif s3_solo:
        # Process a single S3 file using S3 key provided as argument
        print(f"Starting S3 solo mode with S3 key: {s3_key}")

        # Override with environment variables if not provided via command line
        if not index_prefix:
            index_prefix = os.getenv('INDEX_PREFIX', 'redb')
        if not repo:
            repo = os.getenv('REPO', 's3-solo')

        # Validate required parameters
        if not s3_key:
            print("ERROR: S3 key is required for S3-solo mode")
            sys.exit(1)

        # Extract hash from S3 key by splitting and taking the last chunk
        # S3 key format examples:
        # - 09/f7/09f7d02a3c2382199458c98a62b045145ee54ab6aba86166aecf3d10c3c1444c.zip
        # - private/ab/cd/abcd1234567890abcdef1234567890abcdef1234567890abcdef123456.zip
        try:
            # Remove .zip extension and split by '/'
            sample_hash = s3_key.replace('.zip', '').split('/')[-1]
        except Exception as e:
            print(f"ERROR: Failed to extract hash from S3 key {s3_key}: {e}")
            sys.exit(1)

        print(f"S3 Key: {s3_key}")
        print(f"Extracted hash: {sample_hash}")
        print(f"Using index_prefix: {index_prefix}")
        print(f"Using repo: {repo}")
        print(f"Dry run mode: {dry_run}")
        print(f"Selected modules: {selected_modules}")

        # Use ingestor with S3-solo mode
        Ingestor(
            path=None,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo,
            index_prefix=index_prefix or "s3_solo",
            selected_modules=selected_modules,
            s3_mode=True,
            s3_solo=True,
            s3_solo_hash=sample_hash,
            s3_solo_key=s3_key,
            dry_run=dry_run,
            force=force,
            decompile_modules=decompile_modules_set,
        ).ingest()

    elif nomad_job:
        # Run as Nomad job using environment variables - convert to S3-solo mode
        print("Starting Nomad job processor...")
        
        # Process Nomad environment variables
        job_id = os.getenv('JOB_ID')
        s3_key = os.getenv('S3_KEY')
        worker_type = os.getenv('WORKER_TYPE')
        callback_url = os.getenv('CALLBACK_URL')
        modules = os.getenv('ANALYSIS_MODULES', 'all')
        
        # Validate required parameters
        if not all([job_id, s3_key, worker_type, callback_url]):
            print("ERROR: Missing required Nomad job parameters")
            print("Required: JOB_ID, S3_KEY, WORKER_TYPE, CALLBACK_URL")
            sys.exit(1)
        
        print(f"Job ID: {job_id}")
        print(f"S3 Key: {s3_key}")
        print(f"Worker Type: {worker_type}")
        print(f"Callback URL: {callback_url}")
        print(f"Analysis Modules: {modules}")
        
        # Extract hash from S3 key (remove sharding structure and .zip extension)
        # S3 key format: 09/f7/09f7d02a3c2382199458c98a62b045145ee54ab6aba86166aecf3d10c3c1444c.zip
        # Extract: 09f7d02a3c2382199458c98a62b045145ee54ab6aba86166aecf3d10c3c1444c
        try:
            parts = s3_key.split('/')
            if len(parts) == 3:
                sample_hash = parts[2].replace('.zip', '')
            else:
                # Fallback for non-sharded keys
                sample_hash = s3_key.replace('.zip', '')
        except Exception as e:
            print(f"ERROR: Failed to extract hash from S3 key {s3_key}: {e}")
            sys.exit(1)
        
        print(f"Extracted hash: {sample_hash}")
        
        # Set decompile flag based on worker type
        decompile = worker_type == 'decompilation'
        
        # Override modules if specified
        if modules != 'all':
            selected_modules = modules
        
        # Use S3-solo mode with extracted hash
        Ingestor(
            path=None,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo,
            index_prefix=index_prefix or "nomad",
            selected_modules=selected_modules,
            s3_mode=False,  # Not bulk S3 mode
            s3_solo=True,   # Use S3-solo mode
            s3_solo_hash=sample_hash,
            dry_run=dry_run,
            force=force,
            decompile_modules=decompile_modules_set,
        ).ingest()

        # TODO: Add callback to send results to callback_url
        print(f"[INFO] Nomad job {job_id} completed. Callback URL: {callback_url}")
    
    elif s3_mode:
        print(f"S3 mode enabled")
        if s3_notes:
            print(f"S3 notes filter: {s3_notes}")
        Ingestor(
            path=None,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo,
            index_prefix=index_prefix,
            selected_modules=selected_modules,
            s3_mode=True,
            s3_notes=s3_notes,
            magika_filter=magika_filter,
            dry_run=dry_run,
            force=force,
            decompile_modules=decompile_modules_set,
        ).ingest()
    else:
        print(f"Local mode with path: {path}")
        if path.endswith('.txt'):
            print(f"Reading file list from: {path}")

        Ingestor(
            path=path,
            decompile=decompile,
            yara_scan=yara_scan,
            with_yara=with_yara,
            repository=repo,
            index_prefix=index_prefix,
            selected_modules=selected_modules,
            dry_run=dry_run,
            force=force,
            decompile_modules=decompile_modules_set,
        ).ingest()

if __name__ == "__main__":
    main()