Hanjun Dai

134 papers A* 50A 5B 1Journal 65Unranked 12
YearRankTypeTitle / Venue / Authors
2026 J jnl
Trans. Mach. Learn. Res.
Shicong Cen, Jincheng Mei, Hanjun Dai, Dale Schuurmans, Yuejie Chi, Bo Dai
2025 J jnl
CoRR
Haotian Sun, Yitong Li, Yuchen Zhuang, Niao He, Hanjun Dai, Bo Dai
2025 A conf
AISTATS
Tong Yang, Jincheng Mei, Hanjun Dai, Zixin Wen, Shicong Cen, Dale Schuurmans, Yuejie Chi, Bo Dai
2025 J jnl
CoRR
Yuchen Zhuang, Aaron Trinh, Rushi Qiang, Haotian Sun, Chao Zhang, Hanjun Dai, Bo Dai
2025 A* conf
ICLR
Shicong Cen, Jincheng Mei, Katayoon Goshvadi, Hanjun Dai, Tong Yang, Sherry Yang, Dale Schuurmans, Yuejie Chi, Bo Dai
2024 J jnl
CoRR
Dale Schuurmans, Hanjun Dai, Francesco Zanini
2024 J jnl
CoRR
Shicong Cen, Jincheng Mei, Hanjun Dai, Dale Schuurmans, Yuejie Chi, Bo Dai
2024 J jnl
CoRR
Bernd Bohnet, Azade Nova, Aaron T. Parisi, Kevin Swersky, Katayoon Goshvadi, Hanjun Dai, Dale Schuurmans, Noah Fiedel, Hanie Sedghi
2024 J jnl
CoRR
Tong Yang, Jincheng Mei, Hanjun Dai, Zixin Wen, Shicong Cen, Dale Schuurmans, Yuejie Chi, Bo Dai
2024 J jnl
CoRR
Changhao Li, Yuchen Zhuang, Rushi Qiang, Haotian Sun, Hanjun Dai, Chao Zhang, Bo Dai
2024 A* conf
ICML
Songtao Liu, Hanjun Dai, Yue Zhao, Peng Liu
2024 J jnl
CoRR
Songtao Liu, Hanjun Dai, Yue Zhao, Peng Liu
2024 J jnl
CoRR
Songtao Liu, Zhengkai Tu, Hanjun Dai, Peng Liu
2024 J jnl
Trans. Mach. Learn. Res.
Ruoxi Sun, Sercan Ö. Arik, Alexandre Muzio, Lesly Miculicich, Satya Kesav Gundabathula, Pengcheng Yin, Hanjun Dai, Hootan Nakhost, Rajarishi Sinha, Zifeng Wang, Tomas Pfister
2024 A* conf
USENIX Security Symposium
Chang Zhu, Ziyang Li, Anton Xue, Ati Priya Bajaj, Wil Gibbs, Yibo Liu, Rajeev Alur, Tiffany Bao, Hanjun Dai, Adam Doupé, Mayur Naik, Yan Shoshitaishvili, Ruoyu Wang, Aravind Machiry
2024 A* conf
NeurIPS
Hanjun Dai, Bethany Wang, Xingchen Wan, Bo Dai, Sherry Yang, Azade Nova, Pengcheng Yin, Phitchaya Mangpo Phothilimthana, Charles Sutton, Dale Schuurmans
2024 J jnl
CoRR
Hanjun Dai, Bethany Wang, Xingchen Wan, Bo Dai, Sherry Yang, Azade Nova, Pengcheng Yin, Phitchaya Mangpo Phothilimthana, Charles Sutton, Dale Schuurmans
2024 J jnl
CoRR
Shicong Cen, Jincheng Mei, Katayoon Goshvadi, Hanjun Dai, Tong Yang, Sherry Yang, Dale Schuurmans, Yuejie Chi, Bo Dai
2023 A* conf
ICLR
Haoran Sun, Bo Dai, Charles Sutton, Dale Schuurmans, Hanjun Dai
2023 conf
ACL (Findings)
Xingchen Wan, Ruoxi Sun, Hanjun Dai, Sercan Ö. Arik, Tomas Pfister
2023 J jnl
CoRR
Xingchen Wan, Ruoxi Sun, Hanjun Dai, Sercan Ö. Arik, Tomas Pfister
2023 A* conf
NeurIPS
Katayoon Goshvadi, Haoran Sun, Xingchao Liu, Azade Nova, Ruqi Zhang, Will Grathwohl, Dale Schuurmans, Hanjun Dai
2023 A conf
AISTATS
Haoran Sun, Hanjun Dai, Bo Dai, Haomin Zhou, Dale Schuurmans
2023 J jnl
CoRR
Lijun Yu, Jin Miao, Xiaoyu Sun, Jiayi Chen, Alexander G. Hauptmann, Hanjun Dai, Wei Wei
2023 conf
EMNLP (Industry Track)
Lijun Yu, Jin Miao, Xiaoyu Sun, Jiayi Chen, Alexander G. Hauptmann, Hanjun Dai, Wei Wei
2023 A* conf
ICML
Azade Nova, Hanjun Dai, Dale Schuurmans
2023 J jnl
CoRR
Azade Nova, Hanjun Dai, Dale Schuurmans
2023 A* conf
NeurIPS
Kensen Shi, Hanjun Dai, Wen-Ding Li, Kevin Ellis, Charles Sutton
2023 J jnl
CoRR
Kensen Shi, Hanjun Dai, Wen-Ding Li, Kevin Ellis, Charles Sutton
2023 J jnl
CoRR
Zhaocheng Zhu, Yuan Xue, Xinyun Chen, Denny Zhou, Jian Tang, Dale Schuurmans, Hanjun Dai
2023 A* conf
NeurIPS
Yilun Du, Sherry Yang, Bo Dai, Hanjun Dai, Ofir Nachum, Josh Tenenbaum, Dale Schuurmans, Pieter Abbeel
2023 J jnl
CoRR
Yilun Du, Mengjiao Yang, Bo Dai, Hanjun Dai, Ofir Nachum, Joshua B. Tenenbaum, Dale Schuurmans, Pieter Abbeel
2023 A conf
AISTATS
Hanjun Dai, Yuan Xue, Niao He, Yixin Wang, Na Li, Dale Schuurmans, Bo Dai
2023 J jnl
CoRR
Dinghuai Zhang, Hanjun Dai, Nikolay Malkin, Aaron C. Courville, Yoshua Bengio, Ling Pan
2023 A* conf
NeurIPS
Dinghuai Zhang, Hanjun Dai, Nikolay Malkin, Aaron C. Courville, Yoshua Bengio, Ling Pan
2023 conf
EMNLP (Findings)
Jiayi Chen, Hanjun Dai, Bo Dai, Aidong Zhang, Wei Wei
2023 J jnl
CoRR
Jiayi Chen, Hanjun Dai, Bo Dai, Aidong Zhang, Wei Wei
2023 A* conf
ICML
Haoran Sun, Katayoon Goshvadi, Azade Nova, Dale Schuurmans, Hanjun Dai
2023 J jnl
CoRR
Ruoxi Sun, Sercan Ö. Arik, Hootan Nakhost, Hanjun Dai, Rajarishi Sinha, Pengcheng Yin, Tomas Pfister
2023 conf
EMNLP (Findings)
Ruoxi Sun, Sercan Ö. Arik, Rajarishi Sinha, Hootan Nakhost, Hanjun Dai, Pengcheng Yin, Tomas Pfister
2023 J jnl
CoRR
Ruoxi Sun, Sercan Ö. Arik, Rajarishi Sinha, Hootan Nakhost, Hanjun Dai, Pengcheng Yin, Tomas Pfister
2023 A* conf
ICLR
Haoran Sun, Lijun Yu, Bo Dai, Dale Schuurmans, Hanjun Dai
2023 A* conf
EMNLP
Xingchen Wan, Ruoxi Sun, Hootan Nakhost, Hanjun Dai, Julian Eisenschlos, Sercan Ö. Arik, Tomas Pfister
2023 J jnl
CoRR
Xingchen Wan, Ruoxi Sun, Hootan Nakhost, Hanjun Dai, Julian Martin Eisenschlos, Sercan Ö. Arik, Tomas Pfister
2023 A* conf
NeurIPS
Meng Liu, Mingda Zhang, Jialu Liu, Hanjun Dai, Ming-Hsuan Yang, Shuiwang Ji, Zheyun Feng, Boqing Gong
2023 J jnl
CoRR
Meng Liu, Mingda Zhang, Jialu Liu, Hanjun Dai, Ming-Hsuan Yang, Shuiwang Ji, Zheyun Feng, Boqing Gong
2022 J jnl
CoRR
Haoran Sun, Etash Kumar Guha, Hanjun Dai
2022 A* conf
ICLR
Pardis Pashakhanloo, Aaditya Naik, Yuepeng Wang, Hanjun Dai, Petros Maniatis, Mayur Naik
2022 A* conf
ICLR
Kensen Shi, Hanjun Dai, Kevin Ellis, Charles Sutton
2022 J jnl
CoRR
Kensen Shi, Hanjun Dai, Kevin Ellis, Charles Sutton
2022 J jnl
CoRR
Haoran Sun, Hanjun Dai, Bo Dai, Haomin Zhou, Dale Schuurmans
2022 A* conf
NeurIPS
Ruoxi Sun, Hanjun Dai, Adams Wei Yu
2022 J jnl
CoRR
Ruoxi Sun, Hanjun Dai, Adams Wei Yu
2022 J jnl
CoRR
Hanjun Dai, Yuan Xue, Niao He, Bethany Wang, Na Li, Dale Schuurmans, Bo Dai
2022 A* conf
ICML
Hanjun Dai, Mengjiao Yang, Yuan Xue, Dale Schuurmans, Bo Dai
2022 A* conf
ICLR
Hanjun Dai, Yuan Xue, Zia Syed, Dale Schuurmans, Bo Dai
2022 A* conf
NeurIPS
Haoran Sun, Hanjun Dai, Dale Schuurmans
2022 J jnl
CoRR
Haoran Sun, Hanjun Dai, Dale Schuurmans
2022 A* conf
ICLR
Haoran Sun, Hanjun Dai, Wei Xia, Arun Ramamurthy
2022 A* conf
KDD
Hongyu Ren, Hanjun Dai, Bo Dai, Xinyun Chen, Denny Zhou, Jure Leskovec, Dale Schuurmans
2022 J jnl
CoRR
Haoran Sun, Lijun Yu, Bo Dai, Dale Schuurmans, Hanjun Dai
2021 A* conf
ICLR
Augustus Odena, Kensen Shi, David Bieber, Rishabh Singh, Charles Sutton, Hanjun Dai
2021 A* conf
NeurIPS
Hongyu Ren, Hanjun Dai, Zihang Dai, Mengjiao Yang, Jure Leskovec, Dale Schuurmans, Bo Dai
2021 J jnl
CoRR
Hongyu Ren, Hanjun Dai, Zihang Dai, Mengjiao Yang, Jure Leskovec, Dale Schuurmans, Bo Dai
2021 A* conf
ICML
Hongyu Ren, Hanjun Dai, Bo Dai, Xinyun Chen, Michihiro Yasunaga, Haitian Sun, Dale Schuurmans, Jure Leskovec, Denny Zhou
2021 A* conf
ICLR
Binghong Chen, Tianzhe Wang, Chengtao Li, Hanjun Dai, Le Song
2021 J jnl
CoRR
Hanjun Dai, Yuan Xue, Zia Syed, Dale Schuurmans, Bo Dai
2021 J jnl
CoRR
Hongyu Ren, Hanjun Dai, Bo Dai, Xinyun Chen, Denny Zhou, Jure Leskovec, Dale Schuurmans
2021 A* conf
ICML
Xinyun Chen, Petros Maniatis, Rishabh Singh, Charles Sutton, Hanjun Dai, Max Lin, Denny Zhou
2021 J jnl
CoRR
Xinyun Chen, Petros Maniatis, Rishabh Singh, Charles Sutton, Hanjun Dai, Max Lin, Denny Zhou
2021 A* conf
NeurIPS
Ruoxi Sun, Hanjun Dai, Li Li, Steven Kearnes, Bo Dai
2020 conf
CAV (2)
Xujie Si, Aaditya Naik, Hanjun Dai, Mayur Naik, Le Song
2020 J jnl
CoRR
Yujia Xie, Hanjun Dai, Minshuo Chen, Bo Dai, Tuo Zhao, Hongyuan Zha, Wei Wei, Tomas Pfister
2020 A* conf
NeurIPS
Yujia Xie, Hanjun Dai, Minshuo Chen, Bo Dai, Tuo Zhao, Hongyuan Zha, Wei Wei, Tomas Pfister
2020 A* conf
ICML
Mengjiao Yang, Bo Dai, Hanjun Dai, Dale Schuurmans
2020 J jnl
CoRR
Mengjiao Yang, Bo Dai, Hanjun Dai, Dale Schuurmans
2020 J jnl
CoRR
Ruoxi Sun, Hanjun Dai, Li Li, Steven Kearnes, Bo Dai
2020 A* conf
ICLR
Elizabeth Dinella, Hanjun Dai, Ziyang Li, Mayur Naik, Le Song, Ke Wang
2020 A* conf
NeurIPS
Hanjun Dai, Rishabh Singh, Bo Dai, Charles Sutton, Dale Schuurmans
2020 J jnl
CoRR
Hanjun Dai, Rishabh Singh, Bo Dai, Charles Sutton, Dale Schuurmans
2020
Hanjun Dai
2020 A* conf
ICML
Xinshi Chen, Hanjun Dai, Yu Li, Xin Gao, Le Song
2020 J jnl
CoRR
Xinshi Chen, Hanjun Dai, Yu Li, Xin Gao, Le Song
2020 J jnl
CoRR
Rohit Batra, Hanjun Dai, Tran Doan Huan, Lihua Chen, Chiho Kim, Will R. Gutekunst, Le Song, Rampi Ramprasad
2020 A* conf
ICML
Binghong Chen, Chengtao Li, Hanjun Dai, Le Song
2020 J jnl
CoRR
Binghong Chen, Chengtao Li, Hanjun Dai, Le Song
2020 J jnl
CoRR
Hanjun Dai, Chengtao Li, Connor W. Coley, Bo Dai, Le Song
2020 A* conf
ICML
Hanjun Dai, Azade Nazi, Yujia Li, Bo Dai, Dale Schuurmans
2020 J jnl
CoRR
Hanjun Dai, Azade Nazi, Yujia Li, Bo Dai, Dale Schuurmans
2019 A* conf
ICML
Thomas Kipf, Yujia Li, Hanjun Dai, Vinícius Flores Zambaldi, Alvaro Sanchez-Gonzalez, Edward Grefenstette, Pushmeet Kohli, Peter W. Battaglia
2019 J jnl
CoRR
Harsh Shrivastava, Eugene Bart, Bob Price, Hanjun Dai, Bo Dai, Srinivas Aluru
2019 A* conf
NeurIPS
Bo Dai, Zhen Liu, Hanjun Dai, Niao He, Arthur Gretton, Le Song, Dale Schuurmans
2019 J jnl
CoRR
Bo Dai, Zhen Liu, Hanjun Dai, Niao He, Arthur Gretton, Le Song, Dale Schuurmans
2019 A conf
AISTATS
Bo Dai, Hanjun Dai, Arthur Gretton, Le Song, Dale Schuurmans, Niao He
2019 A* conf
NeurIPS
Hanjun Dai, Yujia Li, Chenglong Wang, Rishabh Singh, Po-Sen Huang, Pushmeet Kohli
2019 J jnl
CoRR
Hanjun Dai, Yujia Li, Chenglong Wang, Rishabh Singh, Po-Sen Huang, Pushmeet Kohli
2019 conf
ICLR (Poster)
Xujie Si, Yuan Yang, Hanjun Dai, Mayur Naik, Le Song
2019 J jnl
CoRR
Xinshi Chen, Hanjun Dai, Le Song
2019 A* conf
ICML
Xinshi Chen, Hanjun Dai, Le Song
2019 A* conf
NeurIPS
Hanjun Dai, Chengtao Li, Connor W. Coley, Bo Dai, Le Song
2018 conf
ICLR (Workshop)
Feng Wang, Weiyang Liu, Hanjun Dai, Haijun Liu, Jian Cheng
2018 A* conf
ICML
Hanjun Dai, Hui Li, Tian Tian, Xin Huang, Lin Wang, Jun Zhu, Le Song
2018 J jnl
CoRR
Hanjun Dai, Hui Li, Tian Tian, Xin Huang, Lin Wang, Jun Zhu, Le Song
2018 J jnl
CoRR
Thomas Kipf, Yujia Li, Hanjun Dai, Vinícius Flores Zambaldi, Edward Grefenstette, Pushmeet Kohli, Peter W. Battaglia
2018 A* conf
NeurIPS
Harsh Shrivastava, Eugene Bart, Bob Price, Hanjun Dai, Bo Dai, Srinivas Aluru
2018 A* conf
NeurIPS
Bo Dai, Hanjun Dai, Niao He, Weiyang Liu, Zhen Liu, Jianshu Chen, Lin Xiao, Le Song
2018 J jnl
CoRR
Yuyu Zhang, Hanjun Dai, Kamil Toraman, Le Song
2018 J jnl
CoRR
Bo Dai, Hanjun Dai, Arthur Gretton, Le Song, Dale Schuurmans, Niao He
2018 A* conf
NeurIPS
Xujie Si, Hanjun Dai, Mukund Raghothaman, Mayur Naik, Le Song
2018 A* conf
ICML
Hanjun Dai, Zornitsa Kozareva, Bo Dai, Alexander J. Smola, Le Song
2018 conf
ICLR (Poster)
Hanjun Dai, Yingtao Tian, Bo Dai, Steven Skiena, Le Song
2018 J jnl
CoRR
Hanjun Dai, Yingtao Tian, Bo Dai, Steven Skiena, Le Song
2018 A* conf
AAAI
Yuyu Zhang, Hanjun Dai, Zornitsa Kozareva, Alexander J. Smola, Le Song
2017 J jnl
CoRR
Rakshit S. Trivedi, Hanjun Dai, Yichen Wang, Le Song
2017 A* conf
ICML
Rakshit S. Trivedi, Hanjun Dai, Yichen Wang, Le Song
2017 conf
NIPS
Elias B. Khalil, Hanjun Dai, Yuyu Zhang, Bistra Dilkina, Le Song
2017 J jnl
CoRR
Hanjun Dai, Elias B. Khalil, Yuyu Zhang, Bistra Dilkina, Le Song
2017 conf
ICLR (Poster)
Hanjun Dai, Bo Dai, Yan-Ming Zhang, Shuang Li, Le Song
2017 J jnl
Bioinform.
Hanjun Dai, Ramzan Umarov, Hiroyuki Kuwahara, Yu Li, Le Song, Xin Gao
2017 J jnl
CoRR
Yuyu Zhang, Hanjun Dai, Zornitsa Kozareva, Alexander J. Smola, Le Song
2016 A* conf
ICML
Hanjun Dai, Bo Dai, Le Song
2016 J jnl
CoRR
Hanjun Dai, Bo Dai, Le Song
2016 A conf
AISTATS
Bo Dai, Niao He, Hanjun Dai, Le Song
2016 J jnl
CoRR
Hanjun Dai, Yichen Wang, Rakshit S. Trivedi, Le Song
2016 conf
DLRS@RecSys
Hanjun Dai, Yichen Wang, Rakshit S. Trivedi, Le Song
2016 A* conf
KDD
Nan Du, Hanjun Dai, Rakshit S. Trivedi, Utkarsh Upadhyay, Manuel Gomez-Rodriguez, Le Song
2015 J jnl
ACM Trans. Inf. Syst.
Qing Cui, Bin Gao, Jiang Bian, Siyu Qiu, Hanjun Dai, Tie-Yan Liu
2015 conf
NIPS
Shuang Li, Yao Xie, Hanjun Dai, Le Song
2015 J jnl
CoRR
Shuang Li, Yao Xie, Hanjun Dai, Le Song
2015 J jnl
CoRR
Yao Xie, Ruiyang Song, Hanjun Dai, Qingbin Li, Le Song
2015 J jnl
CoRR
Bo Dai, Niao He, Hanjun Dai, Le Song
2014 B conf
COLING
Fei Tian, Hanjun Dai, Jiang Bian, Bin Gao, Rui Zhang, Enhong Chen, Tie-Yan Liu
2014 A* conf
AAAI
Yuyu Zhang, Hanjun Dai, Chang Xu, Jun Feng, Taifeng Wang, Jiang Bian, Bin Wang, Tie-Yan Liu
2014 J jnl
CoRR
Yuyu Zhang, Hanjun Dai, Chang Xu, Jun Feng, Taifeng Wang, Jiang Bian, Bin Wang, Tie-Yan Liu
redb/extractors/js_extractors/js_patterns.py
← Index redb/extractors/js_extractors/js_patterns.py python
"""Canonical, compiled JavaScript regex patterns shared across JS extractors.

All suspicious-API patterns and the few feature-only patterns live here so each
expression is compiled exactly once per Python process and so any pattern that
was previously duplicated across `js_features.py` and `js_suspicious_apis.py`
now resolves to a single shared compiled object.

JavaScript is case-sensitive at runtime, but every suspicious-API pattern matches
either a literal-case identifier (`\\beval\\s*\\(`, `String\\.fromCharCode`, etc.)
or a string-quoted token (`"powershell"`). Compiling them with `re.IGNORECASE`
matches the historical behaviour of `JSSuspiciousAPIsExtractor` and is safe for
the patterns that historically came from `JSFeaturesExtractor` — those literals
are spelled in real-world JS exactly as written.

`scan_source()` is the entry point used by extractors: it walks the source once
per pattern using the pre-compiled regexes and returns a flat
`{name: {"count": N, "lines": [unique_line_numbers_sorted]}}` dict. Both
`JSFeaturesExtractor` and `JSSuspiciousAPIsExtractor` consume the same dict so
the per-pattern × per-line loops they used to run independently collapse to a
single shared scan.
"""

