Adding anti XSS NIHVIVO-3379
This commit is contained in:
parent
dac5d91478
commit
36a99486f6
12 changed files with 400 additions and 41 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue