Skip to content

Commit

Permalink
Merge branch 'hotfix-10.3.6' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
DominicWatson committed Nov 30, 2015
2 parents d721051 + 2931a8e commit 051af8f
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 2 deletions.
2 changes: 1 addition & 1 deletion support/build/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ build.number.remote.url=http://downloads.presidecms.com/presidecms/build.number

##############################
# MANUALLY UPDATE EACH VERSION
preside.version=10.3.5
preside.version=10.3.6
##############################
42 changes: 42 additions & 0 deletions support/docs/docs/02.devguides/21.xss/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
id: xss
title: XSS Protection
---

>>> This feature was first introduced in **PresideCMS v10.3.6**. The details below do not apply for older versions of the software.
PresideCMS comes with XSS protection out of the box using the AntiSamy project. This protection will automatically strip unwanted HTML from user input in order to prevent the possibility of successful cross site scripting attacks.

## Configuring protection

The protection is turned on by default but bypassed by default when the logged in user is a CMS administrator. These settings, and also the AntiSamy profile to be used, can be edited in your sites `Config.cfc` file:

```luceescript
public void function configure() {
super.configure();
// turn off antisamy (don't do this!)
settings.antiSamy.enabled = false;
// use the "tinymce" AntiSamy policy (default is myspace)
settings.antiSamy.policy = "tinymce";
// do not bypass antisamy, even when logged in user is admin
settings.antiSamy.bypassForAdministrators = false;
// ...
}
```

The list of possible policies to use are:

* antisamy
* ebay
* myspace
* slashdot
* tinymce

We plan to provide the ability for custom antisamy profiles to be used, but as of v10.3.6, these are the only options available.

