Skip to content

Commit

Permalink
Merge pull request #533 from SalesforceFoundation/feature/248
Browse files Browse the repository at this point in the history
DO NOT MERGE: FEATURE 248 BRANCH
  • Loading branch information
jjbennett committed Nov 17, 2023
2 parents bdde567 + 9181b50 commit 3a9e1e5
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 17 deletions.
32 changes: 25 additions & 7 deletions src/classes/UTIL_HtmlOutput_CTRL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* @author Salesforce.org
* @date 2020
* @group Utilities
* @description The UTIL_HtmlOutput_CTRL is used to safely unescape any allowlisted tags and urls
* @description The UTIL_HtmlOutput_CTRL is used to safely unescape allowlisted tags and urls
*/
public with sharing class UTIL_HtmlOutput_CTRL {

Expand All @@ -53,11 +53,10 @@ public with sharing class UTIL_HtmlOutput_CTRL {

/** @description The map of allowed urls and their temporary substitution values */
private static final Map<String, String> SUBSTITUTION_BY_ALLOWED_URL = new Map<String, String> {
'<a href="/ui/setup/apex/batch/ScheduleBatchApexPage' => '|schedBatch|',
'<a href="/08e?setupid=ScheduledJobs' => '|schedJobs|',
'" target="_blank"' => '|blankTarget|',
'" target="_new"' => '|newTarget|',
'"' => '|quote|'
'<a href="/ui/setup/apex/batch/ScheduleBatchApexPage"' => '|schedBatch|',
'<a href="/08e?setupid=ScheduledJobs"' => '|schedJobs|',
'target="_blank"' => '|blankTarget|',
'target="_new"' => '|newTarget|'
};

/** @description The unescaped html passed in by the page. */
Expand Down Expand Up @@ -108,13 +107,32 @@ public with sharing class UTIL_HtmlOutput_CTRL {
}

if (hasURL) {
replacedHtml = replacedHtml.trim();
for (String allowedUrl : SUBSTITUTION_BY_ALLOWED_URL.keySet()) {
replacedHtml = replacedHtml.replace(allowedUrl, SUBSTITUTION_BY_ALLOWED_URL.get(allowedUrl));
String subbedValue = SUBSTITUTION_BY_ALLOWED_URL.get(allowedUrl);
replacedHtml = replacedHtml.replace(allowedUrl, subbedValue);
}

replacedHtml = cleanupUrl(replacedHtml, SUBSTITUTION_BY_ALLOWED_URL.values()[0]);
replacedHtml = cleanupUrl(replacedHtml, SUBSTITUTION_BY_ALLOWED_URL.values()[1]);
}

return replacedHtml;
}

private String cleanupUrl(String replacedHtml, String urlSubstitution) {
String urlAttributes = replacedHtml.substringBetween(urlSubstitution, '|emptyClose|');
if (urlAttributes == null) {
return replacedHtml;
}
if (urlAttributes.contains(SUBSTITUTION_BY_ALLOWED_URL.values()[2])) {
urlAttributes = urlAttributes.substringAfter(SUBSTITUTION_BY_ALLOWED_URL.values()[2]);
} else if (urlAttributes.contains(SUBSTITUTION_BY_ALLOWED_URL.values()[3])) {
urlAttributes = urlAttributes.substringAfter(SUBSTITUTION_BY_ALLOWED_URL.values()[3]);
}

return replacedHtml.replace(urlAttributes, '');
}

/*******************************************************************************************************************
* @description Returns the escaped html with the temporary tags and url to their original allowlisted values
Expand Down
53 changes: 43 additions & 10 deletions src/classes/UTIL_HtmlOutput_TEST.cls
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public with sharing class UTIL_HtmlOutput_TEST {
controller.unsafeHtml = html;
controller.hasUrl = false;

System.assertEquals(html, controller.getSafeHtml());
System.assertEquals(html, controller.getSafeHtml(),'Expected the bold tags with a space to be allowed.');
}

/*******************************************************************************************************
Expand All @@ -60,7 +60,7 @@ public with sharing class UTIL_HtmlOutput_TEST {
controller.unsafeHtml = html;
controller.hasUrl = false;

System.assertEquals(html, controller.getSafeHtml());
System.assertEquals(html, controller.getSafeHtml(), 'Expected the bold tags to be allowed.');
}

/*******************************************************************************************************
Expand All @@ -75,8 +75,8 @@ public with sharing class UTIL_HtmlOutput_TEST {
controller.hasUrl = false;
String actualHtml = controller.getSafeHtml();

System.assert(actualHtml.startsWith('&lt;'));
System.assertNotEquals(html, actualHtml);
System.assert(actualHtml.startsWith('&lt;'), 'Expected the img tag to be escaped.');
System.assertNotEquals(html, actualHtml, 'Expected the html to have been escaped.');
}

/*******************************************************************************************************
Expand All @@ -90,21 +90,22 @@ public with sharing class UTIL_HtmlOutput_TEST {
controller.unsafeHtml = html;
controller.hasUrl = true;

System.assertEquals(html, controller.getSafeHtml());
System.assertEquals(html, controller.getSafeHtml(), 'Expected the url to be allowed.');
}

/*******************************************************************************************************
* @description Verifies a string with allowlisted url with extra space is returned without escaping
* @description Verifies a string with allowlisted url with extra space is returned without the space
*/
@isTest
private static void shouldReturnOriginalUrlWithSpace() {
private static void shouldRemoveWhiteSpaceFromUrl() {
String html = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage" >';
String expectedHtml = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage">';
UTIL_HtmlOutput_CTRL controller = new UTIL_HtmlOutput_CTRL();

controller.unsafeHtml = html;
controller.hasUrl = true;

System.assertEquals(html, controller.getSafeHtml());
System.assertEquals(expectedHtml, controller.getSafeHtml(), 'Expected only the space to be removed from the url.');
}

/*******************************************************************************************************
Expand All @@ -119,7 +120,39 @@ public with sharing class UTIL_HtmlOutput_TEST {
controller.hasUrl = true;
String actualHtml = controller.getSafeHtml();

System.assert(actualHtml.startsWith('&lt;'));
System.assertNotEquals(html, actualHtml);
System.assert(actualHtml.startsWith('&lt;'), 'Expected the url to be escaped.');
System.assertNotEquals(html, actualHtml, 'Expected the html to be espaced.');
}

/*******************************************************************************************************
* @description Verifies unallowed attributes within the url are removed
*/
@isTest
private static void shouldReturnCleanUrl() {
String html = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage" ONMOUSEOVER=alert(document.domain)>';
String expectedHtml = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage">';
UTIL_HtmlOutput_CTRL controller = new UTIL_HtmlOutput_CTRL();

controller.unsafeHtml = html;
controller.hasUrl = true;
String actualHtml = controller.getSafeHtml();

System.assertEquals(expectedHtml, controller.getSafeHtml(), 'Expected the unallowed attributes to be removed from the url.');
}

/*******************************************************************************************************
* @description Verifies target remains in url.
*/
@isTest
private static void shouldAllowTargetInUrl() {
String html = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage" target="_blank">';
String expectedHtml = '<a href="/ui/setup/apex/batch/ScheduleBatchApexPage" target="_blank">';
UTIL_HtmlOutput_CTRL controller = new UTIL_HtmlOutput_CTRL();

controller.unsafeHtml = html;
controller.hasUrl = true;
String actualHtml = controller.getSafeHtml();

System.assertEquals(expectedHtml, controller.getSafeHtml(), 'Expected the target to remain in the url.');
}
}

0 comments on commit 3a9e1e5

Please sign in to comment.