diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/ConfigurationBasedCategoryFilter.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/ConfigurationBasedCategoryFilter.java index 82aa54524..78bca277f 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/ConfigurationBasedCategoryFilter.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/ConfigurationBasedCategoryFilter.java @@ -4,8 +4,7 @@ import soot.jimple.infoflow.InfoflowConfiguration.SourceSinkFilterMode; import soot.jimple.infoflow.android.InfoflowAndroidConfiguration.SourceSinkConfiguration; import soot.jimple.infoflow.android.data.CategoryDefinition; -import soot.jimple.infoflow.android.source.parsers.xml.AbstractXMLSourceSinkParser.ICategoryFilter; -import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkCategory; +import soot.jimple.infoflow.android.source.parsers.xml.XMLSourceSinkParser.ICategoryFilter; import soot.jimple.infoflow.sourcesSinks.definitions.SourceSinkType; /** @@ -29,29 +28,26 @@ public ConfigurationBasedCategoryFilter(SourceSinkConfiguration config) { } @Override - public boolean acceptsCategory(ISourceSinkCategory category) { + public boolean acceptsCategory(CategoryDefinition category) { // We cannot compare descriptions to the configuration file - if (category instanceof CategoryDefinition) { - CategoryDefinition catDef = (CategoryDefinition) category; - catDef = catDef.getIdOnlyDescription(); + category = category.getIdOnlyDescription(); - CategoryMode sourceType = config.getSourceCategoriesAndModes().get(catDef); - CategoryMode sinkType = config.getSinkCategoriesAndModes().get(catDef); + CategoryMode sourceType = config.getSourceCategoriesAndModes().get(category); + CategoryMode sinkType = config.getSinkCategoriesAndModes().get(category); - // Check whether the category is excluded for everything - if (sourceType != null && sourceType == CategoryMode.Exclude) - if (sinkType != null && sinkType == CategoryMode.Exclude) - return false; + // Check whether the category is excluded for everything + if (sourceType != null && sourceType == CategoryMode.Exclude) + if (sinkType != null && sinkType == CategoryMode.Exclude) + return false; - // Check whether the category is included for something - if (config.getSinkFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { - if (sourceType != null && sourceType == CategoryMode.Include) - return true; - if (sinkType != null && sinkType == CategoryMode.Include) - return true; + // Check whether the category is included for something + if (config.getSinkFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { + if (sourceType != null && sourceType == CategoryMode.Include) + return true; + if (sinkType != null && sinkType == CategoryMode.Include) + return true; - return false; - } + return false; } // There is no reason to exclude the category @@ -59,36 +55,34 @@ public boolean acceptsCategory(ISourceSinkCategory category) { } @Override - public SourceSinkType filter(ISourceSinkCategory category, SourceSinkType sourceSinkType) { + public SourceSinkType filter(CategoryDefinition category, SourceSinkType sourceSinkType) { // We cannot compare descriptions to the configuration file - if (category instanceof CategoryDefinition) { - CategoryDefinition catDef = (CategoryDefinition) category; - catDef = catDef.getIdOnlyDescription(); - CategoryMode sourceMode = config.getSourceCategoriesAndModes().get(category); - CategoryMode sinkMode = config.getSinkCategoriesAndModes().get(category); - if (sourceSinkType == SourceSinkType.Source || sourceSinkType == SourceSinkType.Both) { - if (config.getSourceFilterMode() == SourceSinkFilterMode.UseAllButExcluded) { - if (sourceMode != null && sourceMode == CategoryMode.Exclude) - sourceSinkType = sourceSinkType.removeType(SourceSinkType.Source); - } else if (config.getSourceFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { - if (sourceMode == null || sourceMode != CategoryMode.Include) - sourceSinkType = sourceSinkType.removeType(SourceSinkType.Source); - } + category = category.getIdOnlyDescription(); + + CategoryMode sourceMode = config.getSourceCategoriesAndModes().get(category); + CategoryMode sinkMode = config.getSinkCategoriesAndModes().get(category); + + if (sourceSinkType == SourceSinkType.Source || sourceSinkType == SourceSinkType.Both) { + if (config.getSourceFilterMode() == SourceSinkFilterMode.UseAllButExcluded) { + if (sourceMode != null && sourceMode == CategoryMode.Exclude) + sourceSinkType = sourceSinkType.removeType(SourceSinkType.Source); + } else if (config.getSourceFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { + if (sourceMode == null || sourceMode != CategoryMode.Include) + sourceSinkType = sourceSinkType.removeType(SourceSinkType.Source); } + } - if (sourceSinkType == SourceSinkType.Sink || sourceSinkType == SourceSinkType.Both) { - if (config.getSinkFilterMode() == SourceSinkFilterMode.UseAllButExcluded) { - if (sinkMode != null && sinkMode == CategoryMode.Exclude) - sourceSinkType = sourceSinkType.removeType(SourceSinkType.Sink); - } else if (config.getSinkFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { - if (sinkMode == null || sinkMode != CategoryMode.Include) - sourceSinkType = sourceSinkType.removeType(SourceSinkType.Sink); - } + if (sourceSinkType == SourceSinkType.Sink || sourceSinkType == SourceSinkType.Both) { + if (config.getSinkFilterMode() == SourceSinkFilterMode.UseAllButExcluded) { + if (sinkMode != null && sinkMode == CategoryMode.Exclude) + sourceSinkType = sourceSinkType.removeType(SourceSinkType.Sink); + } else if (config.getSinkFilterMode() == SourceSinkFilterMode.UseOnlyIncluded) { + if (sinkMode == null || sinkMode != CategoryMode.Include) + sourceSinkType = sourceSinkType.removeType(SourceSinkType.Sink); } - return sourceSinkType; } - return SourceSinkType.Undefined; + return sourceSinkType; } } diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java deleted file mode 100644 index f5be84b30..000000000 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java +++ /dev/null @@ -1,577 +0,0 @@ -package soot.jimple.infoflow.android.source.parsers.xml; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import soot.jimple.infoflow.android.data.AndroidMethod; -import soot.jimple.infoflow.android.data.CategoryDefinition; -import soot.jimple.infoflow.android.data.CategoryDefinition.CATEGORY; -import soot.jimple.infoflow.data.AbstractMethodAndClass; -import soot.jimple.infoflow.sourcesSinks.definitions.AccessPathTuple; -import soot.jimple.infoflow.sourcesSinks.definitions.IAccessPathBasedSourceSinkDefinition; -import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkCategory; -import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; -import soot.jimple.infoflow.sourcesSinks.definitions.MethodSourceSinkDefinition; -import soot.jimple.infoflow.sourcesSinks.definitions.MethodSourceSinkDefinition.CallType; -import soot.jimple.infoflow.sourcesSinks.definitions.SourceSinkType; - -/** - * Abstract class for all Flowdroid XML parsers. Returns a Set of Methods when - * calling the function parse. Subclasses may implement parser specifics to - * different language engines. - * - * @author Anna-Katharina Wickert - * @author Joern Tillmans - * @author Steven Arzt - * @author Niklas Vogel - * - */ -public abstract class AbstractXMLSourceSinkParser { - private final static Logger logger = LoggerFactory.getLogger(AbstractXMLSourceSinkParser.class); - - /** - * Filter interface for excluding certain categories from the source/sink set. - * - * @author Steven Arzt - * - */ - public interface ICategoryFilter { - - /** - * Checks whether methods from the given category shall be parsed or not - * - * @param category The category in which the source or sink is defined - * @return True if the given category shall be parsed, otherwise false - */ - public boolean acceptsCategory(ISourceSinkCategory category); - - /** - * Filters sources and sinks by category - * - * @param category The category in which the source or sink is defined - * @param sourceSinkType Specifies whether the category is used for sources or - * sinks - * @return The acceptable use of the given category. This is a reduction from - * the given type. If the requested type, for example, is "Both", this - * method may return "Source", if the category shall only be used for - * sources, but not for sinks. - */ - public SourceSinkType filter(ISourceSinkCategory category, SourceSinkType sourceSinkType); - - } - - /** - * Handler class for the XML parser - * - * @author Anna-Katharina Wickert - * @author Joern Tillmans - * @author Steven Arzt - * - */ - protected class SAXHandler extends DefaultHandler { - - // Holding temporary values for handling with SAX - protected String methodSignature = null; - protected String fieldSignature = null; - - protected ISourceSinkCategory category; - protected boolean isSource, isSink; - protected List pathElements; - protected List pathElementTypes; - protected int paramIndex; - protected List paramTypes = new ArrayList(); - protected CallType callType; - - protected String accessPathParentElement = ""; - protected String description = ""; - - protected Set baseAPs = new HashSet<>(); - protected List> paramAPs = new ArrayList<>(); - protected Set returnAPs = new HashSet<>(); - - protected Map sourcesAndSinks; - protected ICategoryFilter categoryFilter = null; - - /** - * Event Handler for the starting element for SAX. Possible start elements for - * filling AndroidMethod objects with the new data format: - method: Setting - * parsingvalues to false or null and get and set the signature and category, - - * accessPath: To get the information whether the AndroidMethod is a sink or - * source, - and the other elements doesn't care for creating the AndroidMethod - * object. At these element we will look, if calling the method getAccessPath - * (using an new SAX Handler) - */ - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - String qNameLower = qName.toLowerCase(); - switch (qNameLower) { - case XMLConstants.CATEGORY_TAG: - handleStarttagCategory(attributes); - break; - - case XMLConstants.FIELD_TAG: - handleStarttagField(attributes); - break; - - case XMLConstants.METHOD_TAG: - handleStarttagMeethod(attributes); - break; - - case XMLConstants.ACCESSPATH_TAG: - handleStarttagAccesspath(attributes); - break; - - case XMLConstants.BASE_TAG: - accessPathParentElement = qNameLower; - description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); - break; - - case XMLConstants.RETURN_TAG: - accessPathParentElement = qNameLower; - description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); - break; - - case XMLConstants.PARAM_TAG: - handleStarttagParam(attributes, qNameLower); - break; - - case XMLConstants.PATHELEMENT_TAG: - handleStarttagPathelement(attributes); - break; - } - } - - protected void handleStarttagCategory(Attributes attributes) { - String strSysCategory = attributes.getValue(XMLConstants.ID_ATTRIBUTE).trim(); - String strCustomCategory = attributes.getValue(XMLConstants.CUSTOM_ID_ATTRIBUTE); - if (strCustomCategory != null && !strCustomCategory.isEmpty()) - strCustomCategory = strCustomCategory.trim(); - String strCustomDescription = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); - if (strCustomDescription != null && !strCustomDescription.isEmpty()) - strCustomDescription = strCustomDescription.trim(); - - category = getOrMakeCategory(CATEGORY.valueOf(strSysCategory), strCustomCategory, strCustomDescription); - - // Check for excluded categories - if (categoryFilter != null && !categoryFilter.acceptsCategory(category)) { - category = null; - } - } - - protected void handleStarttagField(Attributes attributes) { - if (category != null && attributes != null) { - fieldSignature = parseSignature(attributes); - accessPathParentElement = XMLConstants.BASE_TAG; - } - } - - protected void handleStarttagMeethod(Attributes attributes) { - if (category != null && attributes != null) { - methodSignature = parseSignature(attributes); - - // Get the call type - callType = CallType.MethodCall; - String strCallType = attributes.getValue(XMLConstants.CALL_TYPE); - if (strCallType != null && !strCallType.isEmpty()) { - strCallType = strCallType.trim(); - if (strCallType.equalsIgnoreCase("MethodCall")) - callType = CallType.MethodCall; - else if (strCallType.equalsIgnoreCase("Callback")) - callType = CallType.Callback; - } - } - } - - protected void handleStarttagAccesspath(Attributes attributes) { - if ((methodSignature != null && !methodSignature.isEmpty()) - || (fieldSignature != null && !fieldSignature.isEmpty())) { - pathElements = new ArrayList<>(); - pathElementTypes = new ArrayList<>(); - - if (attributes != null) { - String tempStr = attributes.getValue(XMLConstants.IS_SOURCE_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) - isSource = tempStr.equalsIgnoreCase(XMLConstants.TRUE); - - tempStr = attributes.getValue(XMLConstants.IS_SINK_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) - isSink = tempStr.equalsIgnoreCase(XMLConstants.TRUE); - - String newDesc = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); - if (newDesc != null && !newDesc.isEmpty()) - description = newDesc; - } - } - } - - protected void handleStarttagParam(Attributes attributes, String qNameLower) { - if ((methodSignature != null || fieldSignature != null) && attributes != null) { - String tempStr = attributes.getValue(XMLConstants.INDEX_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) - paramIndex = Integer.parseInt(tempStr); - - tempStr = attributes.getValue(XMLConstants.TYPE_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) - paramTypes.add(tempStr.trim()); - } - accessPathParentElement = qNameLower; - description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); - } - - protected void handleStarttagPathelement(Attributes attributes) { - if (methodSignature != null && attributes != null) { - String tempStr = attributes.getValue(XMLConstants.FIELD_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) { - pathElements.add(tempStr); - } - - tempStr = attributes.getValue(XMLConstants.TYPE_ATTRIBUTE); - if (tempStr != null && !tempStr.isEmpty()) { - pathElementTypes.add(tempStr); - } - } - } - - /** - * Reads the method or field signature from the given attribute map - * - * @param attributes The attribute map from which to read the signature - * @return The signature that identifies a Soot method or field - */ - private String parseSignature(Attributes attributes) { - String signature = attributes.getValue(XMLConstants.SIGNATURE_ATTRIBUTE).trim(); - - // If the user did not specify the brackets, we add them on - // the fly - if (signature != null && !signature.isEmpty()) - if (!signature.startsWith("<")) - signature = "<" + signature + ">"; - return signature; - } - - /** - * PathElement is the only element having values inside, so nothing to do here. - * Doesn't care at the current state of parsing. - **/ - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - } - - /** - * EventHandler for the End of an element. Putting the values into the objects. - * For additional information: startElement description. Starting with the - * innerst elements and switching up to the outer elements - * - * - pathElement -> means field sensitive, adding SootFields - */ - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - String qNameLower = qName.toLowerCase(); - switch (qNameLower) { - - case XMLConstants.CATEGORY_TAG: - category = null; - break; - - case XMLConstants.METHOD_TAG: - handleEndtagMethod(); - break; - - case XMLConstants.FIELD_TAG: - handleEndtagField(); - break; - - case XMLConstants.ACCESSPATH_TAG: - handleEndtagAccesspath(); - break; - - case XMLConstants.BASE_TAG: - accessPathParentElement = ""; - break; - - case XMLConstants.RETURN_TAG: - accessPathParentElement = ""; - break; - - case XMLConstants.PARAM_TAG: - accessPathParentElement = ""; - paramIndex = -1; - paramTypes.clear(); - break; - - case XMLConstants.PATHELEMENT_TAG: - break; - } - } - - protected void handleEndtagMethod() { - if (methodSignature == null) - return; - - // Check whether we have data - if (!baseAPs.isEmpty() || !paramAPs.isEmpty() || !returnAPs.isEmpty()) { - AndroidMethod tempMeth = AndroidMethod.createFromSignature(methodSignature); - - @SuppressWarnings("unchecked") - ISourceSinkDefinition ssd = createMethodSourceSinkDefinition(tempMeth, baseAPs, - paramAPs.toArray(new Set[paramAPs.size()]), returnAPs, callType, category); - addSourceSinkDefinition(methodSignature, ssd); - } - - // Start a new method and discard our old data - methodSignature = null; - fieldSignature = null; - baseAPs = new HashSet<>(); - paramAPs = new ArrayList<>(); - returnAPs = new HashSet<>(); - description = null; - } - - protected void handleEndtagField() { - // Create the field source - if (!baseAPs.isEmpty()) { - ISourceSinkDefinition ssd = createFieldSourceSinkDefinition(fieldSignature, baseAPs, paramAPs); - ssd.setCategory(category); - addSourceSinkDefinition(fieldSignature, ssd); - } - - // Start a new field and discard our old data - methodSignature = null; - fieldSignature = null; - baseAPs = new HashSet<>(); - paramAPs = new ArrayList<>(); - returnAPs = new HashSet<>(); - description = null; - } - - protected void handleEndtagAccesspath() { - // Record the access path for the current element - if (isSource || isSink) { - // Clean up the path elements - if (pathElements != null && pathElements.isEmpty() && pathElementTypes != null - && pathElementTypes.isEmpty()) { - pathElements = null; - pathElementTypes = null; - } - - // Sanity check - if (pathElements != null && pathElementTypes != null && pathElements.size() != pathElementTypes.size()) - throw new RuntimeException( - String.format("Length mismatch between path elements (%d) and their types (%d)", - pathElements.size(), pathElementTypes.size())); - if (pathElements == null || pathElements.isEmpty()) - if (pathElementTypes != null && !pathElementTypes.isEmpty()) - throw new RuntimeException("Got types for path elements, but no elements (i.e., fields)"); - - // Filter the sources and sinks - SourceSinkType sstype = SourceSinkType.fromFlags(isSink, isSource); - if (categoryFilter != null) - sstype = categoryFilter.filter(category, sstype); - - if (sstype != SourceSinkType.Neither) { - AccessPathTuple apt = AccessPathTuple.fromPathElements(pathElements, pathElementTypes, sstype); - - // Optional description - if (description != null && !description.isEmpty()) - apt.setDescription(description); - - // Simplify the AP after setting the description for not breaking the generic - // source definition - apt = apt.simplify(); - - switch (accessPathParentElement) { - case XMLConstants.BASE_TAG: - baseAPs.add(apt); - break; - case XMLConstants.RETURN_TAG: - returnAPs.add(apt); - break; - case XMLConstants.PARAM_TAG: - while (paramAPs.size() <= paramIndex) - paramAPs.add(new HashSet()); - paramAPs.get(paramIndex).add(apt); - } - } - } - - pathElements = null; - pathElementTypes = null; - - isSource = false; - isSink = false; - pathElements = null; - pathElementTypes = null; - - description = null; - } - - } - - protected Map sourcesAndSinks; - - protected Set sources = new HashSet<>(); - protected Set sinks = new HashSet<>(); - protected ICategoryFilter categoryFilter = null; - - protected final Map categories = new HashMap<>(); - - /** - * gets the category for the given values. If this is the first time such a - * category is requested, the respective definition is created. Otherwise, the - * existing one is returned. - * - * @param systemCategory The system-defined category name - * @param customCategory The user-defined category name - * @param customDescription The human-readable description for the custom - * category - * @return The category definition object for the given category names - */ - private ISourceSinkCategory getOrMakeCategory(CATEGORY systemCategory, String customCategory, - String customDescription) { - // For the key in the map, we ignore the description. We do not want to - // have the - // same category id just with different descriptions. - - ISourceSinkCategory keyDef = new CategoryDefinition(systemCategory, customCategory); - return categories.computeIfAbsent(keyDef, - d -> new CategoryDefinition(systemCategory, customCategory, customDescription)); - } - - /** - * Parses the given input stream to obtain the source/sink definitions - * - * @param stream The stream whose data to parse - */ - protected void parseInputStream(InputStream stream) { - SAXParserFactory pf = SAXParserFactory.newInstance(); - try { - pf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - pf.setFeature("http://xml.org/sax/features/external-general-entities", false); - pf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - SAXParser parser = pf.newSAXParser(); - runParse(parser, stream); - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.printStackTrace(); - } - // Build the source and sink lists - buildSourceSinkLists(); - } - - /** - * Builds the lists of sources and sinks from the data that we have parsed - */ - protected abstract void buildSourceSinkLists(); - - /** - * Runs the parse method on the given parser - * - * @param parser Parser to run parse on - * @param stream Input stream of XML file - */ - protected abstract void runParse(SAXParser parser, InputStream stream); - - /** - * Adds a new source or sink definition - * - * @param signature The signature of the method or field that is considered a - * source or a sink - * @param ssd The source or sink definition - */ - protected void addSourceSinkDefinition(String signature, IAccessPathBasedSourceSinkDefinition ssd) { - if (sourcesAndSinks.containsKey(signature)) - sourcesAndSinks.get(signature).merge(ssd); - else - sourcesAndSinks.put(signature, ssd); - } - - public Set getSources() { - return sources; - } - - public Set getSinks() { - return sinks; - } - - protected static InputStream getStream(String fileName) throws IOException { - File f = new File(fileName); - if (f.exists()) - return new FileInputStream(f); - - return ResourceUtils.getResourceStream(fileName); - } - - public Set getAllMethods() { - Set sourcesSinks = new HashSet<>(sources.size() + sinks.size()); - sourcesSinks.addAll(sources); - sourcesSinks.addAll(sinks); - return sourcesSinks; - } - - protected void addSourceSinkDefinition(String signature, ISourceSinkDefinition ssd) { - if (sourcesAndSinks.containsKey(signature)) - sourcesAndSinks.get(signature).merge(ssd); - else - sourcesAndSinks.put(signature, ssd); - } - - protected abstract ISourceSinkDefinition createFieldSourceSinkDefinition(String signature, - Set baseAPs, List> paramAPs); - - /** - * Factory method for {@link MethodSourceSinkDefinition} instances - * - * @param method The method that is to be defined as a source or sink - * @param baseAPs The access paths rooted in the base object that shall be - * considered as sources or sinks - * @param paramAPs The access paths rooted in parameters that shall be - * considered as sources or sinks. The index in the set - * corresponds to the index of the formal parameter to which - * the respective set of access paths belongs. - * @param returnAPs The access paths rooted in the return object that shall be - * considered as sources or sinks - * @param callType The type of call (normal call, callback, etc.) - * @return The newly created {@link MethodSourceSinkDefinition} instance - */ - protected abstract ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodAndClass method, - Set baseAPs, Set[] paramAPs, Set returnAPs, - CallType callType, ISourceSinkCategory category); - - /** - * Reads the method or field signature from the given attribute map - * - * @param attributes The attribute map from which to read the signature - * @return The signature that identifies a Soot method or field - */ - protected String parseSignature(Attributes attributes) { - String signature = attributes.getValue(XMLConstants.SIGNATURE_ATTRIBUTE).trim(); - - // If the user did not specify the brackets, we add them on - // the fly - if (signature != null && !signature.isEmpty()) - if (!signature.startsWith("<")) - signature = "<" + signature + ">"; - return signature; - } - -} diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java index 4d2b3d2ae..38bb4c496 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java @@ -4,11 +4,16 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; @@ -16,16 +21,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import soot.jimple.infoflow.InfoflowConfiguration; import soot.jimple.infoflow.android.data.AndroidMethod; -import soot.jimple.infoflow.data.AbstractMethodAndClass; +import soot.jimple.infoflow.android.data.CategoryDefinition; +import soot.jimple.infoflow.android.data.CategoryDefinition.CATEGORY; import soot.jimple.infoflow.sourcesSinks.definitions.AccessPathTuple; import soot.jimple.infoflow.sourcesSinks.definitions.FieldSourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.definitions.IAccessPathBasedSourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkCategory; -import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinitionProvider; import soot.jimple.infoflow.sourcesSinks.definitions.MethodSourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.definitions.MethodSourceSinkDefinition.CallType; @@ -39,13 +46,361 @@ * @author Anna-Katharina Wickert * @author Joern Tillmans * @author Steven Arzt - * @author Niklas Vogel */ -public class XMLSourceSinkParser extends AbstractXMLSourceSinkParser implements ISourceSinkDefinitionProvider { +public class XMLSourceSinkParser implements ISourceSinkDefinitionProvider { private final static Logger logger = LoggerFactory.getLogger(XMLSourceSinkParser.class); + /** + * Filter interface for excluding certain categories from the source/sink set + * + * @author Steven Arzt + * + */ + public interface ICategoryFilter { + + /** + * Checks whether methods from the given category shall be parsed or not + * + * @param category The category in which the source or sink is defined + * @return True if the given category shall be parsed, otherwise false + */ + public boolean acceptsCategory(CategoryDefinition category); + + /** + * Filters sources and sinks by category + * + * @param category The category in which the source or sink is defined + * @param sourceSinkType Specifies whether the category is used for sources or + * sinks + * @return The acceptable use of the given category. This is a reduction from + * the given type. If the requested type, for example, is "Both", this + * method may return "Source", if the category shall only be used for + * sources, but not for sinks. + */ + public SourceSinkType filter(CategoryDefinition category, SourceSinkType sourceSinkType); + + } + + /** + * Handler class for the XML parser + * + * @author Anna-Katharina Wickert + * @author Joern Tillmans + * @author Steven Arzt + * + */ + protected class SAXHandler extends DefaultHandler { + + // Holding temporary values for handling with SAX + protected String methodSignature = null; + protected String fieldSignature = null; + + protected CategoryDefinition category; + protected boolean isSource, isSink; + protected List pathElements; + protected List pathElementTypes; + protected int paramIndex; + protected List paramTypes = new ArrayList(); + protected CallType callType; + + protected String accessPathParentElement = ""; + protected String description = ""; + + protected Set baseAPs = new HashSet<>(); + protected List> paramAPs = new ArrayList<>(); + protected Set returnAPs = new HashSet<>(); + + /** + * Event Handler for the starting element for SAX. Possible start elements for + * filling AndroidMethod objects with the new data format: - method: Setting + * parsingvalues to false or null and get and set the signature and category, - + * accessPath: To get the information whether the AndroidMethod is a sink or + * source, - and the other elements doesn't care for creating the AndroidMethod + * object. At these element we will look, if calling the method getAccessPath + * (using an new SAX Handler) + */ + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + String qNameLower = qName.toLowerCase(); + switch (qNameLower) { + case XMLConstants.CATEGORY_TAG: + String strSysCategory = attributes.getValue(XMLConstants.ID_ATTRIBUTE).trim(); + String strCustomCategory = attributes.getValue(XMLConstants.CUSTOM_ID_ATTRIBUTE); + if (strCustomCategory != null && !strCustomCategory.isEmpty()) + strCustomCategory = strCustomCategory.trim(); + String strCustomDescription = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + if (strCustomDescription != null && !strCustomDescription.isEmpty()) + strCustomDescription = strCustomDescription.trim(); + + category = getOrMakeCategory(CATEGORY.valueOf(strSysCategory), strCustomCategory, strCustomDescription); + + // Check for excluded categories + if (categoryFilter != null && !categoryFilter.acceptsCategory(category)) { + category = null; + } + break; + + case XMLConstants.FIELD_TAG: + if (category != null && attributes != null) { + fieldSignature = parseSignature(attributes); + accessPathParentElement = XMLConstants.BASE_TAG; + } + break; + + case XMLConstants.METHOD_TAG: + if (category != null && attributes != null) { + methodSignature = parseSignature(attributes); + + // Get the call type + callType = CallType.MethodCall; + String strCallType = attributes.getValue(XMLConstants.CALL_TYPE); + if (strCallType != null && !strCallType.isEmpty()) { + strCallType = strCallType.trim(); + if (strCallType.equalsIgnoreCase("MethodCall")) + callType = CallType.MethodCall; + else if (strCallType.equalsIgnoreCase("Callback")) + callType = CallType.Callback; + } + } + break; + + case XMLConstants.ACCESSPATH_TAG: + if ((methodSignature != null && !methodSignature.isEmpty()) + || (fieldSignature != null && !fieldSignature.isEmpty())) { + pathElements = new ArrayList<>(); + pathElementTypes = new ArrayList<>(); + + if (attributes != null) { + String tempStr = attributes.getValue(XMLConstants.IS_SOURCE_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) + isSource = tempStr.equalsIgnoreCase(XMLConstants.TRUE); + + tempStr = attributes.getValue(XMLConstants.IS_SINK_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) + isSink = tempStr.equalsIgnoreCase(XMLConstants.TRUE); + + String newDesc = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + if (newDesc != null && !newDesc.isEmpty()) + description = newDesc; + } + } + break; + + case XMLConstants.BASE_TAG: + accessPathParentElement = qNameLower; + description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + break; + + case XMLConstants.RETURN_TAG: + accessPathParentElement = qNameLower; + description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + break; + + case XMLConstants.PARAM_TAG: + if (methodSignature != null && attributes != null) { + String tempStr = attributes.getValue(XMLConstants.INDEX_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) + paramIndex = Integer.parseInt(tempStr); + + tempStr = attributes.getValue(XMLConstants.TYPE_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) + paramTypes.add(tempStr.trim()); + } + accessPathParentElement = qNameLower; + description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + break; + + case XMLConstants.PATHELEMENT_TAG: + if (methodSignature != null && attributes != null) { + String tempStr = attributes.getValue(XMLConstants.FIELD_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) { + pathElements.add(tempStr); + } + + tempStr = attributes.getValue(XMLConstants.TYPE_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) { + pathElementTypes.add(tempStr); + } + } + break; + } + } + + /** + * Reads the method or field signature from the given attribute map + * + * @param attributes The attribute map from which to read the signature + * @return The signature that identifies a Soot method or field + */ + private String parseSignature(Attributes attributes) { + String signature = attributes.getValue(XMLConstants.SIGNATURE_ATTRIBUTE).trim(); + + // If the user did not specify the brackets, we add them on + // the fly + if (signature != null && !signature.isEmpty()) + if (!signature.startsWith("<")) + signature = "<" + signature + ">"; + return signature; + } + + /** + * PathElement is the only element having values inside, so nothing to do here. + * Doesn't care at the current state of parsing. + **/ + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + } + + /** + * EventHandler for the End of an element. Putting the values into the objects. + * For additional information: startElement description. Starting with the + * innerst elements and switching up to the outer elements + * + * - pathElement -> means field sensitive, adding SootFields + */ + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + String qNameLower = qName.toLowerCase(); + switch (qNameLower) { + + case XMLConstants.CATEGORY_TAG: + category = null; + break; + + case XMLConstants.METHOD_TAG: + if (methodSignature == null) + break; + + // Check whether we have data + if (!baseAPs.isEmpty() || !paramAPs.isEmpty() || !returnAPs.isEmpty()) { + AndroidMethod tempMeth = AndroidMethod.createFromSignature(methodSignature); + + @SuppressWarnings("unchecked") + IAccessPathBasedSourceSinkDefinition ssd = createMethodSourceSinkDefinition(tempMeth, baseAPs, + paramAPs.toArray(new Set[paramAPs.size()]), returnAPs, callType, category); + addSourceSinkDefinition(methodSignature, ssd); + } + + // Start a new method and discard our old data + methodSignature = null; + fieldSignature = null; + baseAPs = new HashSet<>(); + paramAPs = new ArrayList<>(); + returnAPs = new HashSet<>(); + description = null; + break; + + case XMLConstants.FIELD_TAG: + // Create the field source + if (!baseAPs.isEmpty()) { + IAccessPathBasedSourceSinkDefinition ssd = createFieldSourceSinkDefinition(fieldSignature, baseAPs); + ssd.setCategory(category); + addSourceSinkDefinition(fieldSignature, ssd); + } + + // Start a new field and discard our old data + methodSignature = null; + fieldSignature = null; + baseAPs = new HashSet<>(); + paramAPs = new ArrayList<>(); + returnAPs = new HashSet<>(); + description = null; + break; + + case XMLConstants.ACCESSPATH_TAG: + // Record the access path for the current element + if (isSource || isSink) { + // Clean up the path elements + if (pathElements != null && pathElements.isEmpty() && pathElementTypes != null + && pathElementTypes.isEmpty()) { + pathElements = null; + pathElementTypes = null; + } + + // Sanity check + if (pathElements != null && pathElementTypes != null + && pathElements.size() != pathElementTypes.size()) + throw new RuntimeException( + String.format("Length mismatch between path elements (%d) and their types (%d)", + pathElements.size(), pathElementTypes.size())); + if (pathElements == null || pathElements.isEmpty()) + if (pathElementTypes != null && !pathElementTypes.isEmpty()) + throw new RuntimeException("Got types for path elements, but no elements (i.e., fields)"); + + // Filter the sources and sinks + SourceSinkType sstype = SourceSinkType.fromFlags(isSink, isSource); + if (categoryFilter != null) + sstype = categoryFilter.filter(category, sstype); + + if (sstype != SourceSinkType.Neither) { + AccessPathTuple apt = AccessPathTuple.fromPathElements(pathElements, pathElementTypes, sstype); + + // Optional description + if (description != null && !description.isEmpty()) + apt.setDescription(description); + + // Simplify the AP after setting the description for not breaking the generic + // source definition + apt = apt.simplify(); + + switch (accessPathParentElement) { + case XMLConstants.BASE_TAG: + baseAPs.add(apt); + break; + case XMLConstants.RETURN_TAG: + returnAPs.add(apt); + break; + case XMLConstants.PARAM_TAG: + while (paramAPs.size() <= paramIndex) + paramAPs.add(new HashSet()); + paramAPs.get(paramIndex).add(apt); + } + } + } + + pathElements = null; + pathElementTypes = null; + + isSource = false; + isSink = false; + pathElements = null; + pathElementTypes = null; + + description = null; + break; + + case XMLConstants.BASE_TAG: + accessPathParentElement = ""; + break; + + case XMLConstants.RETURN_TAG: + accessPathParentElement = ""; + break; + + case XMLConstants.PARAM_TAG: + accessPathParentElement = ""; + paramIndex = -1; + paramTypes.clear(); + break; + + case XMLConstants.PATHELEMENT_TAG: + break; + } + } + + } + + protected Map sourcesAndSinks; + + protected Set sources = new HashSet<>(); + protected Set sinks = new HashSet<>(); + protected ICategoryFilter categoryFilter = null; + + protected final Map categories = new HashMap<>(); + // XML stuff incl. Verification against XSD protected static final String XSD_FILE_PATH = "schema/SourcesAndSinks.xsd"; protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; @@ -84,9 +439,36 @@ public static XMLSourceSinkParser fromStream(InputStream inputStream, ICategoryF return pmp; } + @Override + public Set getSources() { + return sources; + } + + @Override + public Set getSinks() { + return sinks; + } + /** - * Builds the lists of sources and sinks from the data that we have parsed + * gets the category for the given values. If this is the first time such a + * category is requested, the respective definition is created. Otherwise, the + * existing one is returned. + * + * @param systemCategory The system-defined category name + * @param customCategory The user-defined category name + * @param customDescription The human-readable description for the custom + * category + * @return The category definition object for the given category names */ + private CategoryDefinition getOrMakeCategory(CATEGORY systemCategory, String customCategory, + String customDescription) { + // For the key in the map, we ignore the description. We do not want to + // have the + // same category id just with different descriptions. + CategoryDefinition keyDef = new CategoryDefinition(systemCategory, customCategory); + return categories.computeIfAbsent(keyDef, + d -> new CategoryDefinition(systemCategory, customCategory, customDescription)); + } /** * Creates a new instance of the {@link XMLSourceSinkParser} class @@ -98,13 +480,39 @@ protected XMLSourceSinkParser(ICategoryFilter categoryFilter) { this.categoryFilter = categoryFilter; } + /** + * Parses the given input stream to obtain the source/sink definitions + * + * @param stream The stream whose data to parse + */ + protected void parseInputStream(InputStream stream) { + SAXParserFactory pf = SAXParserFactory.newInstance(); + try { + // Prevent XXE + pf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + pf.setFeature("http://xml.org/sax/features/external-general-entities", false); + pf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + + SAXParser parser = pf.newSAXParser(); + parser.parse(stream, new SAXHandler()); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + // Build the source and sink lists + buildSourceSinkLists(); + } + /** * Builds the lists of sources and sinks from the data that we have parsed */ - @Override protected void buildSourceSinkLists() { - for (ISourceSinkDefinition def : sourcesAndSinks.values()) { - ISourceSinkDefinition sourceDef = def.getSourceOnlyDefinition(); + for (IAccessPathBasedSourceSinkDefinition def : sourcesAndSinks.values()) { + IAccessPathBasedSourceSinkDefinition sourceDef = def.getSourceOnlyDefinition(); if (sourceDef != null && !sourceDef.isEmpty()) { if (sourceDef instanceof MethodSourceSinkDefinition) { MethodSourceSinkDefinition methodSrc = (MethodSourceSinkDefinition) sourceDef; @@ -116,7 +524,7 @@ protected void buildSourceSinkLists() { sources.add(sourceDef); } - ISourceSinkDefinition sinkDef = def.getSinkOnlyDefinition(); + IAccessPathBasedSourceSinkDefinition sinkDef = def.getSinkOnlyDefinition(); if (sinkDef != null && !sinkDef.isEmpty()) { if (sourceDef instanceof MethodSourceSinkDefinition) { MethodSourceSinkDefinition methodSink = (MethodSourceSinkDefinition) sourceDef; @@ -162,6 +570,28 @@ private static void verifyXML(InputStream inp) throws IOException { } } + @Override + public Set getAllMethods() { + Set sourcesSinks = new HashSet<>(sources.size() + sinks.size()); + sourcesSinks.addAll(sources); + sourcesSinks.addAll(sinks); + return sourcesSinks; + } + + /** + * Adds a new source or sink definition + * + * @param signature The signature of the method or field that is considered a + * source or a sink + * @param ssd The source or sink definition + */ + protected void addSourceSinkDefinition(String signature, IAccessPathBasedSourceSinkDefinition ssd) { + if (sourcesAndSinks.containsKey(signature)) + sourcesAndSinks.get(signature).merge(ssd); + else + sourcesAndSinks.put(signature, ssd); + } + /** * Factory method for {@link MethodSourceSinkDefinition} instances * @@ -177,15 +607,10 @@ private static void verifyXML(InputStream inp) throws IOException { * @param callType The type of call (normal call, callback, etc.) * @return The newly created {@link MethodSourceSinkDefinition} instance */ - @Override - protected ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodAndClass method, + protected IAccessPathBasedSourceSinkDefinition createMethodSourceSinkDefinition(AndroidMethod method, Set baseAPs, Set[] paramAPs, Set returnAPs, CallType callType, ISourceSinkCategory category) { - if (method instanceof AndroidMethod) { - AndroidMethod amethod = (AndroidMethod) method; - return new MethodSourceSinkDefinition(amethod, baseAPs, paramAPs, returnAPs, callType, category); - } - return null; + return new MethodSourceSinkDefinition(method, baseAPs, paramAPs, returnAPs, callType, category); } /** @@ -196,25 +621,9 @@ protected ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodA * sinks * @return The newly created {@link FieldSourceSinkDefinition} instance */ - @Override protected IAccessPathBasedSourceSinkDefinition createFieldSourceSinkDefinition(String signature, - Set baseAPs, List> paramAPs) { + Set baseAPs) { return new FieldSourceSinkDefinition(signature, baseAPs); } - /** - * Run parse method on given parser - * - * @param parser The parser which should be used to parse - * @param stream The XML text input stream which should be parsed - */ - @Override - protected void runParse(SAXParser parser, InputStream stream) { - try { - parser.parse(stream, new SAXHandler()); - } catch (SAXException | IOException e) { - e.printStackTrace(); - } - } - } diff --git a/soot-infoflow/src/soot/jimple/infoflow/data/AbstractMethodAndClass.java b/soot-infoflow/src/soot/jimple/infoflow/data/AbstractMethodAndClass.java deleted file mode 100644 index 4d5c80ac9..000000000 --- a/soot-infoflow/src/soot/jimple/infoflow/data/AbstractMethodAndClass.java +++ /dev/null @@ -1,104 +0,0 @@ -package soot.jimple.infoflow.data; - -import java.util.List; - -/** - * Abstract class for all MethodAndClass containers. - * - * @author Steven Arzt - * @author Niklas Vogel - * - */ -public abstract class AbstractMethodAndClass { - protected final String methodName; - protected final String className; - protected final String returnType; - protected final List parameters; - - private String subSignature = null; - private String signature = null; - - public AbstractMethodAndClass(String methodName, String className, String returnType, List parameters) { - this.methodName = methodName; - this.className = className; - this.returnType = returnType; - this.parameters = parameters; - } - - public String getMethodName() { - return this.methodName; - } - - public String getClassName() { - return this.className; - } - - public String getReturnType() { - return this.returnType; - } - - public List getParameters() { - return this.parameters; - } - - /** - * Get subsignature of stored method and class - * - * @return Subsignature - */ - public String getSubSignature() { - if (subSignature != null) - return subSignature; - - StringBuilder sb = new StringBuilder( - 10 + this.returnType.length() + this.methodName.length() + (this.parameters.size() * 30)); - if (!this.returnType.isEmpty()) { - sb.append(this.returnType); - sb.append(" "); - } - sb.append(this.methodName); - sb.append("("); - - for (int i = 0; i < this.parameters.size(); i++) { - if (i > 0) - sb.append(","); - sb.append(this.parameters.get(i).trim()); - } - sb.append(")"); - this.subSignature = sb.toString(); - - return this.subSignature; - } - - /** - * Get the signature of the stored method and class - * - * @return Signature - */ - public String getSignature() { - if (signature != null) - return signature; - - StringBuilder sb = new StringBuilder(10 + this.className.length() + this.returnType.length() - + this.methodName.length() + (this.parameters.size() * 30)); - sb.append("<"); - sb.append(this.className); - sb.append(": "); - if (!this.returnType.isEmpty()) { - sb.append(this.returnType); - sb.append(" "); - } - sb.append(this.methodName); - sb.append("("); - - for (int i = 0; i < this.parameters.size(); i++) { - if (i > 0) - sb.append(","); - sb.append(this.parameters.get(i).trim()); - } - sb.append(")>"); - this.signature = sb.toString(); - - return this.signature; - } -} diff --git a/soot-infoflow/src/soot/jimple/infoflow/data/SootMethodAndClass.java b/soot-infoflow/src/soot/jimple/infoflow/data/SootMethodAndClass.java index c744cdb45..bc98eebcc 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/data/SootMethodAndClass.java +++ b/soot-infoflow/src/soot/jimple/infoflow/data/SootMethodAndClass.java @@ -20,42 +20,117 @@ * Data container which stores the string representation of a SootMethod and its * corresponding class */ -public class SootMethodAndClass extends AbstractMethodAndClass { +public class SootMethodAndClass { + private final String methodName; + private final String className; + private final String returnType; + private final List parameters; + private String subSignature = null; + private String signature = null; private int hashCode = 0; public SootMethodAndClass(String methodName, String className, String returnType, List parameters) { - super(methodName, className, returnType, parameters); + this.methodName = methodName; + this.className = className; + this.returnType = returnType; + this.parameters = parameters; } public SootMethodAndClass(String methodName, String className, String returnType, String parameters) { - super(methodName, className, returnType, parameterFromString(parameters)); - } + this.methodName = methodName; + this.className = className; + this.returnType = returnType; - private static List parameterFromString(String parameters) { - List paras = new ArrayList<>(); - if (paras != null && !paras.isEmpty()) { + this.parameters = new ArrayList<>(); + if (parameters != null && !parameters.isEmpty()) { String[] params = parameters.split(","); for (String s : params) - paras.add(s); + this.parameters.add(s); } - return paras; } public SootMethodAndClass(SootMethod sm) { - super(sm.getName(), sm.getDeclaringClass().getName(), sm.getReturnType().toString(), parameterFromMethod(sm)); - } - - private static List parameterFromMethod(SootMethod sm) { - ArrayList parameters = new ArrayList(); + this.methodName = sm.getName(); + this.className = sm.getDeclaringClass().getName(); + this.returnType = sm.getReturnType().toString(); + this.parameters = new ArrayList(); for (Type p : sm.getParameterTypes()) - parameters.add(p.toString()); - return parameters; + this.parameters.add(p.toString()); } public SootMethodAndClass(SootMethodAndClass methodAndClass) { - super(methodAndClass.methodName, methodAndClass.className, methodAndClass.returnType, - new ArrayList(methodAndClass.parameters)); + this.methodName = methodAndClass.methodName; + this.className = methodAndClass.className; + this.returnType = methodAndClass.returnType; + this.parameters = new ArrayList(methodAndClass.parameters); + } + + public String getMethodName() { + return this.methodName; + } + + public String getClassName() { + return this.className; + } + + public String getReturnType() { + return this.returnType; + } + + public List getParameters() { + return this.parameters; + } + + public String getSubSignature() { + if (subSignature != null) + return subSignature; + + StringBuilder sb = new StringBuilder( + 10 + this.returnType.length() + this.methodName.length() + (this.parameters.size() * 30)); + if (!this.returnType.isEmpty()) { + sb.append(this.returnType); + sb.append(" "); + } + sb.append(this.methodName); + sb.append("("); + + for (int i = 0; i < this.parameters.size(); i++) { + if (i > 0) + sb.append(","); + sb.append(this.parameters.get(i).trim()); + } + sb.append(")"); + this.subSignature = sb.toString(); + + return this.subSignature; + } + + public String getSignature() { + if (signature != null) + return signature; + + StringBuilder sb = new StringBuilder(10 + this.className.length() + this.returnType.length() + + this.methodName.length() + (this.parameters.size() * 30)); + sb.append("<"); + sb.append(this.className); + sb.append(": "); + if (!this.returnType.isEmpty()) { + sb.append(this.returnType); + sb.append(" "); + } + sb.append(this.methodName); + sb.append("("); + + for (int i = 0; i < this.parameters.size(); i++) { + if (i > 0) + sb.append(","); + sb.append(this.parameters.get(i).trim()); + } + sb.append(")>"); + this.signature = sb.toString(); + + return this.signature; } @Override diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java index 9ed969943..bd8f468c5 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java @@ -46,11 +46,4 @@ public interface ISourceSinkDefinition { */ public abstract void merge(ISourceSinkDefinition other); - /** - * Indicates if the definition contains any sources or sinks - * - * @return The boolean if this definition is empty - */ - public abstract boolean isEmpty(); - }