From 6d618452274ac0f59958a45142b43c6094d7a8d6 Mon Sep 17 00:00:00 2001 From: Erika Pauwels Date: Tue, 6 Jun 2017 22:03:49 +0200 Subject: [PATCH] Add generic SPARQL escape helpers --- README.md | 28 +++++++++++++++------------- lib/escape_helpers.rb | 14 ++++++++++++++ sinatra_template/utils.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 58790d4..b719891 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,21 @@ Get the session id from the request headers. #### sparql_client Returns a SPARQL::Client instance connection to the SPARQL endpoint configured through the `MU_SPARQL_ENDPOINT` environment variable. +#### sparql_escape ; sparql_escape_{string|uri|date|datetime|bool|int|float}(value) +The Ruby templates extends the core classes `String`, `Date`, `DateTime`, `Time`, `Integer`, `Float`, `Boolean` and `URI` with a `sparql_escape` method. This method can be used to avoid SPARQL injection by escaping user input while constructing a SPARQL query. E.g. + +``` +query = " INSERT DATA {" +query += " GRAPH <#{settings.graph}> {" +query += " <#{user_uri}> a <#{RDF::Vocab::FOAF.Person}> ;" +query += " <#{RDF::Vocab::FOAF.name}> #{name.sparql_escape} ;" +query += " <#{RDF::Vocab::DC.created}> #{now.sparql_escape} ." +query += " }" +query += " }" +``` + +Next to the extensions, the template also provides a helper function per datatype that takes any value as parameter. E.g. `sparql_escape_uri("http://mu.semte.ch/application")`. + #### update(query) Executes the given SPARQL update query. @@ -115,16 +130,3 @@ You can now run your tests inside the container with: ## Experimental features #### MU_SPARQL_UPDATE_ENDPOINT environment variable Configure the SPARQL update endpoint path. This should be a path relative to the base of `MU_SPARQL_ENDPOINT`. Default: `/sparql`. The update endpoint can be retrieved via the `update_endpoint` helper method. - -#### sparql_escape() -The Ruby templates extends the core classes `String`, `Date`, `Integer`, `Float` and `Boolean` with a `sparql_escape` method. This method can be used to avoid SPARQL injection by escaping user input while constructing a SPARQL query. E.g. - -``` -query = " INSERT DATA {" -query += " GRAPH <#{settings.graph}> {" -query += " <#{user_uri}> a <#{RDF::Vocab::FOAF.Person}> ;" -query += " <#{RDF::Vocab::FOAF.name}> #{name.sparql_escape} ;" -query += " <#{RDF::Vocab::DC.created}> #{now.sparql_escape} ." -query += " }" -query += " }" -``` diff --git a/lib/escape_helpers.rb b/lib/escape_helpers.rb index 9085087..d80634a 100644 --- a/lib/escape_helpers.rb +++ b/lib/escape_helpers.rb @@ -1,10 +1,24 @@ +require 'uri' + class String def sparql_escape '"' + self.gsub(/[\\"']/) { |s| '\\' + s } + '"' end end +class URI::Generic + def sparql_escape + '<' + self.to_s.gsub(/[\\"']/) { |s| '\\' + s } + '>' + end +end + class Date + def sparql_escape + '"' + self.xmlschema + '"^^xsd:date' + end +end + +class DateTime def sparql_escape '"' + self.xmlschema + '"^^xsd:dateTime' end diff --git a/sinatra_template/utils.rb b/sinatra_template/utils.rb index 1e03417..08e9fa9 100644 --- a/sinatra_template/utils.rb +++ b/sinatra_template/utils.rb @@ -2,6 +2,7 @@ require 'logger' require 'rdf/vocab' require 'sparql/client' +require 'uri' require_relative '../lib/escape_helpers.rb' module SinatraTemplate @@ -44,6 +45,34 @@ def sparql_client @sparql_client end + def sparql_escape_string(value) + value ? value.to_s.sparql_escape : value + end + + def sparql_escape_uri(value) + value ? URI.parse(value).sparql_escape : value + end + + def sparql_escape_int(value) + value ? value.to_i.sparql_escape : value + end + + def sparql_escape_float(value) + value ? value.to_f.sparql_escape : value + end + + def sparql_escape_bool(value) + value ? true.sparql_escape : false.sparql_escape + end + + def sparql_escape_date(value) + value ? Date.parse(value).sparql_escape : value + end + + def sparql_escape_datetime(value) + value ? DateTime.parse(value).sparql_escape : value + end + def update(query) log.info "Executing query: #{query}" sparql_client.update query, { endpoint: update_endpoint }