import bisect
import re
from typing import Dict, Iterable, List, Mapping

_FLAGS = re.IGNORECASE

# Canonical compiled patterns, keyed by their human-readable name. The name is
# also the value emitted into `redb_js_suspicious_apis.api_name`.
PATTERNS = {
    # ---- code execution ----
    "eval": re.compile(r"\beval\s*\(", _FLAGS),
    "Function constructor": re.compile(r"\bnew\s+Function\s*\(", _FLAGS),
    "execScript": re.compile(r"\bexecScript\s*\(", _FLAGS),
    "document.write": re.compile(r"\bdocument\.write(?:ln)?\s*\(", _FLAGS),
    "innerHTML assignment": re.compile(r"\.innerHTML\s*=", _FLAGS),
    "outerHTML assignment": re.compile(r"\.outerHTML\s*=", _FLAGS),
    "insertAdjacentHTML": re.compile(r"\.insertAdjacentHTML\s*\(", _FLAGS),
    # ---- network ----
    "XMLHttpRequest": re.compile(r"\bnew\s+XMLHttpRequest\b", _FLAGS),
    "fetch": re.compile(r"\bfetch\s*\(", _FLAGS),
    "WebSocket": re.compile(r"\bnew\s+WebSocket\s*\(", _FLAGS),
    "navigator.sendBeacon": re.compile(r"\bnavigator\.sendBeacon\s*\(", _FLAGS),
    "ActiveXObject XMLHTTP": re.compile(
        r"ActiveXObject\s*\(\s*[\"\'](?:MSXML2\.XMLHTTP|Microsoft\.XMLHTTP)", _FLAGS
    ),
    "require network module": re.compile(
        r"require\s*\(\s*[\"\'](?:http|https|net|dgram)[\"\']", _FLAGS
    ),
    "axios": re.compile(r"\baxios\b", _FLAGS),
    # ---- filesystem ----
    "require fs": re.compile(r"require\s*\(\s*[\"\']fs[\"\']", _FLAGS),
    "require path": re.compile(r"require\s*\(\s*[\"\']path[\"\']", _FLAGS),
    "FileSystemObject": re.compile(r"Scripting\.FileSystemObject", _FLAGS),
    "ADODB.Stream": re.compile(r"ADODB\.Stream", _FLAGS),
    "Shell.Application": re.compile(r"Shell\.Application", _FLAGS),
    "WScript.CreateObject": re.compile(r"WScript\.CreateObject", _FLAGS),
    # ---- process ----
    "require child_process": re.compile(r"require\s*\(\s*[\"\']child_process[\"\']", _FLAGS),
    "child_process exec": re.compile(r"child_process\.(?:exec|spawn|execFile|fork)\s*\(", _FLAGS),
    "WScript.Shell": re.compile(r"WScript\.Shell", _FLAGS),
    "WScript.Shell.Run": re.compile(r"\.Run\s*\(", _FLAGS),
    "WScript.Shell.Exec": re.compile(r"\.Exec\s*\(", _FLAGS),
    "ShellExecute": re.compile(r"\bShellExecute\b", _FLAGS),
    "PowerShell reference": re.compile(r"[\"\']powershell[\"\']", _FLAGS),
    "cmd.exe reference": re.compile(r"[\"\']cmd\.exe[\"\']", _FLAGS),
    "require os": re.compile(r"require\s*\(\s*[\"\']os[\"\']", _FLAGS),
    # ---- registry ----
    "RegRead": re.compile(r"\.RegRead\s*\(", _FLAGS),
    "RegWrite": re.compile(r"\.RegWrite\s*\(", _FLAGS),
    "RegDelete": re.compile(r"\.RegDelete\s*\(", _FLAGS),
    "StdRegProv": re.compile(r"StdRegProv", _FLAGS),
    # ---- crypto / encoding ----
    "atob": re.compile(r"\batob\s*\(", _FLAGS),
    "btoa": re.compile(r"\bbtoa\s*\(", _FLAGS),
    "String.fromCharCode": re.compile(r"String\.fromCharCode\s*\(", _FLAGS),
    "unescape": re.compile(r"\bunescape\s*\(", _FLAGS),
    "decodeURIComponent": re.compile(r"\bdecodeURIComponent\s*\(", _FLAGS),
    "Buffer.from": re.compile(r"Buffer\.from\s*\(", _FLAGS),
    "crypto module": re.compile(r"crypto\.create(?:Cipher|Decipher|Hash|Hmac)", _FLAGS),
    # ---- DOM manipulation ----
    "document.forms": re.compile(r"document\.forms", _FLAGS),
    "document.cookie": re.compile(r"document\.cookie", _FLAGS),
    "querySelector sensitive input": re.compile(
        r"document\.querySelector\s*\([^)]*(?:password|credit|card|cvv|ssn)", _FLAGS
    ),
    "submit event listener": re.compile(r"addEventListener\s*\(\s*[\"\']submit", _FLAGS),
    "createElement script/iframe": re.compile(
        r"\.createElement\s*\(\s*[\"\'](?:script|iframe)", _FLAGS
    ),
    "dynamic script src": re.compile(r"\.src\s*=\s*[\"\'](?:https?://|//)", _FLAGS),
}

