Matthew J. Clemente

On Using the Latest Version of AntiSamy with ColdFusion

Apr 13, 2022
9 minutes

In ColdFusion applications, I tend to use the AntiSamy Java library to sanitize user-supplied input. While updating to the latest version of AntiSamy (actually, the snapshot of an upcoming release - more on that later) I documented the process, and particularly how Maven can make it easier.

For a few years the OWASP AntiSamy project appeared to be strictly in maintenance mode,[1] but development has been very active over the past two years, with 12 releases since March 2020. Reading the release notes reveals that these are not just updating vulnerable dependencies (including log4j), but also valuable improvements, such as schema validation for policy files and reducing the risk of reverse tabnabbing via the adddition of rel="noopener" to anchors when target="_blank" is set. All good reasons to stay current!

The basic process of using Antisamy with ColdFusion varies, depending on if you're using Adobe ColdFusion or Lucee. I'll cover some of the differences later. Regardless, we need to get the AntiSamy jar file, along with all of its dependencies, and load them into our application.

With typical detail and insight, Ben Nadel recently blogged about his AntiSamy update process. It's worth noting that the approach here will be a little different because we'll use Maven, which will actually do a lot of the heavy lifting for us. If you haven't used Maven before, don't stop reading! Just like npm or yarn can make working with JavaScript packages easier, Maven is a very helpful tool when you're working with Java projects - and we only need to use a handful of commands.

Install Maven

This is relatively painless and totally worth it if you're looking to integrate Java projects with a ColdFusion application. There are any number of guides for installing Maven, but the official install guide is probably a good place to start. To quote their docs:

The installation of Apache Maven is a simple process of extracting the archive and adding the bin folder with the mvn command to the PATH.

