IBM Support

Many OpenPages custom helpers stop working correctly after upgrading to 9.0.0.3

Troubleshooting


Problem

In OpenPages 9.0.0.3, the default Content-Security-Policy was changed to be more secure by removing the 'unsafe-inline' and 'unsafe-eval' source values.  Without these values, the browser will ignore inline styles, inline scripts, and inline event handlers.  It will also not execute the javascript eval() function.  
For more information, see https://content-security-policy.com/unsafe-inline

Symptom

The browser console contains errors like:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' ". Either the 'unsafe-inline' keyword, a hash ('sha256-MJrTRIde+lYAJ+jVMPcPE3y0AoS2Y98eKw2RLGh4Pxk='), or a nonce ('nonce-...') is required to enable inline execution.
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' ". Either the 'unsafe-inline' keyword, a hash ('sha256-9PK+x51HIBJTF8W3h1GfrMo58ngBW77+9GoJi1XM6sw='), or a nonce ('nonce-...') is required to enable inline execution.
Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present
 Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self':"

Resolving The Problem

Short term workaround
The recommended way to resolve these problems is to modify the helper to not use the unsafe features.  However, depending on the helper, this could be time-consuming.  In the short term, the unsafe values can be added back into the Content-Security-Policy for specific helpers using the new setting /Platform/Security/Headers/Unsafe Content-Security-Policy URLs.  See the product documentation for details.
Resolving unsafe-eval issues in dojo code
Recent versions of Dojo support the removal of 'unsafe-eval' from the Content-Security-Policy.  See this Dojo CSP documentation.  In addition to updating the dojo config, you will need to change xhr requests that use dojo.xhrGet(Post/Put) to use dojo/request/xhr instead.
Preferred techniques for resolving unsafe-inline issues in HTML
  • Move inline <style> tags to external style sheets
  • Move inline <script> tags to external script files.  This is  not always possible if your JSP dynamically creates the scripts.  This situation is covered later in this section.
  • Change inline style attributes to use class attributes instead and define the classes in the external style sheet.
  • Remove inline event handlers to add event handlers dynamically
function onLoad() {
    const button = document.getElementById("my-button");
	button.addEventListener("click", () => console.log("button clicked");
}

window.addEventListener("load", () => onLoad());
If you have many event handlers, using document.getElementId for them all can be very cumbersome.  In this case, you may wish to use a function like this.
/**
 * To support the removal of unsafe-inline from the Content-Security-Policy, we need to replace
 * inline event handlers in the html like 'onclick' with the javascript equivalents.  Used in JSPs.
 * To use:
 * 1.  Add the class "eventTrigger" to the html element
 * 2.  Set a data-event attribute such as "onclick"
 * 3.  Set a data-func attribute with the name of the function to call when the event is triggered
 * 4.  If there are parameters required by the function, set a data-params attribute containing an array
 *   of arguments in string format, i.e. data-params=\"['arga', 'argb']\".  Note that this must be an array
 *   even if there's only one argument. Also, if it's embedded in a java string, it is tempting to use
 *  single quotes like data-params='['arga', 'argb']' but this syntax will not be parsed correctly by
 *   the browser.  You need to use double quotes for the outer delims and escape them.
 * 5.  Set a data-replace-with-apostrophe If strings contain placeholders for apostrophes, this attribute contains the placeholder that should be replaced with an apostrophe.
 * 6.  Call this function from a document DOMContentLoaded event handler or whenever new DOM content is added.
 * 
 * Example:
 * <button class='eventTrigger' data-func='doclick' data-params=\"['startDate']\">
 *
 * @param The node to search for eventTrigger class names
 */

function addDynamicEventHandlers(nodeRef){
	
	const convertArr= (data,event) => {
		const params = data.params;
	    let args = params.substr(0, params.length - 1).substr(1).split(',');
	    for(let i=0; i< args.length; i++){
	    	args[i] = args[i]?.trim();
	    	if (!args[i].includes("'")) {
				//it's a number or a boolean so we don't want it to be a string
				var asNum = Number(args[i]);
				if (!isNaN(asNum)) {
					args[i] = asNum;
				} else {
					args[i] = Boolean(args[i]);
				}
			} else {
	    		args[i] = args[i].replace(/\'/g, "");
	    	}
	    	
	    	if(args[i] == "this"){
		   	  args[i] = event.target;
		    }
		    
		    if(data.replaceWithApostrophe){
				const regExp =  new RegExp(data.replaceWithApostrophe,'g');
				args[i] = args[i].replace(regExp, "'");
			}
	    }
	    return args;
	}
If you can't move your scripts to an external style sheet
Some JSPs generate internal script tags so it is not possible to move the scripts into a separate file.  In this case, you must add a nonce to the Content-Security-Policy and the same nonce to the script tag.  https://content-security-policy.com/nonce/
You can do this using the Java function functions in  com.openpages.apps.common.util.SecurityPublicUtil.  Depending on how your JSP is structured you could use either
 
/**
 * Sets a nonce into the Content-Security-Policy header so that 'unsafe-inline' is not needed.
 * @param response
 * @return the nonce
 */
public static String addNonceToCSP(HttpServletResponse response)
or 
/**
* Creates a nonce that can be added to a Content-Security-Policy
* @return the nonce
*/
public static String createNonce()

/**
* Sets a nonce into the Content-Security-Policy header so that 'unsafe-inline' is not needed.
* @param response
* @return the nonce
*/
public static String addNonceToCSP(HttpServletResponse response, String nonce)
In either case, you will also add the nonce to your script tag like this
<script type="text/javascript" nonce="<%=nonce.get()%>">
Note that in the above example, the nonce had been stored in a ThreadLocal variable.  Be very sure that you don't store the nonce in a global or static variable or it will be re-used every time the helper is called.  A nonce is intended to be used only once.

Document Location

Worldwide

[{"Type":"MASTER","Line of Business":{"code":"LOB10","label":"Data and AI"},"Business Unit":{"code":"BU048","label":"IBM Software"},"Product":{"code":"SSFUEU","label":"IBM OpenPages with Watson"},"ARM Category":[{"code":"a8m50000000Ck2pAAC","label":"API"},{"code":"a8m50000000Ck26AAC","label":"Installation"}],"ARM Case Number":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"9.0.0"}]

Document Information

Modified date:
03 July 2024

UID

ibm17158837