Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement :escape_mode => :slash #777

Merged
merged 1 commit into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions ext/oj/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static const char nan_val[] = NAN_VAL;
typedef unsigned long ulong;

static size_t hibit_friendly_size(const uint8_t *str, size_t len);
static size_t slash_friendly_size(const uint8_t *str, size_t len);
static size_t xss_friendly_size(const uint8_t *str, size_t len);
static size_t ascii_friendly_size(const uint8_t *str, size_t len);

Expand Down Expand Up @@ -59,6 +60,17 @@ static char hibit_friendly_chars[256] = "\
11111111111111111111111111111111\
11111111111111111111111111111111";

// JSON standard but escape forward slashes `/`
static char slash_friendly_chars[256] = "\
66666666222622666666666666666666\
11211111111111121111111111111111\
11111111111111111111111111112111\
11111111111111111111111111111111\
11111111111111111111111111111111\
11111111111111111111111111111111\
11111111111111111111111111111111\
11111111111111111111111111111111";

// High bit set characters are always encoded as unicode. Worse case is 3
// bytes per character in the output. That makes this conservative.
static char ascii_friendly_chars[256] = "\
Expand Down Expand Up @@ -143,6 +155,10 @@ inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
return calculate_string_size(str, len, hibit_friendly_chars);
}

inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
return calculate_string_size(str, len, slash_friendly_chars);
}

inline static size_t ascii_friendly_size(const uint8_t *str, size_t len) {
return calculate_string_size(str, len, ascii_friendly_chars);
}
Expand Down Expand Up @@ -756,6 +772,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
cmap = ascii_friendly_chars;
size = ascii_friendly_size((uint8_t *)str, cnt);
break;
case SlashEsc:
has_hi = true;
cmap = slash_friendly_chars;
size = slash_friendly_size((uint8_t *)str, cnt);
break;
case XSSEsc:
cmap = xss_friendly_chars;
size = xss_friendly_size((uint8_t *)str, cnt);
Expand Down
6 changes: 6 additions & 0 deletions ext/oj/oj.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ static VALUE rails_sym;
static VALUE raise_sym;
static VALUE ruby_sym;
static VALUE sec_prec_sym;
static VALUE slash_sym;
static VALUE strict_sym;
static VALUE symbol_keys_sym;
static VALUE time_format_sym;
Expand Down Expand Up @@ -405,6 +406,7 @@ static VALUE get_def_opts(VALUE self) {
switch (oj_default_options.escape_mode) {
case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
case SlashEsc: rb_hash_aset(opts, escape_mode_sym, slash_sym); break;
case XSSEsc: rb_hash_aset(opts, escape_mode_sym, xss_safe_sym); break;
case ASCIIEsc: rb_hash_aset(opts, escape_mode_sym, ascii_sym); break;
case JXEsc: rb_hash_aset(opts, escape_mode_sym, unicode_xss_sym); break;
Expand Down Expand Up @@ -734,6 +736,8 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
copts->escape_mode = NLEsc;
} else if (json_sym == v) {
copts->escape_mode = JSONEsc;
} else if (slash_sym == v) {
copts->escape_mode = SlashEsc;
} else if (xss_safe_sym == v) {
copts->escape_mode = XSSEsc;
} else if (ascii_sym == v) {
Expand Down Expand Up @@ -1987,6 +1991,8 @@ void Init_oj(void) {
rb_gc_register_address(&ruby_sym);
sec_prec_sym = ID2SYM(rb_intern("second_precision"));
rb_gc_register_address(&sec_prec_sym);
slash_sym = ID2SYM(rb_intern("slash"));
rb_gc_register_address(&slash_sym);
strict_sym = ID2SYM(rb_intern("strict"));
rb_gc_register_address(&strict_sym);
symbol_keys_sym = ID2SYM(rb_intern("symbol_keys"));
Expand Down
1 change: 1 addition & 0 deletions ext/oj/oj.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ typedef enum { UnixTime = 'u', UnixZTime = 'z', XmlTime = 'x', RubyTime = 'r' }
typedef enum {
NLEsc = 'n',
JSONEsc = 'j',
SlashEsc = 's',
XSSEsc = 'x',
ASCIIEsc = 'a',
JXEsc = 'g', // json gem
Expand Down
6 changes: 6 additions & 0 deletions test/test_various.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,12 @@ def test_escapes_entities_by_default_when_configured_to_do_so
out = Oj.dump hash
assert_equal(%{{"key":"I \\u003c3 this"}}, out)
end
def test_escapes_slashes_by_default_when_configured_to_do_so
hash = {'key' => "I <3 this </script>"}
Oj.default_options = {:escape_mode => :slash}
out = Oj.dump hash
assert_equal(%{{"key":"I <3 this <\\/script>"}}, out)
end
def test_escapes_entities_when_asked_to
hash = {'key' => "I <3 this"}
out = Oj.dump(hash, :escape_mode => :xss_safe)
Expand Down