New upstream version 2.1.0

parent 48ba1e82
# LibScout
LibScout is a light-weight and effective static analysis tool to detect third-party libraries in Android apps. The detection is resilient against<br>
common bytecode obfuscation techniques such as identifier renaming or code-based obfuscations such as reflection-based API hiding or control-flow randomization.<br>
LibScout requires the original library SDKs (compiled .jar/.aar files) to extract library profiles that can be used for detection on Android apps.
LibScout is a light-weight and effective static analysis tool to detect third-party libraries in Android/Java apps. The detection is resilient against common bytecode obfuscation techniques such as identifier renaming or code-based obfuscations such as reflection-based API hiding or control-flow randomization. Further LibScout is capable of pinpointing exact library versions.<br>
LibScout requires the original library SDKs (compiled .jar/.aar files) to extract library profiles that can be used for detection on Android apps. Pre-generated library profiles are hosted at the repository [LibScout-Profiles](https://github.com/reddr/LibScout-Profiles).
Unique features:
* Library detection resilient against many kinds of bytecode obfuscation
* Capability of pinpointing the exact library version (in some cases to a set of 2-3 candidate versions)
* Capability of handling dead-code elimination, by computing a similarity score against baseline SDKs
For technical details and large-scale evaluation results, please refer to our publication:<br>
For technical details and large-scale evaluation results, please refer to our publications:<br>
> Reliable Third-Party Library Detection in Android and its Security Applications<br>
> https://www.infsec.cs.uni-saarland.de/~derr/publications/pdfs/derr_ccs16.pdf
>
> Keep me Updated: An Empirical Study of Third-Party Library Updatability on Android<br>
> https://www.infsec.cs.uni-saarland.de/~derr/publications/pdfs/derr_ccs17.pdf
If you use LibScout in a scientific publication, we would appreciate citations using this Bibtex entry: [[bib]](https://www.infsec.cs.uni-saarland.de/~derr/publications/bib/derr_ccs16.bib)<br>
If you use LibScout in a scientific publication, we would appreciate citations using these Bibtex entries: [[bib-ccs16]](https://www.infsec.cs.uni-saarland.de/~derr/publications/bib/derr_ccs16.bib)
[[bib-ccs17]](https://www.infsec.cs.uni-saarland.de/~derr/publications/bib/derr_ccs17.bib)<br>
## Library Profiles and Scripts
To facilitate usage of LibScout we are happy to release our datasets to the community. <br>
You can find the following resources in the data/scripts directory:<br>
Ready-to-use library profiles and library meta-data can be found in the repository [LibScout-Profiles](https://github.com/reddr/LibScout-Profiles).
It further includes scripts to automatically retrieve complete library version histories.
### Detecting vulnerable library versions
### Library Profiles (last updated: 06/27/2017)
LibScout has builtin functionality to report library versions with the following security vulnerabilities.<br>
Detected vulnerable versions are tagged with <b>[SECURITY]</b>, patches with <b>[SECURITY-FIX]</b>. <br>
This information is encoded in the library.xml files that have been used to generate the profiles.
We try to update the list/profiles whenever we encounter new security issues. If you can share information, please let us know.
You can find all <b>library profiles</b> (ready-to-use) for lib detection in apps in the data directory as compressed .zip file.<br>
It currently includes <b>205</b> unique libraries and <b>3,071</b> library versions.<br> For convenience, data/library-data.csv contains a
complete list of library/-versions including meta data such as release dates.
### Scripts (scripts/mnv-central)
The scripts directory further contains a python script to automatically download original library SDKs including complete version histories from maven-central.<br>
The set of libraries we currently retrieve is stored in a json file.<br>
| Library | Version(s) | Fix Version | Vulnerability | Link |
| ---------- | ---------------:|--------------:|--------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Airpush | < 8.1 | > 8.1 | Unsanitized default WebView settings | [Link](https://support.google.com/faqs/answer/6376737) |
| Apache CC | 3.2.1 / 4.0 | 3.2.2 / 4.1 | Deserialization vulnerability | [Link](http://www.kb.cert.org/vuls/id/576313) |
| Dropbox | 1.5.4 - 1.6.1 | 1.6.2 | DroppedIn vulnerability | [Link](https://blogs.dropbox.com/developers/2015/03/security-bug-resolved-in-the-dropbox-sdks-for-android) |
| Facebook | 3.15 | 3.16 | Account hijacking vulnerability | [Link](http://thehackernews.com/2014/07/facebook-sdk-vulnerability-puts.html) |
| MoPub | < 4.4.0 | 4.4.0 | Unsanitized default WebView settings | [Link](https://support.google.com/faqs/answer/6345928) |
| OkHttp | 2.1-2.7.4 / 3.0.0-3.1.2 | 2.7.5 / 3.2.0 | Certificate pinning bypass | [Link](https://medium.com/square-corner-blog/vulnerability-in-okhttps-certificate-pinner-2a7326ad073b) |
| SuperSonic | < 6.3.5 | 6.3.5 | Unsafe functionality exposure via JS | [Link](https://support.google.com/faqs/answer/7126517) |
| Vungle | < 3.3.0 | 3.3.0 | MitM attack vulnerability | [Link](https://support.google.com/faqs/answer/6313713) |
Due to copyright reasons we cannot publicy provide the original library SDKs. If you are interested in this data, send us an email.<br>
We also welcome contributions to LibScout or our library database (either original SDKs or scripts for automatic retrieval from sources other than mvn central).<br><br>
Contact us for comments, feedback, how to contribute: Erik Derr [lastname@cs.uni-saarland.de]
On our last scan of free apps on Google Play (05/25/2017), LibScout detected >20k apps containing one of these vulnerable lib versions.
These results have been reported to Google's [ASI program](https://developer.android.com/google/play/asi.html) (still under investigation).
## LibScout Repo Structure
<pre><code>
|_ build.xml (ant build file to generate runnable .jar)
|_ data (library profiles and supplemental data sets)
| |_ library-data.csv (library meta data)
| |_ library-profiles-21.06.zip (all library profiles)
| |_ app-version-codes.csv (app packages with valid version codes)
|_ assets
| |_ library.xml (Library meta-data template)
|_ data
| |_ app-version-codes.csv (Google Play app packages with valid version codes)
|_ lib
| pre-compiled WALA libs, Apache commons*, log4j, Android SDK
|_ logging
| |_ logback.xml (log4j configuration file)
|_ scripts
| |_ mvn-central
| |_ mvn-central-crawler.py (script to retrieve complete library histories from mvn-central)
|_ src
source directory of LibScout (de/infsec/tpl). Includes some open-source,
third-party code to parse AXML resources / app manifests etc.
......
......@@ -15,7 +15,7 @@
<!-- optional: version string, e.g. "4.0.4" -->
<version></version>
<!-- optional: date (format: DD/MM/YYYY) -->
<!-- optional: date (format: dd.MM.yyyy example: 21.05.2017) -->
<releasedate></releasedate>
<!-- optional: comment -->
......
......@@ -13,6 +13,9 @@
<logger name="de.infsec.tpl.LibCodeUsage" level="debug"/>
<logger name="de.infsec.tpl.eval.LibraryApiAnalysis" level="info"/>
<logger name="de.infsec.tpl.modules.libapi" level="info"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} %-5level %-25logger{0} : %msg%n</pattern>
......
This diff is collapsed.
#
# Copyright (c) 2015-2016 Erik Derr [derr@cs.uni-saarland.de]
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the specific language governing
# permissions and limitations under the License.
#
#!/usr/bin/python
#
# Crawler for libraries hosted at mvn central
# Retrieves jar|aar files along with some meta data
import json
import urllib2
import datetime
import os
import errno
import zipfile
import traceback
from retrying import retry # may require "pip install retrying"
## functions ##
def unix2Date(unixTime):
unixTime = int(str(unixTime)[:-3])
return datetime.datetime.fromtimestamp(unixTime).strftime('%d.%m.%Y')
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
def write_library_description(fileName, libName, category, version, date, comment):
make_sure_path_exists(os.path.dirname(fileName))
# write lib description in xml format
with open(fileName, "w") as desc:
desc.write("<?xml version=\"1.0\"?>\n")
desc.write("<library>\n")
desc.write(" <!-- library name -->\n")
desc.write(" <name>{}</name>\n".format(libName))
desc.write("\n")
desc.write(" <!-- Advertising, Analytics, Android, SocialMedia, Cloud, Utilities -->\n")
desc.write(" <category>{}</category>\n".format(category))
desc.write("\n")
desc.write(" <!-- optional: version string -->\n")
desc.write(" <version>{}</version>\n".format(version))
desc.write("\n")
desc.write(" <!-- optional: date (format: DD/MM/YYYY) -->\n")
desc.write(" <releasedate>{}</releasedate>\n".format(date))
desc.write("\n")
desc.write(" <!-- optional: comment -->\n")
desc.write(" <comment>{}</comment>\n".format(comment))
desc.write("</library>\n")
# TODO: decorator does not work
@retry(urllib2.URLError, tries=3, delay=3, backoff=1)
def urlopen_with_retry(URL):
return urllib2.urlopen(URL)
def downloadFile(targetDir, groupid, artefactid, version, filetype):
make_sure_path_exists(os.path.dirname(targetDir + "/"))
# assemble download URL
baseURL = "http://search.maven.org/remotecontent?filepath="
artefactid_r = artefactid.replace(".","/")
groupid_r = groupid.replace(".","/")
URL = baseURL + groupid_r + "/" + artefactid_r + "/"
# sometimes it just returns the type "bundle", we then access the jar file
if filetype == "bundle":
filetype = "jar"
fileName = artefactid_r + "-" + version + "." + filetype
URL = URL + version + "/" + fileName
# retrieve and save file
targetFile = targetDir + "/" + fileName
try:
libFile = urllib2.urlopen(URL)
with open(targetFile,'wb') as output:
output.write(libFile.read())
# if filetype is aar unzip classes.jar (since WALA currently does not handle aar's directly)
if filetype == "aar":
fh = open(targetFile, 'rb')
z = zipfile.ZipFile(fh)
for f in z.namelist():
if f == "classes.jar":
z.extract(f, targetDir)
fh.close()
return 0
except urllib2.HTTPError, e:
print 'HTTPError = ' + str(e.code)
return 1
except urllib2.URLError, e:
print 'URLError = ' + str(e.reason)
return 1
except Exception, excp:
print 'Download failed (' + str(excp) + ')'
return 1
def updateLibrary(libName, category, comment, groupId, artefactId):
# replace all blanks with dash
libName = libName.replace(" ", "-")
print " # check library " + libName + " [" + category + "] (g:\"" + groupId + "\" AND a:\"" + artefactId + "\")"
baseDirName = rootDir + category + "/" + libName + "/"
dir = os.path.dirname(baseDirName)
make_sure_path_exists(dir);
# Assemble mvn central search URL and retrieve meta data
try:
mvnSearchURL = "http://search.maven.org/solrsearch/select?q=g:%22" + groupId + "%22+AND+a:%22" + artefactId + "%22&rows=100&core=gav"
response = urllib2.urlopen(mvnSearchURL)
data = json.loads(response.read())
except urllib2.URLError, e:
print 'URLError = ' + str(e.reason)
return
except Exception, excp:
print 'Could not retrieve meta data for ' + libName + ' [SKIP] (' + str(excp) + ')'
return
# DEBUG: pretty print json
#print json.dumps(data, indent=4, sort_keys=True)
#print
numberOfVersions = data["response"]["numFound"]
print " - retrieved meta data for " + str(numberOfVersions) + " versions:"
numberOfUpdates = 0
if numberOfVersions > 0:
for version in data["response"]["docs"]:
# skip lib version if already existing
if not os.path.isfile(baseDirName + "/" + version["v"] + "/" + libDescriptorFileName):
numberOfUpdates += 1
date = unix2Date(version["timestamp"])
targetDir = baseDirName + version["v"]
print " - update version: {} type: {} date: {} target-dir: {}".format(version["v"], version["p"], date, targetDir)
result = downloadFile(targetDir, groupId, artefactId, version["v"], version["p"])
if result == 0:
# write lib description
fileName = targetDir + "/" + "library.xml"
write_library_description(fileName, libName, category, version["v"], date, comment)
if numberOfUpdates == 0:
print " -> all versions up-to-date"
## Main functionality ##
inputFile = "libraries.json"
libDescriptorFileName = "library.xml"
rootDir = "lib-sdks" ### change this directory to your lib-sdks dir ###
print "== mvn central crawler =="
# load iterate over lib json
with open(inputFile) as ifile:
data = json.load(ifile)
# update each lib
for lib in data["libraries"]:
updateLibrary(lib["name"], lib["category"], lib["comment"], lib["groupid"], lib["artefactid"])
......@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import de.infsec.tpl.modules.libapi.LibraryApiAnalysis;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
......@@ -36,7 +37,6 @@ import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import de.infsec.tpl.stats.SQLStats;
import de.infsec.tpl.utils.Utils;
import de.infsec.tpl.eval.LibraryApiAnalysis;
import de.infsec.tpl.profile.LibProfile;
......@@ -49,7 +49,7 @@ public class TplCLI {
* - PROFILE: generate library profiles from original lib SDKs and descriptions
* - MATCH: match lib profiles in provided apps
* - DB: build sqlite database from app stat files
* - LIB_API_ANALYSIS: analyzes library api robustness (api additions, removals, changes)
* - LIB_API_ANALYSIS: analyzes library api stability (api additions, removals, changes)
*/
public static enum OpMode {PROFILE, MATCH, DB, LIB_API_ANALYSIS};
......@@ -110,7 +110,7 @@ public class TplCLI {
private static final String USAGE_PROFILE = TOOLNAME + " --opmode profile -a <path-to-android.jar> -x <path-to-lib-desc> <options> $lib.[jar|aar]";
private static final String USAGE_MATCH = TOOLNAME + " --opmode match -a <path-to-android.jar> <options> $path-to-app(-dir)";
private static final String USAGE_DB = TOOLNAME + " --opmode db -p <path-to-profiles> -s <path-to-stats>";
private static final String USAGE_LIB_API_ANALYSIS = TOOLNAME + " --opmode lib_api_analysis -p <path-to-profiles> -j <output-dir>";
private static final String USAGE_LIB_API_ANALYSIS = TOOLNAME + " --opmode lib_api_analysis -a <path-to-android.jar> $path-to-lib-sdks";
private static ArrayList<File> inputFiles;
private static File libraryDescription = null;
......@@ -124,7 +124,6 @@ public class TplCLI {
// initialize logback
initLogging();
List<LibProfile> profiles = null;
// TODO MODE = LIB_UPDATABILITY
......@@ -142,10 +141,6 @@ public class TplCLI {
break;
case LIB_API_ANALYSIS:
profiles = loadLibraryProfiles();
new LibraryApiAnalysis().run(profiles);
System.exit(0);
case PROFILE:
}
......@@ -156,7 +151,10 @@ public class TplCLI {
new LibraryIdentifier(inputFile).identifyLibraries(profiles);
} else if (CliOptions.opmode.equals(OpMode.PROFILE)) {
new LibraryProfiler(inputFile, libraryDescription).extractFingerPrints();
new LibraryProfiler(inputFile, libraryDescription).extractFingerPrints();
} else if (CliOptions.opmode.equals(OpMode.LIB_API_ANALYSIS)) {
new LibraryApiAnalysis(inputFile);
}
} catch (Throwable t) {
logger.error("[FATAL " + (t instanceof Exception? "EXCEPTION" : "ERROR") + "] analysis aborted: " + t.getMessage());
......@@ -229,13 +227,13 @@ public class TplCLI {
}
// path to Android SDK jar
if (checkRequiredUse(cmd, CliArgs.ARG_ANDROID_LIB, OpMode.PROFILE, OpMode.MATCH)) {
if (checkRequiredUse(cmd, CliArgs.ARG_ANDROID_LIB, OpMode.PROFILE, OpMode.MATCH, OpMode.LIB_API_ANALYSIS)) {
CliOptions.pathToAndroidJar = new File(cmd.getOptionValue(CliArgs.ARG_ANDROID_LIB));
}
// profiles dir option, if provided without argument output is written to default dir
if (checkOptionalUse(cmd, CliArgs.ARG_PROFILES_DIR, OpMode.PROFILE, OpMode.MATCH, OpMode.DB, OpMode.LIB_API_ANALYSIS)) {
if (checkOptionalUse(cmd, CliArgs.ARG_PROFILES_DIR, OpMode.PROFILE, OpMode.MATCH, OpMode.DB)) {
File profilesDir = new File(cmd.getOptionValue(CliArgs.ARG_PROFILES_DIR));
if (profilesDir.exists() && !profilesDir.isDirectory())
throw new ParseException("Profiles directory " + profilesDir + " already exists and is not a directory");
......@@ -300,34 +298,49 @@ public class TplCLI {
* - in profile mode pass *one* library (since it is linked to lib description file)
* - in match mode pass one application file or one directory file (including apks)
*/
if (!(CliOptions.opmode.equals(OpMode.DB) || CliOptions.opmode.equals(OpMode.LIB_API_ANALYSIS))) {
if (!(CliOptions.opmode.equals(OpMode.DB))) {
inputFiles = new ArrayList<File>();
String[] fileExts = CliOptions.opmode.equals(OpMode.MATCH)? new String[]{"apk"} : new String[]{"jar", "aar"};
for (String apkFileName: cmd.getArgs()) {
File arg = new File(apkFileName);
if (arg.isDirectory()) {
inputFiles.addAll(Utils.collectFiles(arg, fileExts));
} else if (arg.isFile()) {
if (arg.getName().endsWith("." + fileExts[0]))
inputFiles.add(arg);
else if (fileExts.length > 1 && arg.getName().endsWith("." + fileExts[1]))
inputFiles.add(arg);
else
throw new ParseException("File " + arg.getName() + " is no valid ." + Utils.join(Arrays.asList(fileExts), "/") + " file");
} else {
throw new ParseException("Argument is no valid file or directory!");
if (CliOptions.opmode.equals(OpMode.LIB_API_ANALYSIS)) {
// we require a directory including library packages/descriptions
for (String path: cmd.getArgs()) {
File dir = new File(path);
if (dir.isDirectory())
inputFiles.add(dir);
}
if (inputFiles.isEmpty()) {
throw new ParseException("You have to provide at least one directory that includes a library package and description");
}
} else {
String[] fileExts = CliOptions.opmode.equals(OpMode.MATCH) ? new String[]{"apk"} : new String[]{"jar", "aar"};
for (String apkFileName : cmd.getArgs()) {
File arg = new File(apkFileName);
if (arg.isDirectory()) {
inputFiles.addAll(Utils.collectFiles(arg, fileExts));
} else if (arg.isFile()) {
if (arg.getName().endsWith("." + fileExts[0]))
inputFiles.add(arg);
else if (fileExts.length > 1 && arg.getName().endsWith("." + fileExts[1]))
inputFiles.add(arg);
else
throw new ParseException("File " + arg.getName() + " is no valid ." + Utils.join(Arrays.asList(fileExts), "/") + " file");
} else {
throw new ParseException("Argument is no valid file or directory!");
}
}
if (inputFiles.isEmpty()) {
if (CliOptions.opmode.equals(OpMode.PROFILE))
throw new ParseException("You have to provide one library.jar to be processed");
else
throw new ParseException("You have to provide a path to a single application file or a directory");
} else if (inputFiles.size() > 1 && CliOptions.opmode.equals(OpMode.PROFILE))
throw new ParseException("You have to provide a path to a single library file or a directory incl. a single lib file");
}
if (inputFiles.isEmpty()) {
if (CliOptions.opmode.equals(OpMode.PROFILE))
throw new ParseException("You have to provide one library.jar to be processed");
else
throw new ParseException("You have to provide a path to a single application file or a directory");
} else if (inputFiles.size() > 1 && CliOptions.opmode.equals(OpMode.PROFILE))
throw new ParseException("You have to provide a path to a single library file or a directory incl. a single lib file");
}
} catch (ParseException e) {
......
......@@ -32,6 +32,8 @@ import java.util.TreeSet;
* - # public APIs per version
* - APIs -> set of versions in which they are included
* - set of alternative APIs (if any), if API is no longer in some version
*
* @deprecated
*/
public class LibApiRobustnessStats implements Serializable {
......
......@@ -44,6 +44,8 @@ import org.slf4j.MDC;
* are public APIs removed, modified, and/or added
* For each library a {@link LibApiRobustnessStats} is created that tracks APIs across versions
* The results are written in the configured json directory and logged.
*
* @deprecated
*/
public class LibraryApiAnalysis {
......
package de.infsec.tpl.modules.libapi;
import com.ibm.wala.classLoader.IMethod;
import de.infsec.tpl.utils.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Spec taken from https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
*/
public enum JvmMethodAccessFlags {
NO_FLAG (0x0000, "no-flag"),
PUBLIC (0x0001, "public"),
PRIVATE (0x0002, "private"),
PROTECTED (0x0004, "protected"),
STATIC (0x0008, "static"),
FINAL (0x0010, "final"),
ABSTRACT (0x0400, "abstract"),
PACKAGE_PROTECTED (0x2000, "package-proteced");
private int value;
private String accessFlagName;
//cache the array of all AccessFlags, because .values() allocates a new array for every call
private final static JvmMethodAccessFlags[] allFlags;
private final static List<Integer> validFlagValues;
private static HashMap<String, JvmMethodAccessFlags> accessFlagsByName;
static {
allFlags = JvmMethodAccessFlags.values();
validFlagValues = new ArrayList<Integer>();
for (JvmMethodAccessFlags flag: allFlags)
validFlagValues.add(flag.getValue());
accessFlagsByName = new HashMap<String, JvmMethodAccessFlags>();
for (JvmMethodAccessFlags accessFlag: allFlags) {
accessFlagsByName.put(accessFlag.accessFlagName, accessFlag);
}
}
private JvmMethodAccessFlags(int value, String accessFlagName) {
this.value = value;
this.accessFlagName = accessFlagName;
}
private static String flags2Str(JvmMethodAccessFlags[] accessFlags) {
int size = 0;
for (JvmMethodAccessFlags accessFlag: accessFlags) {
size += accessFlag.toString().length() + 1;
}
StringBuilder sb = new StringBuilder(size);
for (JvmMethodAccessFlags accessFlag: accessFlags) {
sb.append(accessFlag.toString());
sb.append(" ");
}
if (accessFlags.length > 0) {
sb.delete(sb.length() - 1, sb.length());
}
return sb.toString();
}
public static boolean isValidFlag(int code) {
return validFlagValues.contains(code);
}
public static String flags2Str(int code) {
List<String> matchedFlags = new ArrayList<String>();
for (JvmMethodAccessFlags flag: allFlags) {
if ((code & flag.value) != 0x0) {
matchedFlags.add(flag.accessFlagName + "(" + flag.value + ")");
}
}
return Utils.join(matchedFlags, ",");
}
public static String flags2Str(IMethod m) {
return flags2Str(getMethodAccessCode(m));
}
public static JvmMethodAccessFlags getAccessFlag(String accessFlag) {
return accessFlagsByName.get(accessFlag);
}
public int getValue() {
return value;
}
public String toString() {
return accessFlagName;
}
public static int getAccessFlagFilter(JvmMethodAccessFlags... flags) {
int filter = NO_FLAG.getValue();
if (flags != null) {
for (JvmMethodAccessFlags flag: flags) {
if (!JvmMethodAccessFlags.isValidFlag(flag.getValue())) continue;
filter |= flag.getValue();
}
}
return filter;
}
public static int getPublicOnlyFilter() {
return getAccessFlagFilter(JvmMethodAccessFlags.PRIVATE, JvmMethodAccessFlags.PROTECTED, JvmMethodAccessFlags.STATIC,
JvmMethodAccessFlags.FINAL, JvmMethodAccessFlags.ABSTRACT, JvmMethodAccessFlags.PACKAGE_PROTECTED);
}
public static int getMethodAccessCode(IMethod m) {
int res = 0x0;
if (m == null)
return res;
if (m.isPublic()) {
res |= JvmMethodAccessFlags.PUBLIC.getValue();
} else if (m.isProtected()) {
res |= JvmMethodAccessFlags.PROTECTED.getValue();
} else if (m.isPrivate()) {
res |= JvmMethodAccessFlags.PRIVATE.getValue();
} else {
res |= JvmMethodAccessFlags.PACKAGE_PROTECTED.getValue();
}
if (m.isStatic())
res |= JvmMethodAccessFlags.STATIC.getValue();
if (m.isFinal())
res |= JvmMethodAccessFlags.FINAL.getValue();
if (m.isAbstract())
res |= JvmMethodAccessFlags.ABSTRACT.getValue();
return res;
}
}
This diff is collapsed.
package de.infsec.tpl.modules.libapi;
import com.github.zafarkhaja.semver.Version;
import com.ibm.wala.classLoader.IMethod;
import java.util.*;
import java.util.stream.Collectors;
/**
* Container class to store library API changes across versions
*/
public class LibApiStats {
public String libName;
// set of version strings
public Set<String> versions;
// maps documented API signatures to list of library versions including them
public Map<IMethod, Set<String>> api2Versions;
public Map<Version, LibApiComparator.ApiDiff> version2Diff;
private class Export {
public String libName;
public List<LibApiComparator.ApiDiff.Export> apiDiffs = new ArrayList<>();
// maps documented API signatures to list of library versions including them
public Map<String, Set<String>> api2Versions;
public Export(LibApiStats stats) {
this.libName = stats.libName;
this.apiDiffs = stats.version2Diff.values().stream().map(LibApiComparator.ApiDiff::export).collect(Collectors.toList());
this.api2Versions = new HashMap<String, Set<String>>();
for (IMethod m: stats.api2Versions.keySet()) {
this.api2Versions.put(m.getSignature(), stats.api2Versions.get(m));
}
}
}
public Export export() {
return new Export(this);
}
public LibApiStats(String libName) {
this.libName = libName;
this.api2Versions = new HashMap<IMethod, Set<String>>();
this.versions = new TreeSet<String>();
}
/*
* Update functions
*/
public void setDocumentedAPIs(String version, Set<IMethod> docAPIs) {
for (IMethod api: docAPIs) {
if (!api2Versions.containsKey(api))
api2Versions.put(api, new TreeSet<String>());
api2Versions.get(api).add(version);
}
}
// STATISTICS :: SHARED METHODS across lib versions
// TODO
/* public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\nLibrary: " + libName + "\n");
for (String v: versions2docApiCount.keySet()) {
sb.append(" - version: " + v + " doc APIs: " + versions2docApiCount.get(v) + "\n");
}