# Pattern name -> category (one of code_execution / network / filesystem /
# process / registry / crypto_encoding / dom_manipulation).
CATEGORIES = {
    "eval": "code_execution",
    "Function constructor": "code_execution",
    "execScript": "code_execution",
    "document.write": "code_execution",
    "innerHTML assignment": "code_execution",
    "outerHTML assignment": "code_execution",
    "insertAdjacentHTML": "code_execution",
    "XMLHttpRequest": "network",
    "fetch": "network",
    "WebSocket": "network",
    "navigator.sendBeacon": "network",
    "ActiveXObject XMLHTTP": "network",
    "require network module": "network",
    "axios": "network",
    "require fs": "filesystem",
    "require path": "filesystem",
    "FileSystemObject": "filesystem",
    "ADODB.Stream": "filesystem",
    "Shell.Application": "filesystem",
    "WScript.CreateObject": "filesystem",
    "require child_process": "process",
    "child_process exec": "process",
    "WScript.Shell": "process",
    "WScript.Shell.Run": "process",
    "WScript.Shell.Exec": "process",
    "ShellExecute": "process",
    "PowerShell reference": "process",
    "cmd.exe reference": "process",
    "require os": "process",
    "RegRead": "registry",
    "RegWrite": "registry",
    "RegDelete": "registry",
    "StdRegProv": "registry",
    "atob": "crypto_encoding",
    "btoa": "crypto_encoding",
    "String.fromCharCode": "crypto_encoding",
    "unescape": "crypto_encoding",
    "decodeURIComponent": "crypto_encoding",
    "Buffer.from": "crypto_encoding",
    "crypto module": "crypto_encoding",
    "document.forms": "dom_manipulation",
    "document.cookie": "dom_manipulation",
    "querySelector sensitive input": "dom_manipulation",
    "submit event listener": "dom_manipulation",
    "createElement script/iframe": "dom_manipulation",
    "dynamic script src": "dom_manipulation",
}

