Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: yahoo/serialize-javascript
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v7.1.1
Choose a base ref
...
head repository: yahoo/serialize-javascript
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v7.1.2
Choose a head ref
  • 2 commits
  • 4 files changed
  • 2 contributors

Commits on Sep 23, 2026

  1. Merge commit from fork

    * fix: prevent script close tag from being swallowed by a single match
    
    `SCRIPT_CLOSE_REGEXP` matched `<\/script[^>]*>`, whose wildcard could run
    from one `</script` to the next `>` anywhere in the function source. When a
    second, complete `</script>` fell inside that span, the whole thing collapsed
    into one match, and since the plain-code branch neutralizes only the leading
    `<`, the swallowed tag was re-emitted verbatim.
    
    A function body reaches that shape whenever `</script` appears in code
    position -- `x</script=+/` parses as `x < /script=+/`, a comparison against a
    regex literal -- followed by a `</script>` in a later string. The serialized
    output then carries a live `</script>`, which terminates the script element
    when embedded the way the README documents, so the rest of the payload is
    parsed as HTML. Confirmed in headless Chromium: the injected `onerror` runs.
    
    This regressed in v7.1.0. v7.0.7 used the same wildcard but escaped the
    entire match, so nothing survived.
    
    Excluding `<` from the character class fixes it: a match can no longer reach
    past a second `<`, so every `</script` in the source either starts its own
    match or is followed by a non-delimiter -- and the HTML tokenizer only ends
    the tag name on TAB, LF, FF, CR, SPACE, `/` or `>`, emitting anything else as
    text.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * fix: reject spoofed function toString() and make native-code check stateless
    
    Two defects in `serializeFunc`, found while investigating the script close
    tag escaping.
    
    `fn.toString()` was trusted to return a string. It is attacker-controlled in
    the same way `URL.prototype.toString` and `RegExp.prototype.source` were
    before they were hardened. Returning an object with its own `replace()` is
    enough to defeat the escaping outright, because `escapeFunctionBody()` is
    built entirely from `str.replace(...)` calls -- the object's `replace` simply
    returns itself, and the payload reaches the output untouched:
    
        var f = function () {};
        f.toString = () => ({
            replace: function () { return this; },
            toString: () => 'function(){}</script><img src=x onerror=alert(1)>'
        });
        serialize({ f: f });
        // {"f":function(){}</script><img src=x onerror=alert(1)>}
    
    A primitive string from a spoofed `toString()` was already safe; only the
    non-string case bypasses escaping. Rejecting it matches how the URL and
    RegExp spoofing cases are already handled.
    
    Separately, `IS_NATIVE_CODE_REGEXP` carried a `/g` flag. `.test()` on a
    global regexp advances `lastIndex`, so after one rejection the next call
    began its scan past the `[native code]` match and returned false, letting
    every other native function through:
    
        try { serialize(Math.max); } catch (e) {}  // correctly throws
        serialize(Math.min);                       // 'function min() { [native code] }'
    
    That output is a syntax error rather than an injection, so the impact is
    limited to an unreliable guard, but the flag serves no purpose here.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5 <[email protected]>
    redonkulus and claude authored Sep 23, 2026
    Configuration menu
    Copy the full SHA
    2bdbaaf View commit details
    Browse the repository at this point in the history
  2. release: v7.1.2

    redonkulus committed Sep 23, 2026
    Configuration menu
    Copy the full SHA
    6a73fa1 View commit details
    Browse the repository at this point in the history
Loading