Adding anti XSS NIHVIVO-3379

This commit is contained in:
briancaruso 2011-12-05 22:08:04 +00:00
parent dac5d91478
commit 36a99486f6
12 changed files with 400 additions and 41 deletions

View file

@ -38,6 +38,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.SelectListGeneratorV
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.FieldVTwo;
import edu.cornell.mannlib.vitro.webapp.web.MiscWebUtils;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.DefaultAddMissingIndividualFormModelPreprocessor;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;
/**
* Generates the edit configuration for a default property form.
@ -116,6 +117,9 @@ public class DefaultAddMissingIndividualFormGenerator implements EditConfigurati
//default obj property form.populateTemplate or some such method
//Select from existing also set within template itself
setTemplate(editConfiguration, vreq);
editConfiguration.addValidator(new AntiXssValidation());
//edit key now set in the edit request dispatch controller
return editConfiguration;
}

View file

@ -23,6 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUti
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.FieldVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.DefaultDataPropEmptyField;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash;
public class DefaultDataPropertyFormGenerator extends BaseEditConfigurationGenerator implements EditConfigurationGenerator {
@ -82,6 +83,8 @@ public class DefaultDataPropertyFormGenerator extends BaseEditConfigurationGener
editConfiguration.addField( literalField );
editConfiguration.addValidator(new AntiXssValidation());
// An empty field on an update gets special treatment
if( update ) {
// on update, allow an empty field and deal with it in DefaultDataPropEmptyField

View file

@ -26,6 +26,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.FieldVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;
import edu.cornell.mannlib.vitro.webapp.search.beans.ProhibitedFromSearch;
/**
@ -110,6 +111,9 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene
//default obj property form.populateTemplate or some such method
//Select from existing also set within template itself
setTemplate(editConfiguration, vreq);
editConfiguration.addValidator(new AntiXssValidation());
//Set edit key
setEditKey(editConfiguration, vreq);
//Adding additional data, specifically edit mode

View file

@ -34,6 +34,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.Field;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditN3GeneratorVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.SelectListGeneratorVTwo;
@ -96,6 +97,7 @@ public class NewIndividualFormGenerator implements EditConfigurationGenerator {
prepareForUpdate(vreq, session, editConfiguration);
editConfiguration.addValidator(new AntiXssValidation());
//Form title and submit label now moved to edit configuration template
//TODO: check if edit configuration template correct place to set those or whether

View file

@ -31,6 +31,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.Field;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditN3GeneratorVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.SelectListGeneratorVTwo;
@ -100,6 +101,8 @@ public class RDFSLabelGenerator implements EditConfigurationGenerator {
//placing in session depends on having edit key which is handled in edit request dispatch controller
prepareForUpdate(vreq, session, editConfiguration);
editConfiguration.addValidator(new AntiXssValidation());
//Form title and submit label now moved to edit configuration template
setTemplate(editConfiguration, vreq);
//Set edit key

View file

@ -0,0 +1,192 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.PolicyException;
import org.owasp.validator.html.ScanException;
import com.hp.hpl.jena.rdf.model.Literal;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.MultiValueEditSubmission;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.N3ValidatorVTwo;
import edu.cornell.mannlib.vitro.webapp.web.AntiScript;
/**
* Check if the submitted text has potential XSS problems.
* Error messages from this validator always start with XSS_ERROR_MESSAGE
*
* @author bdc34
*/
public class AntiXssValidation implements N3ValidatorVTwo{
List<String> fieldNamesToValidate;
/**
* Validate all fields on submission.
*/
public AntiXssValidation(){
this.fieldNamesToValidate = ALL_FIELDS;
}
/**
* Validate only fields specified in fieldNamesToValidate.
*/
public AntiXssValidation(List<String> fieldNamesToValidate){
this.fieldNamesToValidate = fieldNamesToValidate;
}
@Override
public Map<String, String> validate(EditConfigurationVTwo editConfig,
MultiValueEditSubmission editSub) {
if( editSub == null ) {
return null;
}
Map<String,String>varToErrMsg = new HashMap<String,String>();
if( fieldNamesToValidate == null ){
if( editSub.getLiteralsFromForm() != null ){
for( String name : editSub.getLiteralsFromForm().keySet()){
varToErrMsg.putAll( checkSubmissionForField( name, editSub));
}
}
if( editSub.getUrisFromForm() != null ){
for( String name : editSub.getUrisFromForm().keySet()){
varToErrMsg.putAll( checkSubmissionForField( name, editSub));
}
}
}else{
for( String fieldName : fieldNamesToValidate){
varToErrMsg.putAll( checkSubmissionForField(fieldName, editSub));
}
}
if( varToErrMsg.isEmpty() )
return null;
else
return varToErrMsg;
}
/**
* Check for XSS for a single field. Returns NO_ERROR if there
* are no errors so it can be added to a map with putAll()
*/
protected Map<String,String> checkSubmissionForField(
String fieldName, MultiValueEditSubmission editSub){
if( fieldName == null || fieldName.isEmpty() || editSub == null)
return NO_ERROR;
if( editSub.getLiteralsFromForm() != null &&
editSub.getLiteralsFromForm().containsKey(fieldName) ){
String error = null;
try {
error = literalHasXSS( editSub.getLiteralsFromForm().get(fieldName) );
} catch (ScanException e) {
error = e.getMessage();
} catch (PolicyException e) {
error = e.getMessage();
}
if( error != null ){
return Collections.singletonMap(fieldName, XSS_ERROR_MESSAGE + " " + error);
}else{
return NO_ERROR;
}
} else if (editSub.getUrisFromForm() != null &&
editSub.getUrisFromForm().containsKey(fieldName)){
String error;
try {
error = uriHasXSS( editSub.getUrisFromForm().get(fieldName));
} catch (ScanException e) {
error = e.getMessage();
} catch (PolicyException e) {
error = e.getMessage();
}
if( error != null ){
return Collections.singletonMap(fieldName, XSS_ERROR_MESSAGE + " " + error);
}else{
return NO_ERROR;
}
}else{
//field wasn't in submission
return NO_ERROR;
}
}
/**
* Check if a list of URIs has any XSS problems.
* Return null if there are none and return an error message if there are problems.
*/
private String uriHasXSS(List<String> uriList) throws ScanException, PolicyException {
AntiSamy antiSamy = AntiScript.getAntiSamyScanner();
ArrayList errorMsgs = new ArrayList();
for( String uri : uriList ){
CleanResults cr = antiSamy.scan( uri );
errorMsgs.addAll( cr.getErrorMessages() );
}
if( errorMsgs.isEmpty() ){
return null;
}else{
return StringUtils.join(errorMsgs, ", ");
}
}
/**
* Check if a List of Literals has any XSS problems.
* Return null if there are none and return an error message if there are problems.
*/
private String literalHasXSS(List<Literal> list) throws ScanException, PolicyException {
AntiSamy antiSamy = AntiScript.getAntiSamyScanner();
ArrayList errorMsgs = new ArrayList();
for( Literal literal : list ){
CleanResults cr = antiSamy.scan(literal.getLexicalForm());
errorMsgs.addAll( cr.getErrorMessages() );
String dt = literal.getDatatypeURI();
if( dt != null ){
cr = antiSamy.scan( dt );
errorMsgs.addAll( cr.getErrorMessages() );
}
String lang = literal.getLanguage() ;
if( lang != null ){
cr = antiSamy.scan( lang );
errorMsgs.addAll( cr.getErrorMessages() );
}
}
if( errorMsgs.isEmpty() )
return null;
else
return StringUtils.join(errorMsgs,", ");
}
/**
* All error messages will start with this string.
*/
public static String XSS_ERROR_MESSAGE = "Field contains unacceptable markup";
private static final Map<String,String>NO_ERROR = Collections.emptyMap();
//value indicates that all fields should be validated.
private static final List<String> ALL_FIELDS = null;
}

