/**
* WS-Attacker - A Modular Web Services Penetration Testing Framework Copyright
* (C) 2011 Christian Mainka
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package wsattacker.plugin.signatureWrapping.option;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import wsattacker.library.signatureWrapping.option.Payload;
import wsattacker.library.signatureWrapping.util.signature.SignatureManager;
import wsattacker.library.xmlutilities.dom.DomUtilities;
import wsattacker.main.composition.plugin.option.AbstractOption;
import wsattacker.main.composition.plugin.option.AbstractOptionBoolean;
import wsattacker.main.composition.plugin.option.AbstractOptionMultiFiles;
import wsattacker.main.composition.testsuite.CurrentRequestContentChangeObserver;
import wsattacker.main.plugin.PluginOptionContainer;
import wsattacker.main.plugin.option.OptionSimpleBoolean;
import wsattacker.main.plugin.option.OptionSimpleMultiFiles;
import wsattacker.main.plugin.option.OptionSimpleVarchar;
import wsattacker.main.plugin.option.OptionSoapAction;
import wsattacker.plugin.signatureWrapping.SignatureWrapping;
/**
* This class takes care on the options for the WS-Attacker XSW Plugin.
*/
public class OptionManager
implements CurrentRequestContentChangeObserver, PropertyChangeListener
{
private SignatureWrapping plugin;
private SignatureManager signatureManager;
private final OptionSoapAction optionSoapAction;
private final OptionSimpleBoolean optionMustContainString, optionUseSchema, abortOnFirstSuccess;
private final OptionSimpleVarchar optionTheContainedString;
private final OptionSimpleMultiFiles optionSchemaFiles;
private final OptionViewButton optionView;
private final List<OptionPayload> optionPayloadList;
private boolean working = false;
private static final OptionManager INSTANCE = new OptionManager();
public static OptionManager getInstance()
{
return INSTANCE;
}
public SignatureWrapping getPlugin()
{
return plugin;
}
public void setPlugin( SignatureWrapping plugin )
{
if ( this.plugin != null )
{
this.plugin.getPluginOptions().addPropertyChangeListener( this );
}
this.plugin = plugin;
if ( plugin != null )
{
this.plugin.getPluginOptions().addPropertyChangeListener( this );
this.plugin.getPluginOptions().setOptions( addConfigOptions() );
}
}
public SignatureManager getSignatureManager()
{
return signatureManager;
}
public void setSignatureManager( SignatureManager signatureManager )
{
this.signatureManager = signatureManager;
}
/**
* Initialization method.
*
* @param plugin
* @param signatureManager
*/
private OptionManager()
{
this.optionSoapAction = new OptionSoapAction( "Change\nAction?", "Allows to change the SoapAction Header." );
this.optionSchemaFiles =
new OptionSimpleMultiFiles( "Used\nSchema\nfiles",
"Set the Schema Files.\nSoap11, Soap12, WSA, WSSE, WSU, DS and XPathFilter2\nare included by default." );
this.optionMustContainString =
new OptionSimpleBoolean( "Search?", false, "SOAP Response must contain a specific String." );
this.abortOnFirstSuccess =
new OptionSimpleBoolean( "Abort?", true, "Abort after first successful attack message." );
this.optionTheContainedString = new OptionSimpleVarchar( "Contains", "Search for this String...", 200 );
this.optionUseSchema = new OptionSimpleBoolean( "Schema?", true, "Use XML Schema." );
this.optionPayloadList = new ArrayList<OptionPayload>();
this.optionView = new OptionViewButton();
optionMustContainString.addPropertyChangeListener( AbstractOptionBoolean.PROP_ON, this );
optionSchemaFiles.addPropertyChangeListener( AbstractOptionMultiFiles.PROP_FILES, this );
optionUseSchema.addPropertyChangeListener( AbstractOptionBoolean.PROP_ON, this );
}
private Logger log()
{
return Logger.getLogger( getClass() );
}
/**
* If the current request is changed, the SignatureManger must be notified.
*/
@Override
public void currentRequestContentChanged( String newContent, String oldContent )
{
if ( !working )
{
working = true;
log().trace( "Current Request Content Changed" );
Document domDoc;
try
{
domDoc = DomUtilities.stringToDom( newContent );
}
catch ( SAXException e )
{
getSignatureManager().setDocument( null );
working = false;
return;
}
getSignatureManager().setDocument( domDoc );
for ( int i = 0; i < optionPayloadList.size(); ++i )
{
OptionPayload option = optionPayloadList.get( i );
option.removePropertyChangeListener( this );
}
optionPayloadList.clear();
for ( Payload payload : getSignatureManager().getPayloads() )
{
payload.addPropertyChangeListener( this );
OptionPayload newPayload = new OptionPayload( payload );
optionPayloadList.add( newPayload );
}
List<AbstractOption> allOptions = addConfigOptions();
allOptions.addAll( optionPayloadList );
if ( plugin != null )
{
plugin.getPluginOptions().setOptions( allOptions );
}
working = false;
}
}
/**
* If no curent request is available, the SignatureManager must be notified.
*/
@Override
public void noCurrentRequestcontent()
{
if ( working )
{
return;
}
working = true;
log().trace( "No Current Message" );
getSignatureManager().setDocument( null );
clearOptions();
working = false;
}
/**
* This methods add the default config options to the OptionManager. Those are: - Option for changing the
* SOAPAction. - Option for aborting the attack if one XSW message is accepted. - Option to not use any XML Schema.
* - Option to selected XML Schema files. - Option to add a search string. - The View Button - The Payload-Chooser
* Combobox
*/
private List<AbstractOption> addConfigOptions()
{
List<AbstractOption> result;
if ( getPlugin() == null )
{
log().debug( "No plugin set?" );
result = Collections.<AbstractOption> emptyList();
}
else
{
List<AbstractOption> newOptions = new ArrayList<AbstractOption>();
log().info( "Adding optionSoapAction" );
newOptions.add( 0, optionSoapAction );
log().info( "Adding abortOnFirstSuccess" );
newOptions.add( 1, abortOnFirstSuccess );
log().info( "Adding optionUseSchema" );
newOptions.add( 2, optionUseSchema );
log().info( "Adding optionSchemaFiles" );
newOptions.add( 3, optionSchemaFiles );
log().info( "Adding optionMustContainString" );
newOptions.add( 4, optionMustContainString );
if ( optionPayloadList.size() > 0 )
{
log().info( "Adding View Button" );
newOptions.add( 5, optionView );
}
if ( optionMustContainString.isOn() )
{
log().info( "Adding optionTheContainedString" );
newOptions.add( 5, optionTheContainedString );
}
result = newOptions;
}
return result;
}
/**
* This function is only needed due to a GUI Bug in WS-Attacker which does not allow to put an AbstractOption at a
* specific position. With this function, you can pop AbstractOptions up to one specific one, than add the needed
* Options, and afterwards re-add the popped one putOptions.
*
* @param needle
* @return
*/
public List<AbstractOption> popOptionsUpTo( AbstractOption needle )
{
List<AbstractOption> result = new ArrayList<AbstractOption>();
PluginOptionContainer container = getPlugin().getPluginOptions();
if ( !container.contains( needle ) )
{
return result;
}
while ( container.size() > 0 )
{
AbstractOption last = container.getByIndex( container.size() - 1 );
if ( last == needle )
{
break;
}
container.remove( last );
result.add( last );
}
log().info( "Popped: " + result.toString() );
return result;
}
/**
* This function is only needed due to a GUI Bug in WS-Attacker which does not allow to put an AbstractOption at a
* specific position. With this function, you can pop AbstractOptions up to one specific one, than add the needed
* Options, and afterwards re-add the popped one putOptions.
*
* @param needle
* @return
*/
public void putOptions( List<AbstractOption> optionList )
{
log().info( "Put: " + optionList.toString() );
PluginOptionContainer container = getPlugin().getPluginOptions();
for ( int i = optionList.size() - 1; i >= 0; --i )
{
container.add( optionList.get( i ) );
}
}
/**
* Clear all options consecutively.
*/
public void clearOptions()
{
if ( getPlugin() == null )
{
log().debug( "No plugin set?" );
}
else
{
log().info( "Clearing Options.." );
PluginOptionContainer container = getPlugin().getPluginOptions();
while ( container.size() > 0 )
{
container.remove( container.getByIndex( 0 ) );
}
}
}
/**
* Handler if an option value is changed. Changes, e.g. the concrete showed PayloadOption.
*/
@Override
public void propertyChange( PropertyChangeEvent pce )
{
PluginOptionContainer container = getPlugin().getPluginOptions();
if ( pce.getSource() instanceof OptionPayload )
{
plugin.checkState();
}
else if ( pce.getSource() == optionMustContainString )
{
log().info( "option == optionMustContainString" );
if ( optionMustContainString.isOn() && !container.contains( optionTheContainedString ) )
{
log().info( "true == optionMustContainString.isOn()" );
container.add( 1 + container.indexOf( optionMustContainString ), optionTheContainedString );
}
else if ( container.contains( optionTheContainedString ) )
{
log().info( "false == optionMustContainString.isOn()" );
container.remove( optionTheContainedString );
}
}
else if ( pce.getSource() == optionSchemaFiles )
{
plugin.setUsedSchemaFiles( optionSchemaFiles.getFiles() );
}
else if ( pce.getSource() == optionUseSchema )
{
log().info( "Remove Schema Files Option" );
if ( !optionUseSchema.isOn() && container.contains( optionSchemaFiles ) )
{
container.remove( optionSchemaFiles );
plugin.setSchemaAnalyzerDepdingOnOption();
}
else if ( !container.contains( optionSchemaFiles ) )
{
log().info( "Add Schema Files Option" );
container.add( 1 + container.indexOf( optionUseSchema ), optionSchemaFiles );
}
}
getPlugin().checkState();
}
public OptionSoapAction getOptionSoapAction()
{
return optionSoapAction;
}
public AbstractOptionMultiFiles getOptionSchemaFiles()
{
return optionSchemaFiles;
}
public OptionSimpleBoolean getOptionMustContainString()
{
return optionMustContainString;
}
public OptionSimpleBoolean getOptionUseSchema()
{
return optionUseSchema;
}
public OptionSimpleBoolean getAbortOnFirstSuccess()
{
return abortOnFirstSuccess;
}
public OptionSimpleVarchar getOptionTheContainedString()
{
return optionTheContainedString;
}
}