Xianwen Wang

87 papers Journal 78Unranked 9
YearRankTypeTitle / Venue / Authors
2026 J jnl
J. Informetrics
Xianwen Wang, Pengfei Jia, Weixi Xie
2026 J jnl
Biomed. Signal Process. Control.
Xianwen Wang, Yibin Liu, Xuesheng Bian
2026 J jnl
J. Informetrics
Yu Geng, Yixian Yin, Ruonan Cai, Xianwen Wang
2025 J jnl
Discov. Artif. Intell.
Xueyun Zhang, Li He, Hui Zou, Xianwen Wang
2025 J jnl
CoRR
Songlin Cai, Xuan Liu, Xianwen Wang
2025 J jnl
J. Inf. Sci.
Guangyao Zhang, Furong Shang, Lili Wang, Weixi Xie, Pengfei Jia, Chunlin Jiang, Xianwen Wang
2025 J jnl
CoRR
Yingyuan Yang, Guoyuan Liang, Xianwen Wang, Kaiming Wang, Can Wang, Xiaojun Wu
2025 J jnl
Scientometrics
Pengfei Jia, Weixi Xie, Wencan Tian, Xianwen Wang
2025 J jnl
J. Assoc. Inf. Sci. Technol.
Xianwen Wang, Wencan Tian, Ruonan Cai, Zhichao Fang
2024 J jnl
CoRR
Wencan Tian, Zhichao Fang, Xianwen Wang, Rodrigo Costas
2024 J jnl
Scientometrics
Wencan Tian, Zhichao Fang, Xianwen Wang, Rodrigo Costas
2024 J jnl
Scientometrics
Weixi Xie, Pengfei Jia, Guangyao Zhang, Xianwen Wang
2024 J jnl
Scientometrics
Wencan Tian, Yongzhen Wang, Zhigang Hu, Ruonan Cai, Guangyao Zhang, Xianwen Wang
2024 conf
BigCom
Yan Wang, Lan Zhang, Xianwen Wang, Ke Ding, Jin Yan
2024 J jnl
Quant. Sci. Stud.
Wencan Tian, Ruonan Cai, Zhichao Fang, Qianqian Xie, Zhigang Hu, Xianwen Wang
2024 J jnl
J. Assoc. Inf. Sci. Technol.
Wencan Tian, Ruonan Cai, Zhichao Fang, Yu Geng, Xianwen Wang, Zhigang Hu
2023 conf
RCAR
Yingyuan Yang, Xianwen Wang, Sijin Luo, Guoyuan Liang
2023 J jnl
J. Inf. Sci.
Yunxue Cui, Zhichao Fang, Xianwen Wang
2023 J jnl
Scientometrics
Pengfei Jia, Weixi Xie, Guangyao Zhang, Xianwen Wang
2023 J jnl
Inf. Process. Manag.
Renmeng Cao, Xiao Fan Liu, Zhichao Fang, Xiao-Ke Xu, Xianwen Wang
2023 J jnl
J. Assoc. Inf. Sci. Technol.
Yunxue Cui, Yongzhen Wang, Xiaozhong Liu, Xianwen Wang, Xuhong Zhang
2022 J jnl
Aslib J. Inf. Manag.
Guangyao Zhang, Licheng Wang, Weixi Xie, Furong Shang, Xinlu Xia, Chunlin Jiang, Xianwen Wang
2022 J jnl
J. Informetrics
Renmeng Cao, Yu Geng, Xiaoke Xu, Xianwen Wang
2022 J jnl
Scientometrics
Yu Geng, Renmeng Cao, Xiao-Pu Han, Wencan Tian, Guangyao Zhang, Xianwen Wang
2022 J jnl
J. Informetrics
Guangyao Zhang, Shenmeng Xu, Yao Sun, Chunlin Jiang, Xianwen Wang
2021 J jnl
CoRR
Guangyao Zhang, Furong Shang, Weixi Xie, Yuhan Guo, Chunlin Jiang, Xianwen Wang
2021 J jnl
CoRR
Renmeng Cao, Xiaoke Xu, Yunxue Cui, Zhizhao Fang, Xianwen Wang
2021 J jnl
J. Assoc. Inf. Sci. Technol.
Zhichao Fang, Rodrigo Costas, Wencan Tian, Xianwen Wang, Paul Wouters
2021 J jnl
Scientometrics
Renmeng Cao, Xianwen Wang, Xiaoke Xu, Jianlin Zhou
2021 J jnl
J. Data Inf. Sci.
Howell Y. Wang, Shelia X. Wei, Cong Cao, Xianwen Wang, Fred Y. Ye
2021 J jnl
J. Informetrics
Fang Xu, Guiyan Ou, Tingcan Ma, Xianwen Wang
2021 J jnl
CoRR
Huixu Li, Lanjian Liu, Xianwen Wang
2021 J jnl
J. Informetrics
Huixu Li, Lanjian Liu, Xianwen Wang
2021 J jnl
Scientometrics
Guangyao Zhang, Yuqi Wang, Weixi Xie, Han Du, Chunlin Jiang, Xianwen Wang
2020 J jnl
CoRR
Zhichao Fang, Rodrigo Costas, Wencan Tian, Xianwen Wang, Paul Wouters
2020 J jnl
Scientometrics
Zhichao Fang, Rodrigo Costas, Wencan Tian, Xianwen Wang, Paul Wouters
2020 J jnl
J. Informetrics
Yukai Wang, Zhongkai Yang, Lanjian Liu, Xianwen Wang
2020 J jnl
Scientometrics
Zhigang Hu, Wencan Tian, Jiacheng Guo, Xianwen Wang
2019 conf
ISSI
Yunxue Cui, Xiaoke Xu, Renmeng Cao, Zhichao Fang, Jianyun Zhou, Xianwen Wang
2019 conf
ASIST
Yukai Wang, Zhongkai Yang, Xianwen Wang
2019 conf
ASIST
Shenmeng Xu, Guangyao Zhang, Yao Sun, Xianwen Wang
2019 conf
ISSI
Wencan Tian, Zhigang Hu, Xianwen Wang
2018 J jnl
Scientometrics
Zhigang Hu, Gege Lin, Taian Sun, Xianwen Wang
2018 conf
ASIST
Xianwen Wang, Yunxue Cui, Shenmeng Xu
2018 J jnl
J. Informetrics
Zhigang Hu, Wencan Tian, Shenmeng Xu, Chunbo Zhang, Xianwen Wang
2018 J jnl
CoRR
Xianwen Wang, Yunxue Cui, Qingchun Li, Xinhui Guo
2018 J jnl
Aslib J. Inf. Manag.
Xianwen Wang, Yunxue Cui, Shenmeng Xu, Zhigang Hu
2017 J jnl
Electron. Libr.
Zhichao Fang, Xinhui Guo, Yang Yang, Zhongkai Yang, Qingchun Li, Zhigang Hu, Xianwen Wang
2017 J jnl
Scientometrics
Lili Wang, Xianwen Wang, Niels J. Philipsen
2017 J jnl
Frontiers Res. Metrics Anal.
Xianwen Wang, Yunxue Cui, Qingchun Li, Xinhui Guo
2016 conf
ICSAI
Xuezhi Lv, Baoxin Fan, Xianwen Wang, Xinhui Zhao
2016 J jnl
CoRR
Xianwen Wang, Zhichao Fang
2016 J jnl
Frontiers Res. Metrics Anal.
Xianwen Wang, Zhichao Fang, Qingchun Li, Xinhui Guo
2016 J jnl
CoRR
Xianwen Wang, Zhichao Fang, Qingchun Li, Xinhui Guo
2016 J jnl
CoRR
Xianwen Wang, Shenmeng Xu, Zhichao Fang
2016 J jnl
CoRR
Xianwen Wang, Zhichao Fang, Xinhui Guo
2016 J jnl
Scientometrics
Xianwen Wang, Zhichao Fang, Xinhui Guo
2016 J jnl
Scientometrics
Xianwen Wang, Zhichao Fang, Xiaoling Sun
2015 conf
ISSI
Xianwen Wang, Zhichao Fang, Yang Yang
2015 J jnl
Scientometrics
Xianwen Wang, Chen Liu, Wenli Mao
2015 J jnl
Scientometrics
Xianwen Wang, Chen Liu, Wenli Mao, Zhichao Fang
2015 J jnl
CoRR
Xianwen Wang, Chen Liu, Wenli Mao, Zhichao Fang
2015 J jnl
Scientometrics
Xianwen Wang, Chen Liu, Wenli Mao, Zhichao Fang
2014 J jnl
Scientometrics
Chunjuan Luan, Haiyan Hou, Yongtao Wang, Xianwen Wang
2014 J jnl
CoRR
Xianwen Wang, Zhichao Fang, Yang Yang
2014 J jnl
CoRR
Xianwen Wang, Chen Liu, Wenli Mao
2014 J jnl
CoRR
Xianwen Wang, Chen Liu, Zhichao Fang, Wenli Mao
2014 J jnl
J. Informetrics
Xianwen Wang, Zhi Wang, Wenli Mao, Chen Liu
2014 J jnl
CoRR
Xianwen Wang, Shenmeng Xu, Zhi Wang, Lian Peng, Chuanli Wang
2014 J jnl
CoRR
Xianwen Wang, Shenmeng Xu, Di Liu, Yongxia Liang
2014 J jnl
Scientometrics
Xianwen Wang, Wenli Mao, Shenmeng Xu, Chunbo Zhang
2013 J jnl
CoRR
Xianwen Wang, Wenli Mao, Shenmeng Xu, Chunbo Zhang
2013 J jnl
Scientometrics
Xianwen Wang, Wenli Mao, Chuanli Wang, Lian Peng, Haiyan Hou
2013 J jnl
CoRR
Xianwen Wang, Wenli Mao
2013 J jnl
Scientometrics
Chunjuan Luan, Zeyuan Liu, Xianwen Wang
2013 J jnl
CoRR
Xianwen Wang, Lian Peng, Chunbo Zhang, Shenmeng Xu, Zhi Wang, Chuanli Wang, Xianbing Wang
2013 J jnl
J. Informetrics
Xianwen Wang, Lian Peng, Chunbo Zhang, Shenmeng Xu, Zhi Wang, Chuanli Wang, Xianbing Wang
2013 J jnl
CoRR
Xianwen Wang, Wenli Mao, Chen Liu
2013 J jnl
CoRR
Xianwen Wang, Zhi Wang, Wenli Mao
2013 J jnl
Scientometrics
Xianwen Wang, Shenmeng Xu, Zhi Wang, Lian Peng, Chuanli Wang
2013 J jnl
Scientometrics
Xianwen Wang, Zhi Wang, Shenmeng Xu
2012 J jnl
CoRR
Xianwen Wang, Shenmeng Xu, Lian Peng, Zhi Wang, Chuanli Wang, Chunbo Zhang, Xianbing Wang
2012 J jnl
J. Informetrics
Xianwen Wang, Shenmeng Xu, Lian Peng, Zhi Wang, Chuanli Wang, Chunbo Zhang, Xianbing Wang
2012 J jnl
Scientometrics
Xianwen Wang, Di Liu, Kun Ding, Xinran Wang
2012 J jnl
Scientometrics
Xianwen Wang, Shenmeng Xu, Di Liu, Yongxia Liang
2012 J jnl
CoRR
Xianwen Wang, Zhi Wang, Shenmeng Xu
2011 J jnl
Scientometrics
Xianwen Wang, Xi Zhang, Shenmeng Xu
redb/extractors/js_extractors/js_strings.py
← Index redb/extractors/js_extractors/js_strings.py python
import base64
import bisect
import inspect
import re
from datetime import datetime, timezone
from typing import Any

