module Ronin::Support::Encoding::JS

Contains methods for encoding/decoding escaping/unescaping JavaScript data.

## Core-Ext Methods

@api public

Constants

BACKSLASHED_CHARS

JavaScript characters that must be back-slashed.

ESCAPE_BYTES

Special JavaScript bytes and their escaped Strings.

Public Class Methods

decode(data) click to toggle source

Alias for {unescape}.

@param [String] data

The escaped JavaScript data.

@return [String]

The unescaped JavaScript String.

@see unescape

# File lib/ronin/support/encoding/js.rb, line 248
def self.decode(data)
  unescape(data)
end
encode(data) click to toggle source

JavaScript escapes every character of the String.

@param [String] data

The data to JavaScript escape.

@return [String]

The JavaScript escaped String.

@example

Encoding::JS.encode("hello")
# => "\\u0068\\u0065\\u006C\\u006C\\u006F"
# File lib/ronin/support/encoding/js.rb, line 221
def self.encode(data)
  encoded = String.new

  if data.valid_encoding?
    data.each_codepoint do |codepoint|
      encoded << encode_byte(codepoint)
    end
  else
    data.each_byte do |byte|
      encoded << encode_byte(byte)
    end
  end

  return encoded
end
encode_byte(byte) click to toggle source

Encodes the byte as a JavaScript character.

@param [Integer] byte

The byte to encode.

@return [String]

The encoded JavaScript character.

@example

Encoding::JS.encode_byte(0x41)
# => "\\x41"
# File lib/ronin/support/encoding/js.rb, line 124
def self.encode_byte(byte)
  if byte > 0xff then "\\u%.4X" % byte
  else                "\\x%.2X" % byte
  end
end
escape(data) click to toggle source

Escapes a String for JavaScript.

@param [String] data

The data to JavaScript escape.

@return [String]

The JavaScript escaped String.

@example

"hello\nworld\n".js_escape
# => "hello\\nworld\\n"
# File lib/ronin/support/encoding/js.rb, line 155
def self.escape(data)
  escaped = String.new

  if data.valid_encoding?
    data.each_codepoint do |codepoint|
      escaped << escape_byte(codepoint)
    end
  else
    data.each_byte do |byte|
      escaped << escape_byte(byte)
    end
  end

  return escaped
end
escape_byte(byte) click to toggle source

Escapes the byte as a JavaScript character.

@param [Integer] byte

The byte to escape.

@return [String]

The escaped JavaScript character.

@example

Encoding::JS.escape_byte(0x41)
# => "A"
Encoding::JS.escape_byte(0x22)
# => "\\\""
Encoding::JS.escape_byte(0x7f)
# => "\\x7F"
# File lib/ronin/support/encoding/js.rb, line 97
def self.escape_byte(byte)
  if byte >= 0x00 && byte <= 0xff
    ESCAPE_BYTES.fetch(byte) do
      if byte >= 0x20 && byte <= 0x7e
        byte.chr
      else
        encode_byte(byte)
      end
    end
  else
    encode_byte(byte)
  end
end
quote(data) click to toggle source

Converts the String into a JavaScript string.

@param [String] data

The data to escape and quote.

@return [String]

The unquoted and unescaped String.

@example

Encoding::JS.quote("hello\nworld\n")
# => "\"hello\\nworld\\n\""
# File lib/ronin/support/encoding/js.rb, line 265
def self.quote(data)
  "\"#{escape(data)}\""
end
unescape(data) click to toggle source

Unescapes a JavaScript escaped String.

@param [String] data

The escaped JavaScript data.

@return [String]

The unescaped JavaScript String.

@example

Encoding::JS.unescape("\\u0068\\u0065\\u006C\\u006C\\u006F world")
# => "hello world"
# File lib/ronin/support/encoding/js.rb, line 184
def self.unescape(data)
  unescaped = String.new(encoding: Encoding::UTF_8)
  scanner   = StringScanner.new(data)

  until scanner.eos?
    unescaped << if (backslash_escape = scanner.scan(/\\[btnfr'"\\]/))
                   BACKSLASHED_CHARS[backslash_escape]
                 elsif (surrogate_pair = scanner.scan(/\\u[dD][890abAB][0-9a-fA-F]{2}\\u[dD][cdefCDEF][0-9a-fA-F]{2}/))
                   hi = surrogate_pair[2..6].to_i(16)
                   lo = surrogate_pair[8..12].to_i(16)

                   (0x1_0000 + ((hi - 0xd800) * 0x400) + (lo - 0xdc00))
                 elsif (unicode_escape = scanner.scan(/[\\%]u[0-9a-fA-F]{1,4}/))
                   unicode_escape[2..].to_i(16)
                 elsif (hex_escape     = scanner.scan(/[\\%][0-9a-fA-F]{1,2}/))
                   hex_escape[1..].to_i(16)
                 else
                   scanner.getch
                 end
  end

  return unescaped
end
unquote(data) click to toggle source

Removes the quotes an unescapes a JavaScript string.

@param [String] data

The JavaScript string to unquote.

@return [String]

The un-quoted String if the String begins and ends with quotes, or
the same String if it is not quoted.

@example

Encoding::JS.unquote("\"hello\\nworld\"")
# => "hello\nworld"
# File lib/ronin/support/encoding/js.rb, line 283
def self.unquote(data)
  if ((data[0] == '"' && data[-1] == '"') ||
      (data[0] == "'" && data[-1] == "'"))
    unescape(data[1..-2])
  else
    data
  end
end