Common Pitfalls - XSS, URL Encoding, etc.

Cross-Site scripting (XSS) issues

Any call to the window.parent or window.top needs be be guarded with a try/catch. The code needs to be prepared for this call to fail:

try{
  window.parent.doSomething();
} catch(xss){
  // ignored or handled
}

URL Encoding

All parameterized calls to the BA Server need to be properly URL Encoded. Unfortunately, proper encoding can be confusing. There are several built-in functions in Javascript: escape, encodeURI, encodeURIComponent. Further complicating matters is the need to specially encode forward and backslashs due to a limitation when running in Tomcat. Special encoding is also required for any URL part referring to a BA-Server Repository path.

When is encoding important?

If you have a static URL being used as part of a form action or in an AJAX call, there's no need to run it through any encoding. It's only when variable input (parameterization) is used as part of the URL construction that encoding is required.

No encoding needed:
"pentaho/api/user-settings/list"

Encoding required:
"pentaho/api/user-settings/" + paraneterizedSetting

What encoding function to use?

Due to the slashes problem with Tomcat, standard Javascript encoding functions will not suffice. Instead, use the URL Encoding module: "common-ui/util/URLEncoder" which provides a function for processing substitution-based URL templates. URLEncoder ensures that the template replacements are properly encoded for their part of the URL (path, query-string). To aid non-AMD scripts, URLEncoder is also available on the global window scope as: "window.pho.Encoder"

Usage (AMD):
require( [ "common-ui/util/URLEncoder" ], function( Encoder ){
   var encodedURL = Encoder.encode( "some/path/{0}/{1}", [ val1, val2 ] );
}

Usage (Non-AMD):
var encodedURL = pho.Encoder.encode( "some/path/{0}/{1}", [ val1, val2 ] );
Optional: passing of an Object for query-string generation.

This behavior is the same as the Dojo module dojo/io-query

require( [ "common-ui/util/URLEncoder" ], function( Encoder ){
   var queryObject = { bang: "something", baz: ["a","b"]
   var encodedURL = Encoder.encode( "some/path/{0}/{1}", [ val1, val2 ], queryObject );
   // results in "some/path/val1/val2?bang=something&baz=a&baz=b
}
Relative vs Absolute Paths

Relative URLs are preferred over absolute as they're generally less prone to error. The exception is for scripts and HTML pages which are run or mapped to different locations. In this case, a relative URL can lead to an incorrect path. In such cases absolute paths cannot be avoided. To facilitate the construction of absolute paths, a global variable is available on all pages containing the root (context) path of the web application: CONTEXT_PATH. Note the value of CONTEXT_PATH will always have a trailing slash

Usage:

var url = CONTEXT_PATH + "some/index.html";
// e.g. /pentaho/some/index.html

Another usage of absolute paths is when you're constructing a link to be supplied outside of the running application, inclusion in an email or PDF for instance. In this case the full URL including hostname is required. Available on every page is a global variable FULL_QUALIFIED_URL which has the full host information in addition to the context path: https://localhost:8080/pentaho

Usage:

var url = FULL_QUALIFIED_URL + "some/directory/resource.css";
// e.g. https:127.0.0.1:8080/pentaho/some/directory/resource.css
Repository Paths

If part of the URL contains a BA Server Repository path, it will need to be pre-processed before passing it as an argument to the URLEncoder. URLEncoder supplies a function for this as well: encodeRepositoryPath

var userSuppliedRepositoryPath = "..."
// handle repository path encoding
var encodedRepositoryPath = Encoder.encodeRepositoryPath( userSuppliedRepositoryPath )

var url = Encoder.encode( "some/path/{0}", [ encodedRepositoryPath ] );

There are other classes available for encoding or decoding repository paths in environments other than javascript.  For GWT applications, the NameUtils class wraps the URLEncoder in static classes.

String url = NameUtils.URLEncode( NameUtils.encodeRepositoryPath( filePath ) );

  If urls need to be encoded or decoded server side, the RepositoryPathEncoder class provides methods to decode and encode paths.

String ActualPath = RepositoryPathEncoder.decodeRepositoryPath( path );

Invalid Repository File Name Characters

Repository file/folder names should be checked for invalid characters prior to submitting to the server.

Javascript in Plugins

Plugin Javascript applications can use mantle/browser/lib/webContext.js to provide the following three global variables.

var RESERVED_CHARS = "\/\\\t\r\n";
var RESERVED_CHARS_DISPLAY = "\/, \\, \\t, \\r, \\n";
var RESERVED_CHARS_REGEX_PATTERN = /.*\[\/\\\t\r\n\]+.*/;
GWT

GWT applications can use NameUtils.isValidFileName( fileName ) or NameUtils.isValidPathName( path ) to check if a file or path name is valid.

NameUtils.getReservedChars() will return a string containing all the characters that are invalid in a file or folder name.

NameUtils.reservedCharListForDisplay( separatorString ) will provide a string containing the printable invalid characters suitable for display.

Server

Server code can use JcrRepositoryFileUtils.getReservedChars() to get a list of characters that are considered invalid.

The list of invalid characters can be overridden to include additional characters which you may deem invalid for various reasons, but care should be taken to include the already present characters, or instability may result.  To override the list of invalid of invalid characters add the following bean somewhere in the repository.spring.xml file.  The example below adds colon (:), square brackets ([, ]), asterisk (*), pipe (|) and quote (") to the list of invalid characters.  Note that the tab, linefeed, carriage return, and slashes are included as well.  These characters must always be marked as invalid.

<\!-\- Override reserved chars in JcrRepositoryFileUtils \-->
  <bean id="reservedChars">
    <constructor-arg>
      <util:list list-class="java.util.ArrayList" value-type="java.lang.Character">
        <value>&#x9;</value>
        <value>&#xD;</value>
        <value>&#xA;</value>
        <value>/</value>
        <value>\</value>
        <value>:</value>
        <value>\[</value>
        <value>\]</value>
        <value>*</value>
        <value>\|</value>
        <value>"</value>
      </util:list>
    </constructor-arg>
    <pen:publish as-type="INTERFACES"/>
  </bean>

Note that creation of this bean will also change the data provided by the javascript global variables as well as NameUtils in gwt.