Maven is also available via Homebrew and MacPorts for macOS and Chocolatey for Windows (though I've never tried any of those approaches). You'll know you're good to go when you can run the mvn --version command in your terminal.

Getting the Jars You Need

We're going to use the AntiSamy project on GitHub to build the jars that we need. As we'll see later on, this will also enable us to build the jars for any version we want, even if hasn't been released yet. So, let's hop into the command line and get started.

  1. Download the project from GitHub:

    git clone git@github.com:nahsra/antisamy.git
  2. Change directories into the project:

    cd antisamy
  3. We're on the main branch now. We could build here, but it's probably better if we checkout a specific release tag before building, so that we know exactly what we're getting. The following command will list the tags, with the most recent first:

    git tag -n --sort=-creatordate
  4. At the time I'm writing this, the most recent release is v1.6.6, so we'll check out the code there:

    git checkout v1.6.6

    This will result in a warning that You are in 'detached HEAD' state. - this is fine - we're not making changes, we're building a version.

  5. Time to build our AntiSamy jar:

    mvn package

    Maven will do its thing, running tests, compiling dependencies, etc. At the end we should get the message: BUILD SUCCESS.

  6. If you open it up the project a file manager, you'll see that it has a new /target directory, in which you will find the newly built antisamy-1.6.6.jar.

  7. Now, if you've worked with AntiSamy before, you know it has a handful of dependency jars. Where are those? We don't have them yet, but it's just one more Maven command to get them:

    mvn dependency:copy-dependencies -DincludeScope=runtime

    Note that the last option there, -DincludeScope=runtime, is so that we only get the production dependencies and not those that are only needed for testing.

  8. After running that command you'll find all the dependency jars within /target/dependency.

That's it, we've got the latest AntiSamy jar and its up-to-date dependencies.

Building a Fat Jar

A brief detour - while we're having fun with Maven, it's interesting to see what else we can do. This section is not necessary for using AntiSamy with ColdFusion - it's just taking a look at an alternative approach to jar building.

Instead of having the AntiSamy jar and its dependencies separate, we can instruct Maven to build a "fat" or "uber" jar - that is, a single jar that contains both AntiSamy and all of its dependencies. Note that this does involve editing the project's pom.xml, but it's a minor change.

Using your favorite code editor, open the AntiSamy pom.xml file. If you're using VS Code, you can just run code pom.xml from the project root.

Locate the top level <plugins> block, within the <build> section. Note that this is following the <pluginManagement> block, not within it. In v1.6.6 it begins on line 218. At the top, we'll add a new <plugin> node for the current version of the Maven Assembly Plugin:

<!-- within <plugins> -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- I placed it above the maven-clean-plugin plugin -->

After saving the file, hop back to the command line. Here, we'll run the following command, which will delete the existing /target directory and then rebuild the jar, using the updated pom.xml:

mvn clean package

Again, Maven will test, assemble, and build, outputting a lot of information to the console, finally culminating with a BUILD SUCCESS message.

With that, you'll find the fat jar (AntiSamy + dependencies) in the /target directory, named antisamy-1.6.6-jar-with-dependencies.jar. You can load that single jar into your ColdFusion application, rather than having to load up AntiSamy and its seventeen dependencies separately.

Loading AntiSamy Into Your CFML Application

Rather than clutter this blog post with extensive code blocks, I've put together a repository on GitHub with demo code of loading AntiSamy into both Lucee and Adobe ColdFusion applications. If the following sections of this post are confusing, hopefully the full code examples there help clarify things!

Lucee

If you're using Lucee, the process of loading the AntiSamy jars into your application is straightforward. You pass them, as an array of file paths, into this.javaSettings.loadPaths in your Application.cfc.[2] You'll then be able to use createObject to access and use AntiSamy.

Adobe ColdFusion Approach

Here, wiring up AntiSamy is a little more complicated, but doable, nonetheless.[3]

Issue 1: For reasons that I don't understand, Adobe ColdFusion does not play nice with loading the AntiSamy jars via this.javaSettings. The solution, as detailed by none other than Ben Nadel, is to use Mark Mandel's JavaLoader Project.

Issue 2: Additionally, Adobe ColdFusion does not play nice with the implementation of SLF4J (a logging library) that ships with AntiSamy. The solution, also thanks to Ben Nadel, is switch SLF4J to a no-operation implementation[4], which is done by adding one more dependency jar: SLF4J NOP Binding. This can be done manually, but we'll use Maven, since its been the focus of this post. By making one small change to AntiSamy's pom.xml, we can instruct Maven to use SLF4J's no-operation jar. This is done by changing the artifactId of SLF4J from slf4j-api to slf4j-nop within its <dependency> block (around line 109 at the time of writing):

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>${version.slf4j}</version>
</dependency>

After saving this change to the pom.xml, we need to clean out and rebuild the jars. We can run the following command, to clean, rebuild, and download production dependencies, all at once:

mvn clean package dependency:copy-dependencies -DincludeScope=runtime

And like that, you'll have the jars you need to use AntiSamy with Adobe Coldfusion. For the specifics of the implementation, head on over to the demo repository on GitHub.

Coda: On the Benefits of Testing

Remember how, in the first paragraph, I said that I ended up using a snapshot of the upcoming release? Here's where I finally get around to explaining why.

Before updating the jars, I wanted to make sure that there were no changes in functionality that would break my application. While this particular app's code coverage is nowhere near 100%, I'm working to improve that, and this was a great opportunity to add a few more tests. So, using TestBox, I built out some specs and expectations for AntiSamy's functionality within the project.

And here's where tests saved the day. When I updated AntiSamy to 1.6.5 (the version when I started writing this post), the tests failed. Specifically, an error message that was expected was not getting returned. This lead to me open an issue on the AntiSamy repository, and it turned out that there was actually a bug with the latest release - 1.6.5 was not returning CSS errors like it should have!

The 1.6.6 release on April 2 included a fix for the issue (along with security fixes and an enhancement).

Looking at the date of the issue I opened, I'm realizing it was over a month ago, and this post has taken quite a while to write, so I'm going to wrap it up. Bottom line, adding tests, even incrementally, can be surprisingly valuable to an application.


Footnotes

  1. You can take a look on Maven Repository to see how releases slowed down and them appeared to stop with 1.5.7 in 2017 ↩︎

  2. See the section "Using Third Party Java Libraries (Jar Files) in CFML" section on CFDocs if you're unfamiliar with this approach. ↩︎

  3. I should note that recent versions of Adobe ColdFusion have built-in support for much of AntiSamy's functionality, via getSafeHTML and isSafeHTML. However, there are still reasons you might want to use the jars directly, rather than the built-in functionality - these include access to the latest updates, the ability to return invalid HTML error messages, and dual-CFML-platform support (Lucee and Adobe). ↩︎

  4. To be clear, this means that SLF4J/AntiSamy discards all logging. Now, logging isn't a terribly important part of the AntiSamy project, but it is used to warn about the use of features that are deprecated. ↩︎