Neil Ghani

90 papers A* 7A 3B 17C 1Misc 1Journal 29Unranked 31
YearRankTypeTitle / Venue / Authors
2025 J jnl
CoRR
Owen Lewis, Neil Ghani, Andrew Dudzik, Christos Perivolaropoulos, Razvan Pascanu, Petar Velickovic
2025 conf
GandALF
Radu Mardare, Neil Ghani, Eigil Fjeldgren Rischel
2024 conf
ACT
Neil Ghani
2024 J jnl
CoRR
Matteo Capucci, Geoffrey S. H. Cruttwell, Neil Ghani, Fabio Zanasi
2024 J jnl
CoRR
Geoffrey S. H. Cruttwell, Bruno Gavranovic, Neil Ghani, Paul W. Wilson, Fabio Zanasi
2022 A conf
ESOP
Geoffrey S. H. Cruttwell, Bruno Gavranovic, Neil Ghani, Paul W. Wilson, Fabio Zanasi
2021 J jnl
CoRR
Geoff S. H. Cruttwell, Bruno Gavranovic, Neil Ghani, Paul W. Wilson, Fabio Zanasi
2021 conf
ACT
Matteo Capucci, Neil Ghani, Jérémy Ledent, Fredrik Nordvall Forsberg
2020 conf
ACT
Robert Atkey, Bruno Gavranovic, Neil Ghani, Clemens Kupke, Jérémy Ledent, Fredrik Nordvall Forsberg
2020 B conf
CPP
Fredrik Nordvall Forsberg, Chuangjie Xu, Neil Ghani
2019 conf
ACT
Neil Ghani, Clemens Kupke, Alasdair Lambert, Fredrik Nordvall Forsberg
2019 J jnl
Math. Struct. Comput. Sci.
Neil Ghani, Fredrik Nordvall Forsberg, Federico Orsanigo
2018 J jnl
Theor. Comput. Sci.
Neil Ghani, Clemens Kupke, Alasdair Lambert, Fredrik Nordvall Forsberg
2018 A* conf
LICS
Neil Ghani, Jules Hedges, Viktor Winschel, Philipp Zahn
2017 J jnl
CoRR
Neil Ghani, Clemens Kupke, Alasdair Lambert, Fredrik Nordvall Forsberg
2017 conf
EvoApplications (2)
Jerry Swan, Krzysztof Krawiec, Neil Ghani
2017 B conf
MFCS
Neil Ghani, Conor McBride, Fredrik Nordvall Forsberg, Stephan Spahn
2016 J jnl
CoRR
Neil Ghani, Jules Hedges
2016 B conf
FoSSaCS
Neil Ghani, Fredrik Nordvall Forsberg, Alex Simpson
2016 J jnl
Math. Struct. Comput. Sci.
Neil Ghani, Peter G. Hancock
2016 B conf
FoSSaCS
Danel Ahman, Neil Ghani, Gordon D. Plotkin
2016 conf
A List of Successes That Can Change the World
Neil Ghani, Fredrik Nordvall Forsberg, Federico Orsanigo
2015 B conf
MFPS
Neil Ghani, Patricia Johann, Fredrik Nordvall Forsberg, Federico Orsanigo, Tim Revell
2015 J jnl
J. Funct. Program.
Thorsten Altenkirch, Neil Ghani, Peter G. Hancock, Conor McBride, Peter Morris
2015 conf
TLCA
Robert Atkey, Neil Ghani, Fredrik Nordvall Forsberg, Timothy Revell, Sam Staton
2015 C conf
WoLLIC
Neil Ghani, Fredrik Nordvall Forsberg, Federico Orsanigo
2015 J jnl
Log. Methods Comput. Sci.
Neil Ghani, Lorenzo Malatesta, Fredrik Nordvall Forsberg
2014 A* conf
POPL
Robert Atkey, Neil Ghani, Patricia Johann
2013 A* conf
LICS
Neil Ghani, Lorenzo Malatesta, Fredrik Nordvall Forsberg, Anton Setzer
2013 J jnl
Log. Methods Comput. Sci.
Neil Ghani, Patricia Johann, Clément Fumex
2013 B conf
CALCO
Neil Ghani, Lorenzo Malatesta, Fredrik Nordvall Forsberg
2013 conf
TLCA
Peter G. Hancock, Conor McBride, Neil Ghani, Lorenzo Malatesta, Thorsten Altenkirch
2012 B conf
FoSSaCS
Robert Atkey, Neil Ghani, Bart Jacobs, Patricia Johann
2012 J jnl
Log. Methods Comput. Sci.
Neil Ghani, Patricia Johann, Clément Fumex
2012 J jnl
Log. Methods Comput. Sci.
Robert Atkey, Patricia Johann, Neil Ghani
2011 B conf
CALCO
Clément Fumex, Neil Ghani, Patricia Johann
2011 B conf
FoSSaCS
Robert Atkey, Patricia Johann, Neil Ghani
2010 B conf
CSL
Neil Ghani, Patricia Johann, Clément Fumex
2009 J jnl
Int. J. Found. Comput. Sci.
Peter Morris, Thorsten Altenkirch, Neil Ghani
2009 J jnl
High. Order Symb. Comput.
Patricia Johann, Neil Ghani
2009 B conf
MFPS
Neil Ghani, Peter G. Hancock, Dirk Pattinson
2009 J jnl
Log. Methods Comput. Sci.
Neil Ghani, Peter G. Hancock, Dirk Pattinson
2008 A* conf
POPL
Patricia Johann, Neil Ghani
2008 conf
MSFP@ICALP
Mauro Jaskelioff, Neil Ghani, Graham Hutton
2008 Misc conf
FLOPS
Rawle C. S. Prince, Neil Ghani, Conor McBride
2008 conf
Trends in Functional Programming
Neil Ghani, Patricia Johann
2007 conf
CATS
Peter Morris, Thorsten Altenkirch, Neil Ghani
2007 B conf
CALCO
Neil Ghani, Alexander Kurz
2007 conf
TLCA
Patricia Johann, Neil Ghani
2007 J jnl
J. Funct. Program.
Neil Ghani, Patricia Johann
2006 conf
CMCS
Neil Ghani, Peter G. Hancock, Dirk Pattinson
2006 J jnl
High. Order Symb. Comput.
Neil Ghani, Tarmo Uustalu, Makoto Hamana
2006 conf
CMCS
Neil Ghani, John Power
2006 ed.
CMCS
Neil Ghani, John Power
2006 J jnl
J. Symb. Comput.
Ronald Brown, Neil Ghani, Anne Heyworth, Christopher D. Wensley
2005 conf
RTA
Michael Gordon Abbott, Neil Ghani, Christoph Lüth
2005 J jnl
Theor. Comput. Sci.
Michael Gordon Abbott, Thorsten Altenkirch, Neil Ghani
2005 A conf
ICFP
Neil Ghani, Patricia Johann, Tarmo Uustalu, Varmo Vene
2005 J jnl
Math. Struct. Comput. Sci.
Neil Ghani, Christoph Lüth, Federico De Marchi
2005 J jnl
Fundam. Informaticae
Michael Gordon Abbott, Thorsten Altenkirch, Conor McBride, Neil Ghani
2004 B conf
APLAS
Neil Ghani, Tarmo Uustalu, Varmo Vene
2004 B conf
MPC
Michael Gordon Abbott, Thorsten Altenkirch, Neil Ghani, Conor McBride
2004 J jnl
RAIRO Theor. Informatics Appl.
Neil Ghani, Tarmo Uustalu
2004 conf
WOOD
Johan Glimming, Neil Ghani
2004 conf
Trends in Functional Programming
Neil Ghani, Tarmo Uustalu, Varmo Vene
2004 conf
CMCS
Neil Ghani, Kidane Yemane, Björn Victor
2004 A* conf
ICALP
Michael Gordon Abbott, Thorsten Altenkirch, Neil Ghani
2003 conf
RTA
Neil Ghani, Anne Heyworth
2003 B conf
FoSSaCS
Michael Gordon Abbott, Thorsten Altenkirch, Neil Ghani
2003 conf
FICS
Neil Ghani, Tarmo Uustalu
2003 conf
TLCA
Michael Gordon Abbott, Thorsten Altenkirch, Neil Ghani, Conor McBride
2003 J jnl
Math. Struct. Comput. Sci.
Neil Ghani, Christoph Lüth, Federico De Marchi, John Power
2003 conf
MERLIN
Neil Ghani, Tarmo Uustalu
2003 J jnl
Nord. J. Comput.
Neil Ghani, Christoph Lüth
2003 J jnl
RAIRO Theor. Informatics Appl.
Federico De Marchi, Neil Ghani, Christoph Lüth
2002 conf
CMCS
Neil Ghani, Christoph Lüth, Federico De Marchi
2002 conf
FICS
Neil Ghani, Christoph Lüth, Federico De Marchi
2002 A conf
ICFP
Christoph Lüth, Neil Ghani
2002 conf
CATS
Neil Ghani, Anne Heyworth
2002 conf
FroCoS
Christoph Lüth, Neil Ghani
2001 conf
CMCS
Neil Ghani, Christoph Lüth, Federico De Marchi, John Power
2000 J jnl
Log. J. IGPL
Neil Ghani, Valeria de Paiva, Eike Ritter
1999 B conf
FoSSaCS
Neil Ghani, Valeria de Paiva, Eike Ritter
1998 A* conf
ICALP
Neil Ghani, Valeria de Paiva, Eike Ritter
1997 conf
TLCA
Neil Ghani
1997 conf
Category Theory and Computer Science
Christoph Lüth, Neil Ghani
1997 A* conf
ICALP
Roberto Di Cosmo, Neil Ghani
1996 B conf
CSL
Neil Ghani
1995 J jnl
J. Funct. Program.
C. Barry Jay, Neil Ghani
1995 conf
TLCA
Neil Ghani
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