# Patterns consumed only by JSFeaturesExtractor (no category, never surfaced as
# a suspicious-API row). Kept here so every JS regex is compiled in one place.
FEATURE_PATTERNS = {
    "hex_escape": re.compile(r"\\x[0-9a-fA-F]{2}"),
    "unicode_escape": re.compile(r"\\u[0-9a-fA-F]{4}"),
    "base64_string": re.compile(r"[A-Za-z0-9+/]{40,}={0,2}"),
    # decodeURI matches BOTH decodeURI and decodeURIComponent. The latter is also
    # a suspicious-API pattern in PATTERNS; this broader form is what the
    # `decodeuri_count` feature column has historically counted.
    "decodeURI": re.compile(r"\b(?:decodeURI|decodeURIComponent)\s*\(", _FLAGS),
    "settimeout_setinterval": re.compile(r"\b(?:setTimeout|setInterval)\s*\(", _FLAGS),
    "function_decl": re.compile(r"\bfunction\s+\w+\s*\(|\bfunction\s*\("),
    "var_decl": re.compile(r"\b(?:var|let|const)\s+"),
    "string_concat": re.compile(r"[\"\'][\s]*\+[\s]*[\"\']"),
    "comment": re.compile(r"//.*?$|/\*[\s\S]*?\*/", re.MULTILINE),
    "long_string": re.compile(r"[\"\']([^\"\']{256,})[\"\']"),
    "array_function_call": re.compile(r"\[(?:0x[0-9a-f]+|[\d]+)\]\s*\(", _FLAGS),
}

