package de.infsec.tpl.config; import de.infsec.tpl.TplCLI; import de.infsec.tpl.utils.Utils; import net.consensys.cava.toml.Toml; import net.consensys.cava.toml.TomlParseResult; import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.stream.Collectors; public class LibScoutConfig { private static final Logger logger = LoggerFactory.getLogger(de.infsec.tpl.config.LibScoutConfig.class); public static final String TOOLNAME = "LibScout"; public static final String TOOLVERSION = "2.3.2"; // modes of operations public enum OpMode { // generate library profiles from original lib SDKs and descriptions PROFILE("profile", "-x path_to_lib_desc [options] path_to_lib(jar|aar)"), // match lib profiles in provided apps MATCH("match", "[options] path_to_app(dir)"), // analyzes library api stability (api additions, removals, changes) LIB_API_ANALYSIS("lib_api_analysis", "path_to_lib_sdks"), // infer library usage in apps and check to which extent detected libs can be updated UPDATABILITY( "updatability", "[options] -l path_to_lib_api_compat path_to_app(dir)"); public String name; public String usageMsg; OpMode(String name, String usageMsg) { this.name = name; this.usageMsg = usageMsg; } public static String getUsageMessage(OpMode op) { return op == null? getToolUsageMsg() : TOOLNAME + " --opmode " + op.name + " " + op.usageMsg; } public static String getToolUsageMsg() { return TOOLNAME + " --opmode <" + getOpModeString() + "> [options]"; } public static String getOpModeString() { return Utils.join(Arrays.stream(OpMode.values()).map(op -> op.name.toLowerCase()).collect(Collectors.toList()), "|"); } } public static OpMode opmode = null; public static boolean opMatch() { return OpMode.MATCH.equals(opmode); } public static boolean opProfile() { return OpMode.PROFILE.equals(opmode); } public static boolean opLibApiAnalysis() { return OpMode.LIB_API_ANALYSIS.equals(opmode); } public static boolean opUpdatability() { return OpMode.UPDATABILITY.equals(opmode); } // config files public static String libScoutConfigFileName = "./config/LibScout.toml"; public static String log4jConfigFileName = "./config/logback.xml"; public static File pathToAndroidJar; public static boolean noPartialMatching = false; public static boolean runLibUsageAnalysis = false; public static boolean genVerboseProfiles = false; // generate lib profiles with TRACE + PubOnly public static boolean libDependencyAnalysis = false; public enum LogType { NONE, CONSOLE, FILE } public static LogType logType = LogType.CONSOLE; public static File logDir = new File("./logs"); public static boolean generateStats = false; public static File statsDir = new File("./stats"); public static boolean generateJSON = false; public static File jsonDir = new File("./json"); public static File profilesDir = new File("./profiles"); public static File libApiCompatDir = null; // package tree public static class PckgTree { public static boolean useAsciiRendering = false; } // reporting (logs, json) public static class Reporting { // upon detection, print/hide comments from library description public static boolean showComments = false; } public static void whoAmI() { logger.info("This is " + TOOLNAME + " " + TOOLVERSION); } public static boolean loadConfig() throws ParseException { File libScoutConfigFile; try { libScoutConfigFile = checkIfValidFile(libScoutConfigFileName); } catch (ParseException e) { throw new ParseException("Could not find the config file LibScout.toml . Please provide it via the --" + TplCLI.CliArgs.ARGL_CONFIG + " switch"); } try { Path confPath = Paths.get(libScoutConfigFile.toURI()); TomlParseResult conf = Toml.parse(confPath); if (conf.hasErrors()) { logger.warn("Error while parsing config file:"); conf.errors().forEach(e -> logger.info(Utils.indent() + "line: " + e.position().line() + ": " + e.toString())); return false; } else { for (String k: conf.dottedKeySet()) { parseConfig(k, conf.get(k)); } } } catch (IOException e) { logger.warn("Error while parsing config file: " + Utils.stacktrace2Str(e)); return false; } return true; } private static void parseConfig(String key, Object value) throws ParseException { try { //logger.debug("Parse config key : " + key + " value: " + value); if ("logging.log4j_config_file".equals(key)) { File f = checkIfValidFile((String) value); log4jConfigFileName = f.getAbsolutePath(); } else if ("sdk.android_sdk_jar".equals(key)) { if (pathToAndroidJar == null) // CLI takes precedence pathToAndroidJar = checkIfValidFile((String) value); } else if ("packageTree.ascii_rendering".equals(key)) { PckgTree.useAsciiRendering = (Boolean) value; } else if ("reporting.show_comments".equals(key)) { Reporting.showComments = (Boolean) value; } else logger.warn("Found unknown config key: " + key); } catch (ParseException e) { throw new ParseException("Could not parse config option " + key + " : " + e.getMessage()); } } public static File checkIfValidFile(String fileName) throws ParseException { File f = new File(fileName); if (f.exists() && f.isFile()) return f; else throw new ParseException("No valid file: " + fileName); } }