summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/psych/parser.c14
-rw-r--r--test/psych/test_tainted.rb128
2 files changed, 142 insertions, 0 deletions
diff --git a/ext/psych/parser.c b/ext/psych/parser.c
index f075105..8e7df95 100644
--- a/ext/psych/parser.c
+++ b/ext/psych/parser.c
@@ -73,6 +73,7 @@ static VALUE parse(VALUE self, VALUE yaml)
yaml_parser_t * parser;
yaml_event_t event;
int done = 0;
+ int tainted = 0;
#ifdef HAVE_RUBY_ENCODING_H
int encoding = rb_utf8_encindex();
rb_encoding * internal_enc = rb_default_internal_encoding();
@@ -81,8 +82,11 @@ static VALUE parse(VALUE self, VALUE yaml)
Data_Get_Struct(self, yaml_parser_t, parser);
+ if (OBJ_TAINTED(yaml)) tainted = 1;
+
if(rb_respond_to(yaml, id_read)) {
yaml_parser_set_input(parser, io_reader, (void *)yaml);
+ if (RTEST(rb_obj_is_kind_of(yaml, rb_cIO))) tainted = 1;
} else {
StringValue(yaml);
yaml_parser_set_input_string(
@@ -140,6 +144,7 @@ static VALUE parse(VALUE self, VALUE yaml)
VALUE prefix = Qnil;
if(start->handle) {
handle = rb_str_new2((const char *)start->handle);
+ if (tainted) OBJ_TAINT(handle);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(handle, encoding, internal_enc);
#endif
@@ -147,6 +152,7 @@ static VALUE parse(VALUE self, VALUE yaml)
if(start->prefix) {
prefix = rb_str_new2((const char *)start->prefix);
+ if (tainted) OBJ_TAINT(prefix);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(prefix, encoding, internal_enc);
#endif
@@ -171,6 +177,7 @@ static VALUE parse(VALUE self, VALUE yaml)
VALUE alias = Qnil;
if(event.data.alias.anchor) {
alias = rb_str_new2((const char *)event.data.alias.anchor);
+ if (tainted) OBJ_TAINT(alias);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(alias, encoding, internal_enc);
#endif
@@ -188,6 +195,7 @@ static VALUE parse(VALUE self, VALUE yaml)
(const char *)event.data.scalar.value,
(long)event.data.scalar.length
);
+ if (tainted) OBJ_TAINT(val);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(val, encoding, internal_enc);
@@ -195,6 +203,7 @@ static VALUE parse(VALUE self, VALUE yaml)
if(event.data.scalar.anchor) {
anchor = rb_str_new2((const char *)event.data.scalar.anchor);
+ if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
@@ -202,6 +211,7 @@ static VALUE parse(VALUE self, VALUE yaml)
if(event.data.scalar.tag) {
tag = rb_str_new2((const char *)event.data.scalar.tag);
+ if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
@@ -226,6 +236,7 @@ static VALUE parse(VALUE self, VALUE yaml)
VALUE implicit, style;
if(event.data.sequence_start.anchor) {
anchor = rb_str_new2((const char *)event.data.sequence_start.anchor);
+ if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
@@ -234,6 +245,7 @@ static VALUE parse(VALUE self, VALUE yaml)
tag = Qnil;
if(event.data.sequence_start.tag) {
tag = rb_str_new2((const char *)event.data.sequence_start.tag);
+ if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
@@ -258,6 +270,7 @@ static VALUE parse(VALUE self, VALUE yaml)
VALUE implicit, style;
if(event.data.mapping_start.anchor) {
anchor = rb_str_new2((const char *)event.data.mapping_start.anchor);
+ if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
@@ -265,6 +278,7 @@ static VALUE parse(VALUE self, VALUE yaml)
if(event.data.mapping_start.tag) {
tag = rb_str_new2((const char *)event.data.mapping_start.tag);
+ if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
diff --git a/test/psych/test_tainted.rb b/test/psych/test_tainted.rb
new file mode 100644
index 0000000..bf55d3b
--- /dev/null
+++ b/test/psych/test_tainted.rb
@@ -0,0 +1,128 @@
+require 'psych/helper'
+
+module Psych
+ class TestStringTainted < TestCase
+ class Tainted < Handler
+ attr_reader :tc
+
+ def initialize tc
+ @tc = tc
+ end
+
+ def start_document version, tags, implicit
+ tags.flatten.each do |tag|
+ assert_taintedness tag
+ end
+ end
+
+ def alias name
+ assert_taintedness name
+ end
+
+ def scalar value, anchor, tag, plain, quoted, style
+ assert_taintedness value
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def start_sequence anchor, tag, implicit, style
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def start_mapping anchor, tag, implicit, style
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def assert_taintedness thing, message = "'#{thing}' should be tainted"
+ tc.assert thing.tainted?, message
+ end
+ end
+
+ class Untainted < Tainted
+ def assert_taintedness thing, message = "'#{thing}' should not be tainted"
+ tc.assert !thing.tainted?, message
+ end
+ end
+
+
+ def setup
+ handler = Tainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def test_tags_are_tainted
+ assert_taintedness "%TAG !yaml! tag:yaml.org,2002:\n---\n!yaml!str \"foo\""
+ end
+
+ def test_alias
+ assert_taintedness "--- &ponies\n- foo\n- *ponies"
+ end
+
+ def test_scalar
+ assert_taintedness "--- ponies"
+ end
+
+ def test_anchor
+ assert_taintedness "--- &hi ponies"
+ end
+
+ def test_scalar_tag
+ assert_taintedness "--- !str ponies"
+ end
+
+ def test_seq_start_tag
+ assert_taintedness "--- !!seq [ a ]"
+ end
+
+ def test_seq_start_anchor
+ assert_taintedness "--- &zomg [ a ]"
+ end
+
+ def test_seq_mapping_tag
+ assert_taintedness "--- !!map { a: b }"
+ end
+
+ def test_seq_mapping_anchor
+ assert_taintedness "--- &himom { a: b }"
+ end
+
+ def assert_taintedness string
+ @parser.parse string.taint
+ end
+ end
+
+ class TestStringUntainted < TestStringTainted
+ def setup
+ handler = Untainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def assert_taintedness string
+ @parser.parse string
+ end
+ end
+
+ class TestStringIOUntainted < TestStringTainted
+ def setup
+ handler = Untainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def assert_taintedness string
+ @parser.parse StringIO.new(string)
+ end
+ end
+
+ class TestIOTainted < TestStringTainted
+ def assert_taintedness string
+ t = Tempfile.new(['something', 'yml'])
+ t.binmode
+ t.write string
+ t.close
+ File.open(t.path) { |f| @parser.parse f }
+ t.close(true)
+ end
+ end
+end