2020import java .util .Collections ;
2121import java .util .List ;
2222
23+ import de .infsec .tpl .modules .libapi .LibraryApiAnalysis ;
2324import org .apache .commons .cli .BasicParser ;
2425import org .apache .commons .cli .CommandLine ;
2526import org .apache .commons .cli .CommandLineParser ;
3637import ch .qos .logback .core .util .StatusPrinter ;
3738import de .infsec .tpl .stats .SQLStats ;
3839import de .infsec .tpl .utils .Utils ;
39- import de .infsec .tpl .eval .LibraryApiAnalysis ;
4040import de .infsec .tpl .profile .LibProfile ;
4141
4242
@@ -49,7 +49,7 @@ public class TplCLI {
4949 * - PROFILE: generate library profiles from original lib SDKs and descriptions
5050 * - MATCH: match lib profiles in provided apps
5151 * - DB: build sqlite database from app stat files
52- * - LIB_API_ANALYSIS: analyzes library api robustness (api additions, removals, changes)
52+ * - LIB_API_ANALYSIS: analyzes library api stability (api additions, removals, changes)
5353 */
5454 public static enum OpMode {PROFILE , MATCH , DB , LIB_API_ANALYSIS };
5555
@@ -110,7 +110,7 @@ public static class CliOptions {
110110 private static final String USAGE_PROFILE = TOOLNAME + " --opmode profile -a <path-to-android.jar> -x <path-to-lib-desc> <options> $lib.[jar|aar]" ;
111111 private static final String USAGE_MATCH = TOOLNAME + " --opmode match -a <path-to-android.jar> <options> $path-to-app(-dir)" ;
112112 private static final String USAGE_DB = TOOLNAME + " --opmode db -p <path-to-profiles> -s <path-to-stats>" ;
113- private static final String USAGE_LIB_API_ANALYSIS = TOOLNAME + " --opmode lib_api_analysis -p <path-to-profiles> -j <output-dir> " ;
113+ private static final String USAGE_LIB_API_ANALYSIS = TOOLNAME + " --opmode lib_api_analysis -a <path-to-android.jar> $path-to-lib-sdks " ;
114114
115115 private static ArrayList <File > inputFiles ;
116116 private static File libraryDescription = null ;
@@ -124,7 +124,6 @@ public static void main(String[] args) {
124124 // initialize logback
125125 initLogging ();
126126
127-
128127 List <LibProfile > profiles = null ;
129128
130129 // TODO MODE = LIB_UPDATABILITY
@@ -142,10 +141,6 @@ public static void main(String[] args) {
142141 break ;
143142
144143 case LIB_API_ANALYSIS :
145- profiles = loadLibraryProfiles ();
146- new LibraryApiAnalysis ().run (profiles );
147- System .exit (0 );
148-
149144 case PROFILE :
150145 }
151146
@@ -156,7 +151,10 @@ public static void main(String[] args) {
156151 new LibraryIdentifier (inputFile ).identifyLibraries (profiles );
157152
158153 } else if (CliOptions .opmode .equals (OpMode .PROFILE )) {
159- new LibraryProfiler (inputFile , libraryDescription ).extractFingerPrints ();
154+ new LibraryProfiler (inputFile , libraryDescription ).extractFingerPrints ();
155+
156+ } else if (CliOptions .opmode .equals (OpMode .LIB_API_ANALYSIS )) {
157+ new LibraryApiAnalysis (inputFile );
160158 }
161159 } catch (Throwable t ) {
162160 logger .error ("[FATAL " + (t instanceof Exception ? "EXCEPTION" : "ERROR" ) + "] analysis aborted: " + t .getMessage ());
@@ -229,13 +227,13 @@ else if (cmd.hasOption(CliArgs.ARG_LOG_DIR)) {
229227 }
230228
231229 // path to Android SDK jar
232- if (checkRequiredUse (cmd , CliArgs .ARG_ANDROID_LIB , OpMode .PROFILE , OpMode .MATCH )) {
230+ if (checkRequiredUse (cmd , CliArgs .ARG_ANDROID_LIB , OpMode .PROFILE , OpMode .MATCH , OpMode . LIB_API_ANALYSIS )) {
233231 CliOptions .pathToAndroidJar = new File (cmd .getOptionValue (CliArgs .ARG_ANDROID_LIB ));
234232 }
235233
236234
237235 // profiles dir option, if provided without argument output is written to default dir
238- if (checkOptionalUse (cmd , CliArgs .ARG_PROFILES_DIR , OpMode .PROFILE , OpMode .MATCH , OpMode .DB , OpMode . LIB_API_ANALYSIS )) {
236+ if (checkOptionalUse (cmd , CliArgs .ARG_PROFILES_DIR , OpMode .PROFILE , OpMode .MATCH , OpMode .DB )) {
239237 File profilesDir = new File (cmd .getOptionValue (CliArgs .ARG_PROFILES_DIR ));
240238 if (profilesDir .exists () && !profilesDir .isDirectory ())
241239 throw new ParseException ("Profiles directory " + profilesDir + " already exists and is not a directory" );
@@ -300,34 +298,49 @@ else if (cmd.hasOption(CliArgs.ARG_LOG_DIR)) {
300298 * - in profile mode pass *one* library (since it is linked to lib description file)
301299 * - in match mode pass one application file or one directory file (including apks)
302300 */
303- if (!(CliOptions .opmode .equals (OpMode .DB ) || CliOptions . opmode . equals ( OpMode . LIB_API_ANALYSIS ) )) {
301+ if (!(CliOptions .opmode .equals (OpMode .DB ))) {
304302 inputFiles = new ArrayList <File >();
305- String [] fileExts = CliOptions .opmode .equals (OpMode .MATCH )? new String []{"apk" } : new String []{"jar" , "aar" };
306-
307- for (String apkFileName : cmd .getArgs ()) {
308- File arg = new File (apkFileName );
309-
310- if (arg .isDirectory ()) {
311- inputFiles .addAll (Utils .collectFiles (arg , fileExts ));
312- } else if (arg .isFile ()) {
313- if (arg .getName ().endsWith ("." + fileExts [0 ]))
314- inputFiles .add (arg );
315- else if (fileExts .length > 1 && arg .getName ().endsWith ("." + fileExts [1 ]))
316- inputFiles .add (arg );
317- else
318- throw new ParseException ("File " + arg .getName () + " is no valid ." + Utils .join (Arrays .asList (fileExts ), "/" ) + " file" );
319- } else {
320- throw new ParseException ("Argument is no valid file or directory!" );
303+
304+ if (CliOptions .opmode .equals (OpMode .LIB_API_ANALYSIS )) {
305+ // we require a directory including library packages/descriptions
306+ for (String path : cmd .getArgs ()) {
307+ File dir = new File (path );
308+
309+ if (dir .isDirectory ())
310+ inputFiles .add (dir );
311+ }
312+
313+ if (inputFiles .isEmpty ()) {
314+ throw new ParseException ("You have to provide at least one directory that includes a library package and description" );
321315 }
316+ } else {
317+ String [] fileExts = CliOptions .opmode .equals (OpMode .MATCH ) ? new String []{"apk" } : new String []{"jar" , "aar" };
318+
319+ for (String apkFileName : cmd .getArgs ()) {
320+ File arg = new File (apkFileName );
321+
322+ if (arg .isDirectory ()) {
323+ inputFiles .addAll (Utils .collectFiles (arg , fileExts ));
324+ } else if (arg .isFile ()) {
325+ if (arg .getName ().endsWith ("." + fileExts [0 ]))
326+ inputFiles .add (arg );
327+ else if (fileExts .length > 1 && arg .getName ().endsWith ("." + fileExts [1 ]))
328+ inputFiles .add (arg );
329+ else
330+ throw new ParseException ("File " + arg .getName () + " is no valid ." + Utils .join (Arrays .asList (fileExts ), "/" ) + " file" );
331+ } else {
332+ throw new ParseException ("Argument is no valid file or directory!" );
333+ }
334+ }
335+
336+ if (inputFiles .isEmpty ()) {
337+ if (CliOptions .opmode .equals (OpMode .PROFILE ))
338+ throw new ParseException ("You have to provide one library.jar to be processed" );
339+ else
340+ throw new ParseException ("You have to provide a path to a single application file or a directory" );
341+ } else if (inputFiles .size () > 1 && CliOptions .opmode .equals (OpMode .PROFILE ))
342+ throw new ParseException ("You have to provide a path to a single library file or a directory incl. a single lib file" );
322343 }
323-
324- if (inputFiles .isEmpty ()) {
325- if (CliOptions .opmode .equals (OpMode .PROFILE ))
326- throw new ParseException ("You have to provide one library.jar to be processed" );
327- else
328- throw new ParseException ("You have to provide a path to a single application file or a directory" );
329- } else if (inputFiles .size () > 1 && CliOptions .opmode .equals (OpMode .PROFILE ))
330- throw new ParseException ("You have to provide a path to a single library file or a directory incl. a single lib file" );
331344 }
332345
333346 } catch (ParseException e ) {
0 commit comments