View file

@ -29,9 +29,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.Field;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.EditSubmission;
/**
* User: bdc34
* Date: Jan 24, 2008
* Time: 1:55:39 PM
*
*/
public class BasicValidation {
@ -271,7 +269,7 @@ public class BasicValidation {
static final List<String> basicValidations;
static{
basicValidations = Arrays.asList(
"nonempty","isDate","dateNotPast","httpUrl" );
"nonempty","isDate","dateNotPast","httpUrl" ,"anti-xss");
}
private Log log = LogFactory.getLog(BasicValidation.class);

View file

@ -372,6 +372,7 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet {
if(EditConfigurationVTwoGenerator == null){
throw new Error("Could not find EditConfigurationVTwoGenerator " + editConfGeneratorName);
} else {
log.debug("Created EditConfiguration using " + editConfGeneratorName);
return EditConfigurationVTwoGenerator.getEditConfiguration(vreq, session);
}

View file

@ -24,10 +24,11 @@ public class AntiScript {
private static final Log log = LogFactory.getLog(AntiScript.class);
private static final String ANTI_SCRIPT_POLICY = "ANTI_SCRIPT_POLICY";
private static Policy policy;
private static AntiSamy antiSamy;
private static final String ANTI_SCRIPT_SCANNER = "ANTI_SCRIPT_SCANNER";
private static String ANTI_SCRIPT_POLICY_FILE = "/WEB-INF/classes/edu/cornell/mannlib/vitro/webapp/web/antisamy-vitro-1.4.4.xml";
private static String ANTI_SCRIPT_POLICY_FILE = "/edu/cornell/mannlib/vitro/webapp/web/antisamy-vitro-1.4.4.xml";
/**
* This will attempt to return HTML that has been cleaned up according
@ -41,11 +42,11 @@ public class AntiScript {
*
* This will return null if dirtyInput is null.
*/
public static String cleanText( String dirtyInput, ServletContext context){
public static String cleanText( String dirtyInput ){
if( dirtyInput == null )
return null;
AntiSamy as = getHTMLScanner(context);
AntiSamy as = getAntiSamyScanner();
CleanResults cr;
try {
cr = as.scan(dirtyInput);
@ -61,62 +62,59 @@ public class AntiScript {
/**
* Method to clean a URL or URI.
*/
public static String cleanURI( String dirtyInput, ServletContext context){
return cleanText(dirtyInput,context);
public static String cleanURI( String dirtyInput ){
return cleanText(dirtyInput);
}
/**
* Method to clean all of the values in a map where the values are of
* type String.
*/
public static <T> void cleanMapValues( Map<T,String> map, ServletContext context){
public static <T> void cleanMapValues( Map<T,String> map ){
for( T key : map.keySet() ){
map.put(key, cleanText(map.get(key), context));
map.put(key, cleanText(map.get(key)) );
}
}
/**
* Try to get the policy from the servlet context, if none exists, create a new one.
* Try to get the static policy, if none exists, create a new one.
* This is a anti-script policy for use with OWASP AntiSamy, not a vivo auth Policy.
* Returns null if no policy can be created.
*/
protected static Policy getAntiScriptPolicy(ServletContext context){
Object obj = context.getAttribute( ANTI_SCRIPT_POLICY );
if( obj == null ){
protected static Policy getAntiScriptPolicy( ){
if( policy == null ){
Policy newPolicy;
try {
String url = ANTI_SCRIPT_POLICY_FILE;
URL policyFile= context.getResource( url );
newPolicy = Policy.getInstance( policyFile );
context.setAttribute(ANTI_SCRIPT_POLICY, newPolicy);
String url = ANTI_SCRIPT_POLICY_FILE;
URL policyFile= AntiScript.class.getResource( url );
newPolicy = Policy.getInstance( policyFile );
log.debug("anti-script policy loaded successfully");
return newPolicy;
policy = newPolicy;
} catch (PolicyException e) {
log.error("Anti-Script policy not setup.", e);
return null;
} catch (MalformedURLException e) {
} catch (Throwable e) {
log.error("Anti-Script policy not setup.", e);
return null;
}
} else {
return (Policy)obj;
}
}
}
return policy;
}
/**
* Try to get an AntiSamy HTML scanner object that is sharied across
* the whole web application. This may return a scanner with a null
* Try to get a static AntiSamy HTML scanner object that is shared the
* whole application. This may return a scanner with a null
* policy if the policy is not setup correctly.
*/
protected static AntiSamy getHTMLScanner( ServletContext context){
Object obj = context.getAttribute( ANTI_SCRIPT_SCANNER );
if( obj == null ){
AntiSamy scanner = new AntiSamy( getAntiScriptPolicy(context));
context.setAttribute( ANTI_SCRIPT_SCANNER , scanner);
log.debug("anti-script scanner loaded successfully");
return scanner;
}else{
return (AntiSamy) obj;
}
public static AntiSamy getAntiSamyScanner( ){
if( antiSamy == null ){
antiSamy = new AntiSamy( getAntiScriptPolicy() );
log.debug("anti-script scanner loaded successfully");
}
return antiSamy;
}
}

View file

@ -45,6 +45,9 @@ public abstract class BaseTemplateModel {
* that shouldn't be in a URI.
*/
protected String cleanURIForDisplay( String dirty ){
if( dirty == null )
return null;
StringBuilder clean = new StringBuilder(dirty.length());
for (char ch: dirty.toCharArray()) {
if (URI_CHARACTERS.indexOf(ch) != -1) {
@ -59,7 +62,7 @@ public abstract class BaseTemplateModel {
* Currently this only checks for XSS exploits.
*/
protected String cleanTextForDisplay( String dirty){
return AntiScript.cleanText(dirty, getServletContext());
return AntiScript.cleanText(dirty);
}
/**
@ -67,7 +70,7 @@ public abstract class BaseTemplateModel {
* a map. Map may be modified.
*/
protected <T> void cleanMapValuesForDisplay( Map<T,String> map){
AntiScript.cleanMapValues(map, getServletContext());
AntiScript.cleanMapValues(map);
}
protected static ServletContext getServletContext() {