Skip to content

Commit

Permalink
feat: Add support for configuration of XSLT security
Browse files Browse the repository at this point in the history
  • Loading branch information
maths22 committed May 23, 2023
1 parent 18d4de4 commit d231858
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 0 deletions.
18 changes: 18 additions & 0 deletions ext/java/nokogiri/XsltStylesheet.java
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,22 @@ public class XsltStylesheet extends RubyObject
if (arg instanceof XmlDocument) { return; }
throw runtime.newArgumentError("argument must be a Nokogiri::XML::Document");
}

@JRubyMethod(meta = true, rest = true, name = "default_security_prefs")
public static IRubyObject
get_default_security_options(ThreadContext context, IRubyObject klazz, IRubyObject[] args)
{
// This method is not supported because the Java XML backend does not support the
// security controls supported by the libxml backend
throw context.getRuntime().newNotImplementedError("Nokogiri::XSLT::Stylesheet.default_security_prefs method is not implemented");
}

@JRubyMethod(meta = true, rest = true, name = "default_security_prefs=")
public static IRubyObject
set_default_security_options(ThreadContext context, IRubyObject klazz, IRubyObject[] args)
{
// This method is not supported because the Java XML backend does not support the
// security controls supported by the libxml backend
throw context.getRuntime().newNotImplementedError("Nokogiri::XSLT::Stylesheet.default_security_prefs= method is not implemented");
}
}
1 change: 1 addition & 0 deletions ext/nokogiri/nokogiri.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <libxslt/extensions.h>
#include <libxslt/xsltconfig.h>
#include <libxslt/xsltutils.h>
#include <libxslt/security.h>
#include <libxslt/transform.h>
#include <libxslt/imports.h>
#include <libxslt/xsltInternals.h>
Expand Down
50 changes: 50 additions & 0 deletions ext/nokogiri/xslt_stylesheet.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <nokogiri.h>

VALUE cNokogiriXsltStylesheet ;
VALUE mNokogiriXsltSecurity ;
VALUE cNokogiriXsltSecurityConfig ;

static void
mark(void *data)
Expand Down Expand Up @@ -400,17 +402,65 @@ rb_xslt_s_register(VALUE self, VALUE uri, VALUE obj)
return self;
}

static void
add_sec_option(xsltSecurityPrefsPtr xsltPrefs, int option, VALUE val)
{
if (val == Qtrue) {
xsltSetSecurityPrefs(xsltPrefs, option, xsltSecurityAllow);
} else if (val == Qfalse) {
xsltSetSecurityPrefs(xsltPrefs, option, xsltSecurityForbid);
}
}

static VALUE
rb_set_default_security_options(VALUE self, VALUE options)
{
Check_Type(options, T_OBJECT);
xsltSecurityPrefsPtr oldDefaults = xsltGetDefaultSecurityPrefs();
xsltSecurityPrefsPtr xsltPrefs = xsltNewSecurityPrefs();
add_sec_option(xsltPrefs, XSLT_SECPREF_READ_FILE, rb_iv_get(options, "@allow_read_file"));
add_sec_option(xsltPrefs, XSLT_SECPREF_WRITE_FILE, rb_iv_get(options, "@allow_write_file"));
add_sec_option(xsltPrefs, XSLT_SECPREF_CREATE_DIRECTORY, rb_iv_get(options, "@allow_create_directory"));
add_sec_option(xsltPrefs, XSLT_SECPREF_READ_NETWORK, rb_iv_get(options, "@allow_read_network"));
add_sec_option(xsltPrefs, XSLT_SECPREF_WRITE_NETWORK, rb_iv_get(options, "@allow_write_network"));
xsltSetDefaultSecurityPrefs(xsltPrefs);
if(oldDefaults) {
xsltFreeSecurityPrefs(oldDefaults);
}
return Qnil;
}