# Patterns consumed only by JSStringsExtractor for encoded-string discovery.
# Scoped to *hidden* strings only — patterns whose decoded form is not visible
# to a substring search over the raw text. Plain long literals are not
# extracted here because they're already preserved in code_text_content and
# scraped by the IOC pipeline over text_raw / text_normalized.
#
# Distinct from FEATURE_PATTERNS even where the names rhyme:
#   FEATURE_PATTERNS["hex_escape"] / ["unicode_escape"]   -> single escape
#   STRING_PATTERNS["hex_escape_seq"] / ["unicode_escape_seq"] -> 4+ / 3+ in a row
#   FEATURE_PATTERNS["base64_string"]                     -> bare base64 token
#   STRING_PATTERNS["base64_quoted"]                      -> base64 inside JS quotes
# These do not share match objects with the suspicious-API or feature scans, so
# they are not folded into JSContext.scan; the strings extractor walks them
# itself (one finditer per pattern, with shared line-offset bisect in #4b).
STRING_PATTERNS = {
    "hex_escape_seq": re.compile(r"(?:\\x[0-9a-fA-F]{2}){4,}"),
    "unicode_escape_seq": re.compile(r"(?:\\u[0-9a-fA-F]{4}){3,}"),
    "charcode_call": re.compile(r"String\.fromCharCode\s*\(\s*([\d,\s]+)\s*\)"),
    "base64_quoted": re.compile(r"[\"\']([A-Za-z0-9+/]{40,}={0,2})[\"\']"),
    "concat_chain": re.compile(r"(?:[\"\'][^\"\']+[\"\']\s*\+\s*){3,}[\"\'][^\"\']+[\"\']"),
}


