Camillia Matuk

37 papers A* 2C 3Misc 3Journal 5Unranked 24
YearRankTypeTitle / Venue / Authors
2024 Misc conf
SAC
Sai Siddartha Maram, Anna Amato, Giovanni Maria Troiano, Steven C. Sutherland, Camillia Matuk, Edward F. Melcer, Elín Carstensdóttir, Casper Harteveld, Magy Seif El-Nasr
2024 conf
ITHET
María José Hernández-Serrano, Felicia Zerwas, Camillia Matuk, Girija Chatufale, Xavier Ochoa, Suzanne Dikker
2024 J jnl
CoRR
Luis Morales-Navarro, Yasmin B. Kafai, Ha Nguyen, Kayla DesPortes, Ralph Vacca, Camillia Matuk, Megan Silander, Anna Amato, Peter J. Woods, Francisco Castro, Mia S. Shaw, Selin Akgün, Christine Greenhow, Antero Garcia
2024 Misc conf
IDC
Camillia Matuk, Bernice d'Anjou, Pranali Mansukhani, Franck Porteous, Kim Burgas, Felicia Zerwas, Yury Shevchenko, Suzanne Dikker
2023 C conf
CoG
Steven C. Sutherland, Tiago Machado, Shruti Mahajan, Omid Mohaddesi, Camillia Matuk, Gillian Smith, Casper Harteveld
2023 J jnl
Proc. ACM Hum. Comput. Interact.
Graham Dove, Jack Shanley, Camillia Matuk, Oded Nov
2022 A* conf
CHI
Ralph Vacca, Kayla DesPortes, Marian Tes, Megan Silander, Camillia Matuk, Anna Amato, Peter J. Woods
2022 J jnl
Br. J. Educ. Technol.
Camillia Matuk, Kayla DesPortes, Anna Amato, Ralph Vacca, Megan Silander, Peter J. Woods, Marian Tes
2022 Misc conf
IDC
Ralph Vacca, Kayla DesPortes, Marian Tes, Megan Silander, Anna Amato, Camillia Matuk, Peter J. Woods
2020 conf
ICLS
Camillia Matuk, Susan A. Yoon, Joseph L. Polman, Anna Amato, Jacob Barton, Nicole Bulalacao, Francesco Cafaro, Lina Chopra Haldar, Amanda M. Cottone, Krista Cortes, Kayla DesPortes, Tim Erickson, William Finzer, Katie Headrick Taylor, Beth Herbel-Eisenmann, Cynthia Graville, Kris D. Gutiérrez, Traci Higgins, Blanca E. Himes, Kathryn A. Lanouette, Hollylynne Lee, Vivian Lim, M. Lisette Lopez, Leilah Lyons, Dan Milz, Maria C. Olivares, Elizabeth Osche, Tapan S. Parikh, Thomas M. Philip, Laurie Rubel, Joey Shelley, Edward Rivero, Jessica Roberts, Collette Roberto, Tony Petrosino, Andee Rubin, Jooeun Shim, Megan Silander, Stephen Sommer, David Stokes, Marian Tes, Milka Trajkova, Ryan J. Urbanowicz, Ralph Vacca, Sarah Van Wart, Veena Vasudevan, Michelle Hoda Wilkerson, Peter J. Woods
2020 conf
FabLearn
Anna Amato, Camillia Matuk, Talia A. Hurwich
2020 conf
ICLS
Talia A. Hurwich, Camillia Matuk, Christopher M. Hovey
2020 conf
ICLS
Anna Amato, Camillia Matuk, Dylan G. M. Schouten, Steven C. Sutherland, Gillian Smith, Casper Harteveld
2019 C conf
FDG
Nathan Partlan, Elín Carstensdóttir, Erica Kleinman, Sam Snodgrass, Casper Harteveld, Gillian Smith, Camillia Matuk, Steven C. Sutherland, Magy Seif El-Nasr
2019 conf
FabLearn
Camillia Matuk, Talia A. Hurwich, Anna Amato
2019 J jnl
Int. J. Comput. Support. Collab. Learn.
Camillia Matuk, Michael Tissenbaum, Bertrand Schneider
2019 conf
CSCL
Camillia Matuk, Wanjing Ma, Garima Sharma, Marcia C. Linn
2019 conf
FabLearn
Camillia Matuk, Frankie Tam, Jonathan Martinez
2018 conf
ICLS
Christopher M. Hovey, Camillia Matuk, Talia A. Hurwich
2018 conf
ICLS
Marcia C. Linn, Bat-Sheva Eylon, Adi Kidron, Libby F. Gerard, Emily Toutkoushian, Kihyun, Kristin Dana Bedell Bedell, Amanda Swearingen, Douglas B. Clark, Satyugjit Virk, Jackie Barnes, Deanne Adams, Hava Ben-Horin, Yael Kali, Tali Tal, Ornit Sagy, Alisa Acosta, Jim Slotta, Camillia Matuk, Christopher M. Hovey, Talia A. Hurwich, Juan Pablo Sarmiento, Jennifer L. Chiu, James P. Bywater, James Hong, Jonathan Osborne, Diana Laurillard
2018 J jnl
Int. J. Comput. Support. Collab. Learn.
Camillia Matuk, Marcia C. Linn
2017 conf
CSCL
Camillia Matuk, Jiayuan Zhang, Marcia C. Linn
2016 conf
FabLearn
Camillia Matuk, Rinat Levy-Cohen, Shashank Pawar
2016 conf
ICLS
Mike Tissenbaum, Camillia Matuk, Matthew Berland, Leilah Lyons, Felipe Cocco, Marcia C. Linn, Jan L. Plass, Nik Hajny, Al Olsen, Beat Schwendimann, Mina Shirvani Boroujeni, James D. Slotta, Jonathan M. Vitale, Libby F. Gerard, Pierre Dillenbourg
2016 conf
ICLS
Irina Uk, Camillia Matuk, Marcia C. Linn
2015 conf
CSCL
Camillia Matuk, Marcia C. Linn
2014 conf
ICLS
Camillia Matuk, Marcia C. Linn
2013 conf
CSCL (2)
Camillia Matuk, Kevin W. McElhaney, David Miller, Jennifer King Chen, Jonathan Lim-Breitbart, Hiroki Terashima, Geoffrey Kwan, Marcia C. Linn
2013 conf
CSCL (2)
Camillia Matuk
2012 conf
ICLS
Camillia Matuk, Kevin W. McElhaney, Jennifer King Chen, David Miller, Jonathan Lim-Breitbart, Marcia C. Linn
2012 conf
ICLS
Kevin W. McElhaney, Camillia Matuk, David Miller, Marcia C. Linn
2011 conf
CSCL
Camillia Matuk, Elissa Sato, Marcia C. Linn
2011 conf
CSCL
Marcia C. Linn, Ji Shen, Hsin-Yi Chang, Fang-Pei Hsieh, Beat Schwendimann, Camillia Matuk, Jennifer King Chen, Jennifer L. Chiu, Charles Xie, Baohui Zhang, Daner Sun, Karel Mous, Quee Boon Koh, Bahadia Namdar, Rutchelle Enriquez, Jing Lei, Heng Luo, Sunghye Lee, Hsin-Kai Wu
2010 conf
ICLS
Camillia Matuk, David H. Uttal
2008 C conf
Diagrams
Camillia Matuk
2008 A* conf
AAAI
Kenneth D. Forbus, Andrew M. Lovett, Kate Lockwood, Jon Wetzel, Camillia Matuk, Benjamin D. Jee, Jeffrey M. Usher
2008 conf
ICLS (3)
Camillia Matuk, David H. Uttal
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