From ff848a5d7797957cc34f8ab6720c0f17930c46e9 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Sun, 1 Sep 2024 11:11:35 +0100 Subject: [PATCH 01/18] feat: add `sanitised` helper Sanitises a string for HTML output (using Rails' built-in sanitizer) --- app/models/search_aspect.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/models/search_aspect.rb b/app/models/search_aspect.rb index f38a748..62b8e94 100644 --- a/app/models/search_aspect.rb +++ b/app/models/search_aspect.rb @@ -34,8 +34,8 @@ def search_term(_value, preferences) end # Sanitise input and convert to Lucene expression - def text_index_term(preferences) # rubocop:disable Metrics/MethodLength - terms = sanitise_punctuation(preference_value(preferences)) + def text_index_term(preferences) + terms = sanitise_punctuation(sanitised(preference_value(preferences))) .split .reject { |token| LUCENE_KEYWORDS.include?(token.downcase) } .reject(&:empty?) @@ -45,9 +45,15 @@ def text_index_term(preferences) # rubocop:disable Metrics/MethodLength "Sorry, '#{preference_value(preferences)}' is not a permissible search term" end - terms - .join(' AND ') - .gsub(/\A(.*)\Z/, '( \1 )') + terms.join(' AND ').gsub(/\A(.*)\Z/, '( \1 )') + end + + # Sanitises a string for HTML output (using Rails' built-in sanitizer) + def sanitised(val) + Rails.logger.debug { "sanitising #{val}!" } if Rails.env.development? + cleaned = ActionController::Base.helpers.sanitize(val) + Rails.logger.debug { "cleaned to #{cleaned}!" } if Rails.env.development? + cleaned end private From dc5372f90e3f88cebd879b3b4760b27c4c9893d4 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Sun, 1 Sep 2024 11:13:04 +0100 Subject: [PATCH 02/18] refactor: include arrays in `sanitise!` helper Remove any non-allowlisted parameters, or params with empty values (e.g. empty strings, empty arrays) --- app/models/user_preferences.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/user_preferences.rb b/app/models/user_preferences.rb index 882ac60..bde662b 100644 --- a/app/models/user_preferences.rb +++ b/app/models/user_preferences.rb @@ -83,9 +83,14 @@ def unlimited? private # Remove any non-allowlisted parameters, or params with empty values + # (e.g. empty strings, empty arrays) def sanitise! @params.keep_if do |_k, v| - !v.to_s.strip.empty? + if v.is_a?(Array) + v.any? { |x| !x.to_s.strip.empty? } + else + !v.to_s.strip.empty? + end end end From 062a281e6ab25eb40ec6a0e5c0acf73ae6767d78 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Sun, 1 Sep 2024 11:17:00 +0100 Subject: [PATCH 03/18] fix: sanitise user output in view Sanitises the supplied input for HTML output using Rails' built-in sanitizer helper --- app/views/ppd/index.html.haml | 18 +++++++++--------- app/views/search/_search_results.html.haml | 8 +++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/views/ppd/index.html.haml b/app/views/ppd/index.html.haml index eedc8f1..46b61cd 100644 --- a/app/views/ppd/index.html.haml +++ b/app/views/ppd/index.html.haml @@ -13,37 +13,37 @@ .form-group %label.col-sm-4.control-label{ for: "paon" } Building name or number .col-sm-8 - %input#paon.form-control{ type: "text", name: "paon", value: @preferences.param( :paon ), placeholder: "For example: Rose Cottage or 17" } + %input#paon.form-control{ type: "text", autocomplete: "on", name: "paon", value: sanitize(@preferences.param( :paon )), placeholder: "For example: Rose Cottage or 17" } .form-group %label.col-sm-4.control-label{ for: "street" } Street .col-sm-8 - %input#street.form-control{ type: "text", name: "street", value: @preferences.param( :street ), placeholder: "Use the full name, or just part of it e.g: Harbour Road or Colston" } + %input#street.form-control{ type: "text", autocomplete: "street-address", name: "street", value: sanitize(@preferences.param( :street )), placeholder: "Use the full name, or just part of it e.g: Harbour Road or Colston" } .form-group %label.col-sm-4.control-label{ for: "town" } Town or city .col-sm-8 - %input#town.form-control{ type: "text", name: "town", value: @preferences.param( :town ), placeholder: "For example: Plymouth" } + %input#town.form-control{ type: "text", autocomplete: "address-level2", name: "town", value: sanitize(@preferences.param( :town )), placeholder: "For example: Plymouth" } .form-group %label.col-sm-4.control-label{ for: "district" } District .col-sm-8 - %input#district.form-control{ type: "text", name: "district", value: @preferences.param( :district ), placeholder: "For example: City of Westminster" } + %input#district.form-control{ type: "text", autocomplete: "address-level3", name: "district", value: sanitize(@preferences.param( :district )), placeholder: "For example: City of Westminster" } .form-group %label.col-sm-4.control-label{ for: "county" } County .col-sm-8 - %input#county.form-control{ type: "text", name: "county", value: @preferences.param( :county ), placeholder: "For example: Devon" } + %input#county.form-control{ type: "text", autocomplete: "county", name: "county", value: sanitize(@preferences.param( :county )), placeholder: "For example: Devon" } .form-group %label.col-sm-4.control-label{ for: "locality" } Locality .col-sm-8 - %input#locality.form-control{ type: "text", name: "locality", value: @preferences.param( :locality ), placeholder: "For example: Thurloxton" } + %input#locality.form-control{ type: "text", autocomplete: "on", name: "locality", value: sanitize(@preferences.param( :locality )), placeholder: "For example: Thurloxton" } .form-group %label.col-sm-4.control-label{ for: "postcode" } Postcode .col-sm-8 - %input#postcode.form-control{ type: "text", name: "postcode", value: @preferences.param( :postcode ), placeholder: "Use a full postcode, or just the first group e.g. PL6 5WS or PL6." } + %input#postcode.form-control{ type: "text", autocomplete: "postal-code", name: "postcode", value: sanitize(@preferences.param( :postcode )), placeholder: "Use a full postcode, or just the first group e.g. PL6 5WS or PL6." } .form-group %label.col-sm-4.control-label{ for: "ptype" } Property type @@ -128,7 +128,7 @@ .input-group %span.input-group-addon £ - %input#min_price.form-control{ type: "text", name: :min_price, value: @preferences.param( :min_price ) } + %input#min_price.form-control{ type: "text", name: :min_price, value: sanitize(@preferences.param( :min_price )) } .col-sm-8.hidden.validation-warning %p.bg-warning Please enter a valid numerical value, or leave blank @@ -140,7 +140,7 @@ .input-group %span.input-group-addon £ - %input#max_price.form-control{ type: "text", name: :max_price, value: @preferences.param( :max_price ) } + %input#max_price.form-control{ type: "text", name: :max_price, value: sanitize(@preferences.param( :max_price )) } .col-sm-8.hidden.validation-warning %p.bg-warning Please enter a valid numerical value, or leave blank diff --git a/app/views/search/_search_results.html.haml b/app/views/search/_search_results.html.haml index 4089c8d..581061d 100644 --- a/app/views/search/_search_results.html.haml +++ b/app/views/search/_search_results.html.haml @@ -11,11 +11,13 @@ #{sr.summarise} by searching for: %ul.search-terms.list.list-bullet - @preferences.each_search_term do |search_term| + - clean_label = sanitize(search_term.label) + - clean_value = sanitize(search_term.value) %li %label - = raw(search_term.label) - - st_path = @preferences.as_path( :search, {}, {search_term.name => search_term.value } ) - %a.search-term{ href: st_path, 'aria-label' => "Remove search term #{raw(search_term.label)}" } + = raw(clean_label) + - st_path = @preferences.as_path( :search, {}, {search_term.name => clean_value } ) + %a.search-term{ href: st_path, 'aria-label' => "Remove search term #{raw(clean_label)}" } %i.fa.fa-times-circle.fa-lg %p.search-selection From 44536d3fadd5d416d5f9a175813e181aa1ea1574 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 2 Sep 2024 09:21:36 +0100 Subject: [PATCH 04/18] build: updated version patch cadence v1.7.6 ~> 1.7.7 --- app/lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/version.rb b/app/lib/version.rb index 2e2f290..73e3c60 100644 --- a/app/lib/version.rb +++ b/app/lib/version.rb @@ -3,7 +3,7 @@ module Version MAJOR = 1 MINOR = 7 - PATCH = 6 + PATCH = 7 SUFFIX = nil VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}#{SUFFIX && ".#{SUFFIX}"}" end From dcc7742016d3c6f63b5e4b760892c63c51a052bb Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 2 Sep 2024 09:21:48 +0100 Subject: [PATCH 05/18] docs: Updated CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f02764d..8d2249f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ This app allows the user to explore HMLR price-paid open linked data. +## Changelog + +## 1.7.7 - 2024-09 + +- (Jon) Updated search query processing and rendering to santise supplied input + for HTML output and resolve discovered XSS vulnerability in displayed results + [GH-236](https://github.com/epimorphics/hmlr-linked-data/issues/236) + ## 1.7.6 - 2024-03-08 - (Jon) Updated the application_controller to include an From 71c552b938048608f3007de9367c1e72c0e59bc1 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 2 Sep 2024 13:25:42 +0100 Subject: [PATCH 06/18] refactor: updated internal sanitiser class `ActionController::Base.helpers.sanitize(val)` ~> `Rails::Html::FullSanitizer.new.sanitize(val)` --- app/models/search_aspect.rb | 11 +++++------ app/models/search_term.rb | 7 +++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/models/search_aspect.rb b/app/models/search_aspect.rb index 62b8e94..7bded93 100644 --- a/app/models/search_aspect.rb +++ b/app/models/search_aspect.rb @@ -29,8 +29,10 @@ def preference_value_as_regex(preferences) end def search_term(_value, preferences) - val = preference_value(preferences) - SearchTerm.new(key, "#{key_as_label} matches '#{val}'", val) + val = sanitised(preference_value(preferences)) + label = "#{key_as_label} matches '#{val}'" + + SearchTerm.new(key, label, val) end # Sanitise input and convert to Lucene expression @@ -50,10 +52,7 @@ def text_index_term(preferences) # Sanitises a string for HTML output (using Rails' built-in sanitizer) def sanitised(val) - Rails.logger.debug { "sanitising #{val}!" } if Rails.env.development? - cleaned = ActionController::Base.helpers.sanitize(val) - Rails.logger.debug { "cleaned to #{cleaned}!" } if Rails.env.development? - cleaned + Rails::Html::FullSanitizer.new.sanitize(val) end private diff --git a/app/models/search_term.rb b/app/models/search_term.rb index fe3dddf..91f9465 100644 --- a/app/models/search_term.rb +++ b/app/models/search_term.rb @@ -23,7 +23,8 @@ def form_name def form_value # @values ? @values.join(",") : @value - @value + # remove any HTML tags from the value + Rails::Html::FullSanitizer.new.sanitize(@value) end def label @@ -44,7 +45,9 @@ def truncated_label_term end def clean_label_term + # remove any HTML tags from the label term + term = Rails::Html::FullSanitizer.new.sanitize(@label_term) # previously we added a profanity filter here, but it was decided not to keep that feature - @label_term + term end end From db003235b7cc9d5200a7e7c8158d743a49af905d Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 2 Sep 2024 13:26:38 +0100 Subject: [PATCH 07/18] fix: resolve date value mis-parsing now checks for date type and only sanitises strings for `search_term.value` --- app/views/search/_search_results.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/search/_search_results.html.haml b/app/views/search/_search_results.html.haml index 581061d..ff59362 100644 --- a/app/views/search/_search_results.html.haml +++ b/app/views/search/_search_results.html.haml @@ -12,7 +12,7 @@ %ul.search-terms.list.list-bullet - @preferences.each_search_term do |search_term| - clean_label = sanitize(search_term.label) - - clean_value = sanitize(search_term.value) + - clean_value = search_term.value.is_a?(Date) ? search_term.value : sanitize(search_term.value) %li %label = raw(clean_label) From 379940996a6b43ea49b2c43ef93f9ed3d0711be2 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 2 Sep 2024 13:27:20 +0100 Subject: [PATCH 08/18] build: removed erroneous rubocop enabled rule --- app/controllers/search_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index ec0ce04..391bd1f 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -36,7 +36,7 @@ def create render_error_page(e, e.message, status) end - # rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity + # rubocop:enable Metrics/MethodLength def use_compact_json? non_compact_formats.exclude?(request.format) @@ -56,7 +56,7 @@ def render_error_page(err, message, status, template = 'ppd/error') @message = message # log the error with as much detail as possible in development to aid in resolving the issue - message = "#{err.class.name} error #{uuid} ::: #{message} ::: #{err.class}" if Rails.env.development? + @message = "#{err.class.name} error: #{message}" if Rails.env.development? # Keep it simple silly in production! Rails.logger.error message From 2c3f74545e4dfb91ec224d93991b2fa82ade81a5 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 3 Sep 2024 16:36:26 +0100 Subject: [PATCH 09/18] Update deployment.yaml --- deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.yaml b/deployment.yaml index f2a3d01..a059395 100644 --- a/deployment.yaml +++ b/deployment.yaml @@ -3,7 +3,7 @@ name: epimorphics/ppd-explorer key: ppd deployments: - branch: "prod" - # deploy: "prod" + deploy: "prod" publish: "prod" - branch: "preprod" deploy: "preprod" From e6b331a42a38e33fee066bdf330d64618add379f Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 16 Sep 2024 14:15:01 +0100 Subject: [PATCH 10/18] fix: resolve undefined method `empty?` for integer updated the type check for the current search terms to only sanitise strings and pass other types, i.e. Date, Integer, as is. --- app/views/search/_search_results.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/search/_search_results.html.haml b/app/views/search/_search_results.html.haml index a123298..89f938f 100644 --- a/app/views/search/_search_results.html.haml +++ b/app/views/search/_search_results.html.haml @@ -13,7 +13,7 @@ %ul.search-terms.list.list-bullet - @preferences.each_search_term do |search_term| - clean_label = sanitize(search_term.label) - - clean_value = search_term.value.is_a?(Date) ? search_term.value : sanitize(search_term.value) + - clean_value = search_term.value.is_a?(String) ? sanitize(search_term.value) : search_term.value %li %label = raw(clean_label) From a09d96ed1024d7a690118242312eeed81f222eec Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 16 Sep 2024 14:17:59 +0100 Subject: [PATCH 11/18] refactor: only show pretty error template in production Due to the manner Rails swallows the stack trace if presenting a custom error template early the ticket issue was hard to determine while stepping through the current codebase; now standard Rails error templates will be used in production and test environments --- app/controllers/search_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index dc3337f..4321c54 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -8,7 +8,7 @@ def index create end - def create + def create # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity @preferences = UserPreferences.new(params) if @preferences.empty? @@ -33,7 +33,7 @@ def create :internal_server_error end - render_error_page(e, e.message, status) + render_error_page(e, e.message, status) if !Rails.env.development? end def use_compact_json? From 97c65958937b1dab5de7aefbe0be56a3b3ac9590 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 16 Sep 2024 14:20:29 +0100 Subject: [PATCH 12/18] build: updated version patch cadence --- app/lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/version.rb b/app/lib/version.rb index 3e0677b..b304d3b 100644 --- a/app/lib/version.rb +++ b/app/lib/version.rb @@ -3,7 +3,7 @@ module Version MAJOR = 1 MINOR = 7 - PATCH = 8 + PATCH = 9 SUFFIX = nil VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}#{SUFFIX && ".#{SUFFIX}"}" end From 1ee2ae216b5f52f58546b7036403967b8aa2f8de Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Mon, 16 Sep 2024 14:20:43 +0100 Subject: [PATCH 13/18] docs: Updated CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f7d93d..0684f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ This app allows the user to explore HMLR price-paid open linked data. ## Changelog +## 1.7.9 - 2024-09 + +- (Jon) Updated the type check for the current search terms to only sanitise + strings and pass other types, i.e. Date, Integer, as is. + [GH-245](https://github.com/epimorphics/ppd-explorer/issues/245) + ## 1.7.8 - 2024-09 - (Jon) Moved all mirrored configuration settings from individual environments From 21c919b35de04a51e2f00a259f128a5a528c439e Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 17 Sep 2024 09:43:18 +0100 Subject: [PATCH 14/18] fix: resolve undefined method `empty?` for integer updated the type check for the current search terms to only sanitise strings and pass other types, i.e. Date, Integer, as is. --- app/views/search/_search_results.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/search/_search_results.html.haml b/app/views/search/_search_results.html.haml index ff59362..ceae3fe 100644 --- a/app/views/search/_search_results.html.haml +++ b/app/views/search/_search_results.html.haml @@ -12,7 +12,7 @@ %ul.search-terms.list.list-bullet - @preferences.each_search_term do |search_term| - clean_label = sanitize(search_term.label) - - clean_value = search_term.value.is_a?(Date) ? search_term.value : sanitize(search_term.value) + - clean_value = search_term.value.is_a?(String) ? sanitize(search_term.value) : search_term.value %li %label = raw(clean_label) From 2449363bc9d0dd2c1b271a2b62a2ef3fc46aa063 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 17 Sep 2024 09:43:36 +0100 Subject: [PATCH 15/18] build: updated version patch cadence --- app/lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/version.rb b/app/lib/version.rb index 73e3c60..3e0677b 100644 --- a/app/lib/version.rb +++ b/app/lib/version.rb @@ -3,7 +3,7 @@ module Version MAJOR = 1 MINOR = 7 - PATCH = 7 + PATCH = 8 SUFFIX = nil VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}#{SUFFIX && ".#{SUFFIX}"}" end From 18bd74da369dfff53762ae218d918160205211bb Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 17 Sep 2024 09:43:53 +0100 Subject: [PATCH 16/18] docs: Updated CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d2249f..0bb79c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ This app allows the user to explore HMLR price-paid open linked data. ## Changelog +## 1.7.8 - 2024-09 + +- (Jon) Updated the type check for the current search terms to only sanitise + strings and pass other types, i.e. Date, Integer, as is. + [GH-245](https://github.com/epimorphics/ppd-explorer/issues/245) + ## 1.7.7 - 2024-09 - (Jon) Updated search query processing and rendering to santise supplied input From 7ea14a8c0904768dbd0a5aa6f445601d47c6c479 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 17 Sep 2024 11:07:26 +0100 Subject: [PATCH 17/18] build: resolve version cadence sync --- app/lib/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/version.rb b/app/lib/version.rb index 3e0677b..b304d3b 100644 --- a/app/lib/version.rb +++ b/app/lib/version.rb @@ -3,7 +3,7 @@ module Version MAJOR = 1 MINOR = 7 - PATCH = 8 + PATCH = 9 SUFFIX = nil VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}#{SUFFIX && ".#{SUFFIX}"}" end From 3d15889fd0add0ef5194216c6c09738ed55dbc10 Mon Sep 17 00:00:00 2001 From: "Jon R. Humphrey" Date: Tue, 17 Sep 2024 11:07:51 +0100 Subject: [PATCH 18/18] docs: Resolve CHANGELOG sync --- CHANGELOG.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 680cbe4..34fd5e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,6 @@ This app allows the user to explore HMLR price-paid open linked data. ## 1.7.9 - 2024-09 -- (Jon) Updated the type check for the current search terms to only sanitise - strings and pass other types, i.e. Date, Integer, as is. - [GH-245](https://github.com/epimorphics/ppd-explorer/issues/245) - -## 1.7.8 - 2024-09 - - (Jon) Moved all mirrored configuration settings from individual environments into the application configuration to reduce the need to manage multiple sources of truth @@ -46,14 +40,6 @@ This app allows the user to explore HMLR price-paid open linked data. - (Dan) updates the help modal focus flow to meet accessibility requirments [GH-218](https://github.com/epimorphics/ppd-explorer/issues/218) -## 1.7.7 - 2024-09 - -- (Jon) Updated search query processing and rendering to santise supplied input - for HTML output and resolve discovered XSS vulnerability in displayed results - [GH-236](https://github.com/epimorphics/hmlr-linked-data/issues/236) - -## Changelog - ## 1.7.8 - 2024-09 - (Jon) Updated the type check for the current search terms to only sanitise