static VALUE
rb_get_default_security_options(VALUE self)
{
VALUE prefs = rb_funcall(cNokogiriXsltSecurityConfig, rb_intern("new"), 0);
xsltSecurityPrefsPtr xsltPrefs = xsltGetDefaultSecurityPrefs();
if(xsltPrefs == NULL) {
return prefs;
}
rb_iv_set(prefs, "@allow_read_file", xsltGetSecurityPrefs(xsltPrefs, XSLT_SECPREF_READ_FILE) == xsltSecurityAllow ? Qtrue : Qfalse);
rb_iv_set(prefs, "@allow_write_file", xsltGetSecurityPrefs(xsltPrefs, XSLT_SECPREF_WRITE_FILE) == xsltSecurityAllow ? Qtrue : Qfalse);
rb_iv_set(prefs, "@allow_create_directory", xsltGetSecurityPrefs(xsltPrefs, XSLT_SECPREF_CREATE_DIRECTORY) == xsltSecurityAllow ? Qtrue : Qfalse);
rb_iv_set(prefs, "@allow_read_network", xsltGetSecurityPrefs(xsltPrefs, XSLT_SECPREF_READ_NETWORK) == xsltSecurityAllow ? Qtrue : Qfalse);
rb_iv_set(prefs, "@allow_write_network", xsltGetSecurityPrefs(xsltPrefs, XSLT_SECPREF_WRITE_NETWORK) == xsltSecurityAllow ? Qtrue : Qfalse);
return prefs;
}

void
noko_init_xslt_stylesheet(void)
{
rb_define_singleton_method(mNokogiriXslt, "register", rb_xslt_s_register, 2);
rb_iv_set(mNokogiriXslt, "@modules", rb_hash_new());

cNokogiriXsltStylesheet = rb_define_class_under(mNokogiriXslt, "Stylesheet", rb_cObject);
mNokogiriXsltSecurity = rb_define_module_under(mNokogiriXslt, "Security");
cNokogiriXsltSecurityConfig = rb_define_class_under(mNokogiriXsltSecurity, "Config", rb_cObject);

rb_undef_alloc_func(cNokogiriXsltStylesheet);

rb_define_singleton_method(cNokogiriXsltStylesheet, "parse_stylesheet_doc", parse_stylesheet_doc, 1);
rb_define_singleton_method(cNokogiriXsltStylesheet, "default_security_options", rb_get_default_security_options, 0);
rb_define_singleton_method(cNokogiriXsltStylesheet, "default_security_options=", rb_set_default_security_options, 1);
rb_define_method(cNokogiriXsltStylesheet, "serialize", rb_xslt_stylesheet_serialize, 1);
rb_define_method(cNokogiriXsltStylesheet, "transform", rb_xslt_stylesheet_transform, -1);
}
18 changes: 18 additions & 0 deletions lib/nokogiri/xslt.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# coding: utf-8
# frozen_string_literal: true

require_relative "xslt/security"

module Nokogiri
class << self
###
Expand All @@ -19,6 +21,8 @@ def XSLT(stylesheet, modules = {})
# See Nokogiri::XSLT::Stylesheet for creating and manipulating
# Stylesheet object.
module XSLT
include Nokogiri::XSLT::Security

class << self
# :call-seq:
# parse(xsl) → Nokogiri::XSLT::Stylesheet
Expand Down Expand Up @@ -86,6 +90,20 @@ def parse(string, modules = {})
end
end

###
# Get the default security options used by libxslt
# [Returns] an object of type Nokogiri::XSLT::Security::Config
def default_security_options
Stylesheet.default_security_options
end

###
# Set the default security options used by libxslt
# +options+ should be an object of type Nokogiri::XSLT::Security::Config
def default_security_options=(options)
Stylesheet.default_security_options=(options)
end

# :call-seq:
# quote_params(params) → Array
#
Expand Down
24 changes: 24 additions & 0 deletions lib/nokogiri/xslt/security.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Nokogiri
module XSLT
module Security
class Config
attr_accessor :allow_read_file
attr_accessor :allow_write_file
attr_accessor :allow_create_directory
attr_accessor :allow_read_network
attr_accessor :allow_write_network

# Mirror xslt (implicit) internal defaults
def initialize
@allow_read_file = true
@allow_write_file = true
@allow_create_directory = true
@allow_read_network = true
@allow_write_network = true
end
end
end
end
end
1 change: 1 addition & 0 deletions nokogiri.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ Gem::Specification.new do |spec|
"lib/nokogiri/xml/xpath/syntax_error.rb",
"lib/nokogiri/xml/xpath_context.rb",
"lib/nokogiri/xslt.rb",
"lib/nokogiri/xslt/security.rb",
"lib/nokogiri/xslt/stylesheet.rb",
"lib/xsd/xmlparser/nokogiri.rb",
]
Expand Down
6 changes: 6 additions & 0 deletions test/files/xslt_included.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version='1.0'>
<xsl:output method="text" indent="no" encoding="UTF-8"/>