For more information on the AntiSamy project, visit [https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project](https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project).
3 changes: 2 additions & 1 deletion support/docs/docs/02.devguides/chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ In this chapter, you should find detailed guides on developing with the PresideC
* [[adminlefthandmenu]]
* [[workingwithuploadedfiles]]
* [[multilingualcontent]]
* [[presidesuperclass]]
* [[presidesuperclass]]
* [[xss]]
44 changes: 44 additions & 0 deletions support/tests/integration/api/security/AntiSamyServiceTest.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
component extends="testbox.system.BaseSpec" {

function beforeAll() {
antiSamy = new preside.system.services.security.AntiSamyService();
}

function run(){

describe( "clean()", function(){

it( "should strip script tags from content (we know it should do much more, but just to test...)", function(){
var dirty = "some test <script>alert('hello')</script> to be cleaned";
var cleaned = "some test to be cleaned";
var actual = antiSamy.clean( dirty );

expect( actual ).toBe( cleaned );
} );

it( "should wrap css in CDATA for the myspace policy", function(){
var dirty = "some input <style>.class { color: red }</style> with css in it";
var cleaned = "some input <style><![CDATA[*.class { color: red; } ]]></style> with css in it";
var actual = antiSamy.clean( dirty, "myspace" );

expect( actual contains "CDATA").toBeTrue();
} );

it( "should entirely strip css for more stricter policies", function(){
var dirty = "some input <style>.class { color: red }</style> with css in it";
var cleaned = "some input with css in it";
var actual = antiSamy.clean( dirty, "tinymce" );

expect( actual ).toBe( cleaned );
} );

it( "should throw a helpful error when the passed policy does not exist", function(){
expect( function(){
antiSamy.clean( "blah", "non-existant-policy" );
} ).toThrow( type="preside.antisamyservice.policy.not.found" );
} );

} );

}
}
1 change: 1 addition & 0 deletions system/BaseApplication.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ component extends="Bootstrap" {
this.name = ExpandPath( "/" );
this.sessionManagement = true;
this.sessionTimeout = CreateTimeSpan( 0, 0, 40, 0 );
this.scriptProtect = "none";

this.PRESIDE_APPLICATION_RELOAD_LOCK_TIMEOUT = 15;
this.PRESIDE_APPLICATION_RELOAD_TIMEOUT = 1200;
Expand Down
2 changes: 2 additions & 0 deletions system/Bootstrap.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ component {
, any sessionTimeout = CreateTimeSpan( 0, 0, 40, 0 )
, numeric applicationReloadTimeout = 1200
, numeric applicationReloadLockTimeout = 15
, string scriptProtect = "none"
) {
this.PRESIDE_APPLICATION_ID = arguments.id;
this.PRESIDE_APPLICATION_RELOAD_LOCK_TIMEOUT = arguments.applicationReloadLockTimeout;
this.PRESIDE_APPLICATION_RELOAD_TIMEOUT = arguments.applicationReloadTimeout;
this.name = arguments.name
this.sessionManagement = arguments.sessionManagement;
this.sessionTimeout = arguments.sessionTimeout;
this.scriptProtect = arguments.scriptProtect;

_setupMappings( argumentCollection=arguments );
_setupDefaultTagAttributes();
Expand Down
6 changes: 6 additions & 0 deletions system/config/Config.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ component output=false {

settings.validationProviders = [ "presideObjectValidators", "passwordPolicyValidator" ];

settings.antiSamy = {
enabled = true
, policy = "myspace"
, bypassForAdministrators = true
};

_loadConfigurationFromExtensions();

environments = {
Expand Down
17 changes: 17 additions & 0 deletions system/handlers/General.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ component {
property name="applicationReloadService" inject="applicationReloadService";
property name="websiteLoginService" inject="websiteLoginService";
property name="adminLoginService" inject="loginService";
property name="antiSamySettings" inject="coldbox:setting:antiSamy";
property name="antiSamyService" inject="delayedInjector:antiSamyService";

public void function applicationStart( event, rc, prc ) {
prc._presideReloaded = true;
announceInterception( "onApplicationStart" );
}

public void function requestStart( event, rc, prc ) {
_xssProtect( argumentCollection = arguments );
_reloadChecks( argumentCollection = arguments );
_recordUserVisits( argumentCollection = arguments );
}
Expand All @@ -34,6 +37,20 @@ component {
}

// private helpers
private void function _xssProtect( event, rc, prc ) {
if ( IsTrue( antiSamySettings.enabled ?: "" ) ) {
if ( IsFalse( antiSamySettings.bypassForAdministrators ?: "" ) || !event.isAdminUser() ) {
var policy = antiSamySettings.policy ?: "myspace";

for( var key in rc ){
if( IsSimpleValue( rc[ key ] ) ) {
rc[ key ] = antiSamyService.clean( rc[ key ], policy );
}
}
}
}
}

private void function _reloadChecks( event, rc, prc ) {
var anythingReloaded = false;

Expand Down
73 changes: 73 additions & 0 deletions system/services/security/AntiSamyService.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @singleton
*
*/
component {

// CONSTRUCTOR
public any function init() {
_setLibPath( ExpandPath( "/coldbox/system/plugins/AntiSamy-lib" ) );
_setupPolicyFiles();
_setupAntiSamy();

return this;
}

// PUBLIC API
public any function clean( required string input, string policy="myspace" ) {
var antiSamyResult = _getAntiSamy().scan( arguments.input, _getPolicyFile( arguments.policy ) );

return antiSamyResult.getCleanHtml();
}

// PRIVATE HELPERS
private void function _setupPolicyFiles() {
var libPath = _getLibPath();

_setPolicyFiles ( {
antisamy = libPath & '/antisamy-anythinggoes-1.4.4.xml'
, ebay = libPath & '/antisamy-ebay-1.4.4.xml'
, myspace = libPath & '/antisamy-myspace-1.4.4.xml'
, slashdot = libPath & '/antisamy-slashdot-1.4.4.xml'
, tinymce = libPath & '/antisamy-tinymce-1.4.4.xml'
} );
}

private void function _setupAntiSamy() {
var jars = DirectoryList( _getLibPath(), false, "path", "*.jar" );

_setAntiSamy( CreateObject( "java", "org.owasp.validator.html.AntiSamy", jars ) );
}

private array function _listJars( required string directory ) {
return ;
}

private string function _getPolicyFile( required string policy ) {
var policies = _getPolicyFiles();

return policies[ arguments.policy ] ?: throw( type="preside.antisamyservice.policy.not.found", message="The policy [#arguments.policy#] was not found. Existing policies: '#SerializeJson( policies.keyArray() )#" );
}

// GETTERS AND SETTERS
private string function _getLibPath() {
return _libPath;
}
private void function _setLibPath( required string libPath ) {
_libPath = arguments.libPath;
}

private struct function _getPolicyFiles() {
return _policyFiles;
}
private void function _setPolicyFiles( required struct policyFiles ) {
_policyFiles = arguments.policyFiles;
}

private any function _getAntiSamy() {
return _antiSamy;
}
private void function _setAntiSamy( required any antiSamy ) {
_antiSamy = arguments.antiSamy;
}
}

0 comments on commit 051af8f

Please sign in to comment.