def line_offsets(source: str) -> List[int]:
    """Sorted list of byte offsets for every newline in `source`, plus a final
    sentinel of len(source). Used to translate match offsets into 1-indexed
    line numbers via bisect.
    """
    offsets = [-1]  # so that bisect_right of offset 0 returns line 1
    push = offsets.append
    idx = source.find("\n")
    while idx != -1:
        push(idx)
        idx = source.find("\n", idx + 1)
    return offsets


def _scan_one(
    pattern: "re.Pattern[str]", source: str, offsets: List[int]
) -> Dict[str, object]:
    """Run a single compiled pattern over `source` and return count + unique lines."""
    count = 0
    seen_lines: "set[int]" = set()
    for m in pattern.finditer(source):
        count += 1
        seen_lines.add(bisect.bisect_right(offsets, m.start()))
    if not count:
        return None  # type: ignore[return-value]
    return {"count": count, "lines": sorted(seen_lines)}


def scan_source(
    source: str,
    patterns: Iterable[Mapping[str, "re.Pattern[str]"]] = (PATTERNS, FEATURE_PATTERNS),
) -> Dict[str, Dict[str, object]]:
    """Scan `source` against every compiled pattern in `patterns`.

    Returns a dict keyed by pattern name. Each entry has:
        "count": total number of matches in the source
        "lines": sorted list of unique 1-indexed line numbers where the pattern
                 matched (deduplicated — multiple matches on the same line
                 collapse to one entry, preserving the historical
                 line-set semantics of JSSuspiciousAPIsExtractor)
    Patterns with zero matches are absent from the dict; callers should default
    to {"count": 0, "lines": []}.
    """
    if not source:
        return {}
    offsets = line_offsets(source)
    results: Dict[str, Dict[str, object]] = {}
    for table in patterns:
        for name, pat in table.items():
            entry = _scan_one(pat, source, offsets)
            if entry is not None:
                results[name] = entry
    return results