</xsl:stylesheet>
7 changes: 7 additions & 0 deletions test/files/xslt_including.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version='1.0'>
<xsl:output method="text" indent="no" encoding="UTF-8"/>

<xsl:include href="./xslt_included.xsl"/>
</xsl:stylesheet>
1 change: 1 addition & 0 deletions test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ module TestBase
XML_XINCLUDE_FILE = File.join(ASSETS_DIR, "xinclude.xml")
XML_ATOM_FILE = File.join(ASSETS_DIR, "atom.xml")
XSLT_FILE = File.join(ASSETS_DIR, "staff.xslt")
XSLT_INCLUDING_FILE = File.join(ASSETS_DIR, "xslt_including.xsl")
XPATH_FILE = File.join(ASSETS_DIR, "slow-xpath.xml")

def i_am_ruby_matching(gem_version_requirement_string)
Expand Down
89 changes: 89 additions & 0 deletions test/test_xslt_transforms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,95 @@ def test_transform_with_quote_params
assert_equal("Booyah", result_doc.at_css("h1").content)
end

def test_default_security_options_read_file
options = Nokogiri::XSLT::Security::Config.new

if Nokogiri.jruby?
assert_raises(NotImplementedError) do
Nokogiri::XSLT.default_security_options = options
end
else
# Default is insecure
Nokogiri::XSLT.default_security_options = options
assert(Nokogiri::XSLT(File.open(XSLT_INCLUDING_FILE)))

options.allow_read_file = false
Nokogiri::XSLT.default_security_options = options
assert_raises(RuntimeError) { Nokogiri::XSLT(File.open(XSLT_INCLUDING_FILE)) }

options.allow_read_file = true
Nokogiri::XSLT.default_security_options = options
assert(Nokogiri::XSLT(File.open(XSLT_INCLUDING_FILE)))
end
end

def test_default_security_options_roundtrip
options = Nokogiri::XSLT::Security::Config.new

if Nokogiri.jruby?
assert_raises(NotImplementedError) do
Nokogiri::XSLT.default_security_options
end
else
# Since the getter constructs the settings from the underlying C library
# this actually tells us if the settings are configured correctly
# Check all settings once per option to make sure fields are plumbed right
# at every stage
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
assert(configured_opts.allow_read_file)
assert(configured_opts.allow_write_file)
assert(configured_opts.allow_create_directory)
assert(configured_opts.allow_read_network)
assert(configured_opts.allow_write_network)

options.allow_read_file = false
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
refute(configured_opts.allow_read_file)
assert(configured_opts.allow_write_file)
assert(configured_opts.allow_create_directory)
assert(configured_opts.allow_read_network)
assert(configured_opts.allow_write_network)

options.allow_write_file = false
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
refute(configured_opts.allow_read_file)
refute(configured_opts.allow_write_file)
assert(configured_opts.allow_create_directory)
assert(configured_opts.allow_read_network)
assert(configured_opts.allow_write_network)

options.allow_create_directory = false
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
refute(configured_opts.allow_read_file)
refute(configured_opts.allow_write_file)
refute(configured_opts.allow_create_directory)
assert(configured_opts.allow_read_network)
assert(configured_opts.allow_write_network)

options.allow_read_network = false
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
refute(configured_opts.allow_read_file)
refute(configured_opts.allow_write_file)
refute(configured_opts.allow_create_directory)
refute(configured_opts.allow_read_network)
assert(configured_opts.allow_write_network)

options.allow_write_network = false
Nokogiri::XSLT.default_security_options = options
configured_opts = Nokogiri::XSLT.default_security_options
refute(configured_opts.allow_read_file)
refute(configured_opts.allow_write_file)
refute(configured_opts.allow_create_directory)
refute(configured_opts.allow_read_network)
refute(configured_opts.allow_write_network)
end
end

def test_exslt
# see http://yokolet.blogspot.com/2010/10/pure-java-nokogiri-xslt-extension.html")
skip_unless_libxml2("cannot get it working on JRuby")
Expand Down

0 comments on commit d231858

Please sign in to comment.