from redb.extractors.enum import Tag
from redb.extractors.js_extractor import JSExtractor
from redb.extractors.js_extractors.js_patterns import STRING_PATTERNS, line_offsets

# Local aliases for the compiled patterns this extractor uses. Defined and
# compiled exactly once in js_patterns.STRING_PATTERNS.
_HEX_STRING_RE = STRING_PATTERNS["hex_escape_seq"]
_UNICODE_STRING_RE = STRING_PATTERNS["unicode_escape_seq"]
_CHARCODE_RE = STRING_PATTERNS["charcode_call"]
_BASE64_STRING_RE = STRING_PATTERNS["base64_quoted"]
_CONCAT_STRING_RE = STRING_PATTERNS["concat_chain"]

# Tokeniser used inside _reconstruct_concat to pull each quoted part out of a
# matched concat chain. Compiled once at module load (was recompiled on every
# concat match before).
_CONCAT_TOKEN_RE = re.compile(r'["\']([^"\']*)["\']')


class JSStringsExtractor(JSExtractor):

    def __init__(
        self, filepath, log, exporters=None, index_prefix=None,
        known_benign=False, known_malicious=False, source=None, context=None,
    ):
        super().__init__(
            filepath, log, exporters, index_prefix,
            known_benign, known_malicious, source, context=context,
        )
        self.string_findings = None
        self.log.debug(inspect.currentframe().f_code.co_name)

    def tag(self):
        return Tag.JS_STRINGS.value

    def _decode_hex_string(self, hex_str):
        """Decode \\x41\\x42 style hex strings."""
        try:
            # Remove \\x prefix and decode
            clean = hex_str.replace('\\x', '')
            return bytes.fromhex(clean).decode('utf-8', errors='replace')
        except Exception:
            return None

    def _decode_unicode_string(self, uni_str):
        """Decode \\u0041\\u0042 style unicode strings."""
        try:
            return uni_str.encode('utf-8').decode('unicode_escape')
        except Exception:
            return None

    def _decode_charcode(self, charcode_str):
        """Decode String.fromCharCode(72, 101, 108, ...) sequences."""
        try:
            codes = [int(c.strip()) for c in charcode_str.split(',') if c.strip().isdigit()]
            return ''.join(chr(c) for c in codes if 0 <= c <= 0x10FFFF)
        except Exception:
            return None

    def _decode_base64(self, b64_str):
        """Attempt to decode base64 string."""
        try:
            decoded = base64.b64decode(b64_str)
            # Check if result is printable text
            text = decoded.decode('utf-8', errors='strict')
            # Only return if it looks like text (>80% printable)
            printable = sum(1 for c in text if c.isprintable() or c in '\n\r\t')
            if printable / len(text) > 0.8:
                return text
        except Exception:
            pass
        return None

    def _reconstruct_concat(self, concat_match):
        """Reconstruct concatenated string parts."""
        try:
            parts = _CONCAT_TOKEN_RE.findall(concat_match)
            return ''.join(parts)
        except Exception:
            return None

    def _find_line_number(self, match_start):
        """1-indexed line number for `match_start`, looked up in O(log L) via
        bisect over `self._line_offsets` (built once per extract() call).

        Replaces the historical `self.js_source[:match_start].count('\\n') + 1`
        which was O(N) per call and quadratic across all matches in a sample.
        """
        return bisect.bisect_right(self._line_offsets, match_start)

    def _scan_text(self, text):
        """Run every encoded-string pattern over `text` and return a list of
        finding dicts. Stateless apart from the per-call `_line_offsets` cache,
        which `_find_line_number` reads — callers must reset it before invoking
        this so line numbers reference the text being scanned, not the previous
        one.
        """
        findings = []

        # Hex-encoded strings
        for m in _HEX_STRING_RE.finditer(text):
            raw = m.group()
            decoded = self._decode_hex_string(raw)
            if decoded and len(decoded) >= 4:
                findings.append({
                    'string': decoded[:4000],
                    'string_raw': raw[:4000],
                    'string_encoding': 'hex',
                    'string_offset': self._find_line_number(m.start()),
                    'string_length': len(decoded),
                    'string_raw_length': len(raw),
                    'string_entropy': self._calculate_text_entropy(decoded),
                })

        # Unicode-encoded strings
        for m in _UNICODE_STRING_RE.finditer(text):
            raw = m.group()
            decoded = self._decode_unicode_string(raw)
            if decoded and len(decoded) >= 3:
                findings.append({
                    'string': decoded[:4000],
                    'string_raw': raw[:4000],
                    'string_encoding': 'unicode',
                    'string_offset': self._find_line_number(m.start()),
                    'string_length': len(decoded),
                    'string_raw_length': len(raw),
                    'string_entropy': self._calculate_text_entropy(decoded),
                })

        # String.fromCharCode sequences
        for m in _CHARCODE_RE.finditer(text):
            raw = m.group()
            decoded = self._decode_charcode(m.group(1))
            if decoded and len(decoded) >= 4:
                findings.append({
                    'string': decoded[:4000],
                    'string_raw': raw[:4000],
                    'string_encoding': 'charcode',
                    'string_offset': self._find_line_number(m.start()),
                    'string_length': len(decoded),
                    'string_raw_length': len(raw),
                    'string_entropy': self._calculate_text_entropy(decoded),
                })

        # Base64-encoded strings
        for m in _BASE64_STRING_RE.finditer(text):
            raw = m.group(0)
            b64_val = m.group(1)
            decoded = self._decode_base64(b64_val)
            if decoded and len(decoded) >= 10:
                findings.append({
                    'string': decoded[:4000],
                    'string_raw': raw[:4000],
                    'string_encoding': 'base64',
                    'string_offset': self._find_line_number(m.start()),
                    'string_length': len(decoded),
                    'string_raw_length': len(raw),
                    'string_entropy': self._calculate_text_entropy(decoded),
                })

        # Concatenated strings (reassembled)
        for m in _CONCAT_STRING_RE.finditer(text):
            raw = m.group()
            reconstructed = self._reconstruct_concat(raw)
            if reconstructed and len(reconstructed) >= 20:
                findings.append({
                    'string': reconstructed[:4000],
                    'string_raw': raw[:4000],
                    'string_encoding': 'concat',
                    'string_offset': self._find_line_number(m.start()),
                    'string_length': len(reconstructed),
                    'string_raw_length': len(raw),
                    'string_entropy': self._calculate_text_entropy(reconstructed),
                })

        return findings

    def extract(self):
        src = self.js_source
        if not src:
            return None

        # Pass 1: raw source. _line_offsets is keyed off whichever text is
        # currently being scanned so _find_line_number resolves to that text.
        self._line_offsets = line_offsets(src)
        findings = self._scan_text(src)

        # Pass 2: deobfuscated text, when the deobfuscator produced something
        # meaningfully different. Same patterns, but a different surface — for
        # samples where the encoded payload is hidden behind an outer wrapper
        # (e.g. array.join() + eval in Vjw0rm/WSH-RAT) only this pass yields
        # any rows at all.
        deobf_text, _ = self._context.deobfuscated
        if deobf_text and deobf_text != src:
            self._line_offsets = line_offsets(deobf_text)
            findings.extend(self._scan_text(deobf_text))

        if not findings:
            return None

        # Deduplicate by decoded string value (raw pass wins on collision: it
        # comes first in `findings`). A string that surfaces only in the
        # deobfuscated text still gets persisted, which is the whole point of
        # the second pass.
        seen_values = set()
        deduped = []
        for f in findings:
            val_key = f['string'][:100]
            if val_key not in seen_values:
                seen_values.add(val_key)
                deduped.append(f)

        self.string_findings = deduped[:500]  # Limit per file
        # Publish to the shared context so post-loop consumers (notably the IOC
        # plumbing in workers.py) can scrape the decoded strings without
        # holding a reference to this extractor instance.
        self._context.decoded_strings = self.string_findings
        return self.string_findings

    def prepare_export_data(self, exporter_type: str) -> Any:
        if exporter_type == "ClickHouseExporter":
            if not self.string_findings:
                return None

            data = []
            for f in self.string_findings:
                data.append([
                    self.sha256,
                    f['string'],
                    f['string_raw'],
                    f['string_encoding'],
                    f['string_offset'],
                    f['string_length'],
                    f['string_raw_length'],
                    f['string_entropy'],
                ])

            column_names = [
                "sha256",
                "string",
                "string_raw",
                "string_encoding",
                "string_offset",
                "string_length",
                "string_raw_length",
                "string_entropy",
            ]

            column_type_names = [
                "FixedString(64)",
                "String",
                "String",
                "LowCardinality(String)",
                "UInt64",
                "UInt32",
                "UInt32",
                "Float32",
            ]

            return (data, column_names, column_type_names)

    def get_clickhouse_table(self) -> str:
        return "code_binja_strings_raw"