1 /* 2 * Created on May 18, 2004 3 * 4 * Paros and its related class files. 5 * 6 * Paros is an HTTP/HTTPS proxy for assessing web application security. 7 * Copyright (C) 2003-2005 Chinotec Technologies Company 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the Clarified Artistic License 11 * as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * Clarified Artistic License for more details. 17 * 18 * You should have received a copy of the Clarified Artistic License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 */ 22 // ZAP: 2011/08/03 Revamped upgrade for 1.3.2 23 // ZAP: 2011/10/05 Write backup file to user dir 24 // ZAP: 2011/11/15 Changed to use ZapXmlConfiguration, to enforce the same 25 // character encoding when reading/writing configurations. Changed to use the 26 // correct file when an error occurs during the load of the configuration file. 27 // Removed the calls XMLConfiguration.load() as they are not needed, the 28 // XMLConfiguration constructor used already does that. 29 // ZAP: 2011/11/20 Support for extension factory 30 // ZAP: 2012/03/03 Added ZAP homepage 31 // ZAP: 2012/03/15 Removed a @SuppressWarnings annotation from the method 32 // copyAllProperties. 33 // ZAP: 2012/03/17 Issue 282 ZAP and PAROS team constants 34 // ZAP: 2012/05/02 Added method createInstance and changed the method 35 // getInstance to use it. 36 // ZAP: 2012/05/03 Changed the Patterns used to detect the O.S. to be final. 37 // ZAP: 2012/06/15 Issue 312 Increase the maximum number of scanning threads allowed 38 // ZAP: 2012/07/13 Added variable for maximum number of threads used in scan (MAX_THREADS_PER_SCAN) 39 // ZAP: 2012/10/15 Issue 397: Support weekly builds 40 // ZAP: 2012/10/17 Issue 393: Added more online links from menu 41 // ZAP: 2012/11/15 Issue 416: Normalise how multiple related options are managed 42 // throughout ZAP and enhance the usability of some options. 43 // ZAP: 2012/11/20 Issue 419: Restructure jar loading code 44 // ZAP: 2012/12/08 Issue 428: Changed to use I18N for messages, to support the marketplace 45 // ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments 46 // ZAP: 2013/04/14 Issue 610: Replace the use of the String class for available/default "Forced 47 // Browse" files 48 // ZAP: 2013/04/15 Issue 632: Manual Request Editor dialogue (HTTP) configurations not saved 49 // correctly 50 // ZAP: 2013/12/03 Issue 933: Automatically determine install dir 51 // ZAP: 2013/12/13 Issue 919: Support for multiple language vulnerability files. 52 // ZAP: 2014/04/11 Issue 1148: ZAP 2.3.0 does not launch after upgrading in some situations 53 // ZAP: 2014/07/15 Issue 1265: Context import and export 54 // ZAP: 2014/08/14 Issue 1300: Add-ons show incorrect language when English is selected on non 55 // English locale 56 // ZAP: 2014/11/11 Issue 1406: Move online menu items to an add-on 57 // ZAP: 2015/01/04 Issue 1388: Not all translated files are updated when "zaplang" package is 58 // imported 59 // ZAP: 2014/01/04 Issue 1394: Import vulnerabilities.xml files when updating the translated 60 // resources 61 // ZAP: 2014/01/04 Issue 1458: Change home/installation dir paths to be always absolute 62 // ZAP: 2015/03/10 Issue 653: Handle updates on Kali better 63 // ZAP: 2015/03/30 Issue 1582: Enablers for low memory option 64 // ZAP: 2015/04/12 Remove "installation" fuzzers dir, no longer in use 65 // ZAP: 2015/08/01 Remove code duplication in catch of exceptions, use installation directory in 66 // default config file 67 // ZAP: 2015/11/11 Issue 2045: Dont copy old configs if -dir option used 68 // ZAP: 2015/11/26 Issue 2084: Warn users if they are probably using out of date versions 69 // ZAP: 2016/02/17 Convert extensions' options to not use extensions' names as XML element names 70 // ZAP: 2016/05/12 Use dev/weekly dir for plugin downloads when copying the existing 'release' 71 // config file 72 // ZAP: 2016/06/07 Remove commented constants and statement that had no (actual) effect, add doc to 73 // a constant and init other 74 // ZAP: 2016/06/07 Use filter directory in ZAP's home directory 75 // ZAP: 2016/06/13 Migrate config option "proxy.modifyAcceptEncoding" 76 // ZAP: 2016/07/07 Convert passive scanners options to new structure 77 // ZAP: 2016/09/22 JavaDoc tweaks 78 // ZAP: 2016/11/17 Issue 2701 Support Factory Reset 79 // ZAP: 2017/05/04 Issue 3440: Log Exception when overwriting a config file 80 // ZAP: 2017/12/26 Remove class methods no longer used. 81 // ZAP: 2018/01/03 No longer create filter dir and deprecate FOLDER_FILTER constant. 82 // Exit immediately if not able to create the home dir. 83 // ZAP: 2018/01/04 Clear SNI Terminator options when updating from older ZAP versions. 84 // ZAP: 2018/01/05 Prevent use of install dir as home dir. 85 // ZAP: 2018/02/14 Remove unnecessary boxing / unboxing 86 // ZAP: 2018/03/16 Use equalsIgnoreCase (Issue 4327). 87 // ZAP: 2018/04/16 Keep backup of malformed config file. 88 // ZAP: 2018/06/13 Correct install dir detection from JAR. 89 // ZAP: 2018/06/29 Allow to check if in dev mode. 90 // ZAP: 2018/07/19 Fallback to bundled config.xml and log4j.properties. 91 // ZAP: 2019/03/14 Move and correct update of old options. 92 // ZAP: 2019/04/01 Refactored to reduce code-duplication. 93 // ZAP: 2019/05/10 Apply installer config options on update. 94 // Fix exceptions during config update. 95 // ZAP: 2019/05/14 Added silent option 96 // ZAP: 2019/05/17 Update cert option to boolean. 97 // ZAP: 2019/05/29 Update Jericho log configuration. 98 // ZAP: 2019/06/01 Normalise line endings. 99 // ZAP: 2019/06/05 Normalise format/style. 100 // ZAP: 2019/06/07 Update current version. 101 // ZAP: 2019/09/16 Deprecate ZAP_HOMEPAGE and ZAP_EXTENSIONS_PAGE. 102 // ZAP: 2019/11/07 Removed constants related to accepting the license. 103 // ZAP: 2020/01/02 Updated config version and default user agent 104 // ZAP: 2020/01/06 Set latest version to default config. 105 // ZAP: 2020/01/10 Correct the MailTo autoTagScanner regex pattern when upgrading from 2.8 or 106 // earlier. 107 // ZAP: 2020/04/22 Check ControlOverrides when determining the locale. 108 // ZAP: 2020/09/17 Correct the Syntax Highlighting markoccurrences config key name when upgrading 109 // from 2.9 or earlier. 110 // ZAP: 2020/10/07 Changes for Log4j 2 migration. 111 // ZAP: 2020/11/02 Do not backup old Log4j config if already present. 112 // ZAP: 2020/11/26 Use Log4j 2 classes for logging. 113 // ZAP: 2021/09/15 Added support for detecting containers 114 // ZAP: 2021/09/21 Added support for detecting snapcraft 115 // ZAP: 2021/10/01 Added support for detecting WebSwing 116 // ZAP: 2021/10/06 Update user agent when upgrading from 2.10 117 package org.parosproxy.paros; 118 119 import java.io.File; 120 import java.io.IOException; 121 import java.io.InputStream; 122 import java.net.MalformedURLException; 123 import java.net.URISyntaxException; 124 import java.net.URL; 125 import java.nio.charset.StandardCharsets; 126 import java.nio.file.FileSystems; 127 import java.nio.file.Files; 128 import java.nio.file.Path; 129 import java.nio.file.Paths; 130 import java.nio.file.StandardCopyOption; 131 import java.security.InvalidParameterException; 132 import java.text.ParseException; 133 import java.text.SimpleDateFormat; 134 import java.util.ArrayList; 135 import java.util.Date; 136 import java.util.HashSet; 137 import java.util.Iterator; 138 import java.util.List; 139 import java.util.Locale; 140 import java.util.NoSuchElementException; 141 import java.util.Properties; 142 import java.util.Set; 143 import java.util.jar.Attributes; 144 import java.util.jar.Manifest; 145 import java.util.regex.Matcher; 146 import java.util.regex.Pattern; 147 import org.apache.commons.configuration.ConfigurationException; 148 import org.apache.commons.configuration.ConversionException; 149 import org.apache.commons.configuration.HierarchicalConfiguration; 150 import org.apache.commons.configuration.XMLConfiguration; 151 import org.apache.logging.log4j.LogManager; 152 import org.apache.logging.log4j.Logger; 153 import org.apache.logging.log4j.core.config.Configurator; 154 import org.parosproxy.paros.extension.option.OptionsParamView; 155 import org.parosproxy.paros.model.FileCopier; 156 import org.parosproxy.paros.model.Model; 157 import org.parosproxy.paros.network.ConnectionParam; 158 import org.zaproxy.zap.ZAP; 159 import org.zaproxy.zap.control.AddOnLoader; 160 import org.zaproxy.zap.control.ControlOverrides; 161 import org.zaproxy.zap.extension.autoupdate.OptionsParamCheckForUpdates; 162 import org.zaproxy.zap.utils.I18N; 163 import org.zaproxy.zap.utils.ZapXmlConfiguration; 164 165 public final class Constant { 166 // ZAP: rebrand 167 public static final String PROGRAM_NAME = "OWASP ZAP"; 168 public static final String PROGRAM_NAME_SHORT = "ZAP"; 169 /** @deprecated (2.9.0) Do not use, it will be removed. */ 170 @Deprecated public static final String ZAP_HOMEPAGE = "http://www.owasp.org/index.php/ZAP"; 171 /** @deprecated (2.9.0) Do not use, it will be removed. */ 172 @Deprecated 173 public static final String ZAP_EXTENSIONS_PAGE = "https://github.com/zaproxy/zap-extensions"; 174 175 public static final String ZAP_TEAM = "ZAP Dev Team"; 176 public static final String PAROS_TEAM = "Chinotec Technologies"; 177 178 // ************************************************************ 179 // the config.xml MUST be set to be the same as the version_tag 180 // otherwise the config.xml will be overwritten everytime. 181 // ************************************************************ 182 private static final String DEV_VERSION = "Dev Build"; 183 public static final String ALPHA_VERSION = "alpha"; 184 public static final String BETA_VERSION = "beta"; 185 186 private static final String VERSION_ELEMENT = "version"; 187 188 // Accessible for tests 189 static final long VERSION_TAG = 20011001; 190 191 // Old version numbers - for upgrade 192 private static final long V_2_10_0_TAG = 20010000; 193 private static final long V_2_9_0_TAG = 2009000; 194 private static final long V_2_8_0_TAG = 2008000; 195 private static final long V_2_7_0_TAG = 2007000; 196 private static final long V_2_5_0_TAG = 2005000; 197 private static final long V_2_4_3_TAG = 2004003; 198 private static final long V_2_3_1_TAG = 2003001; 199 private static final long V_2_2_2_TAG = 2002002; 200 private static final long V_2_2_0_TAG = 2002000; 201 private static final long V_2_1_0_TAG = 2001000; 202 private static final long V_2_0_0_TAG = 2000000; 203 private static final long V_1_4_1_TAG = 1004001; 204 private static final long V_1_3_1_TAG = 1003001; 205 private static final long V_1_3_0_TAG = 1003000; 206 private static final long V_1_2_1_TAG = 1002001; 207 private static final long V_1_2_0_TAG = 1002000; 208 private static final long V_1_1_0_TAG = 1001000; 209 private static final long V_1_0_0_TAG = 1000000; 210 private static final long V_PAROS_TAG = 30020013; 211 212 // ************************************************************ 213 // note the above 214 // ************************************************************ 215 216 // These are no longer final - version is now loaded from the manifest file 217 public static String PROGRAM_VERSION = DEV_VERSION; 218 public static String PROGRAM_TITLE = PROGRAM_NAME + " " + PROGRAM_VERSION; 219 220 public static final String SYSTEM_PAROS_USER_LOG = "zap.user.log"; 221 222 public static final String FILE_SEPARATOR = System.getProperty("file.separator"); 223 224 /** 225 * @deprecated (2.4.2) The path does not take into account the installation directory, use 226 * {@link #getPathDefaultConfigFile()} instead. 227 */ 228 @Deprecated public static final String FILE_CONFIG_DEFAULT = "xml/config.xml"; 229 230 public static final String FILE_CONFIG_NAME = "config.xml"; 231 public static final String FOLDER_PLUGIN = "plugin"; 232 /** 233 * The name of the directory for filter related files (the path should be built using {@link 234 * #getZapHome()} as the parent directory). 235 * 236 * @deprecated (2.8.0) Should not be used, the filter functionality is deprecated (replaced by 237 * scripts and Replacer add-on). 238 * @since 1.0.0 239 */ 240 @Deprecated public static final String FOLDER_FILTER = "filter"; 241 242 /** 243 * The name of the directory where the (file) sessions are saved by default. 244 * 245 * @since 1.0.0 246 */ 247 public static final String FOLDER_SESSION_DEFAULT = "session"; 248 249 public static final String DBNAME_TEMPLATE = 250 "db" + System.getProperty("file.separator") + "zapdb"; 251 252 /** 253 * Prefix (file name) of Messages.properties files. 254 * 255 * @see #MESSAGES_EXTENSION 256 */ 257 public static final String MESSAGES_PREFIX = "Messages"; 258 259 /** 260 * Extension (with dot) of Messages.properties files. 261 * 262 * @see #MESSAGES_PREFIX 263 * @since 2.4.0 264 */ 265 public static final String MESSAGES_EXTENSION = ".properties"; 266 267 public static final String DBNAME_UNTITLED_DEFAULT = 268 FOLDER_SESSION_DEFAULT + System.getProperty("file.separator") + "untitled"; 269 270 public String FILE_CONFIG = FILE_CONFIG_NAME; 271 public String FOLDER_SESSION = FOLDER_SESSION_DEFAULT; 272 public String DBNAME_UNTITLED = 273 FOLDER_SESSION + System.getProperty("file.separator") + "untitled"; 274 275 public static final String FILE_PROGRAM_SPLASH = "resource/zap128x128.png"; 276 277 // Accelerator keys - Default: Windows 278 public static String ACCELERATOR_UNDO = "control Z"; 279 public static String ACCELERATOR_REDO = "control Y"; 280 public static String ACCELERATOR_TRIGGER_KEY = "Control"; 281 282 private static Constant instance = null; 283 284 public static final int MAX_HOST_CONNECTION = 15; 285 public static final int MAX_THREADS_PER_SCAN = 50; 286 // ZAP: Dont announce ourselves 287 // public static final String USER_AGENT = PROGRAM_NAME + "/" + PROGRAM_VERSION; 288 public static final String USER_AGENT = ""; 289 290 private static String staticEyeCatcher = "0W45pz4p"; 291 292 private static final String USER_CONTEXTS_DIR = "contexts"; 293 private static final String USER_POLICIES_DIR = "policies"; 294 295 private static final String ZAP_CONTAINER_FILE = "/zap/container"; 296 private static final String FLATPAK_FILE = "/.flatpak-info"; 297 public static final String FLATPAK_NAME = "flatpak"; 298 private static final String SNAP_FILE = "meta/snap.yaml"; 299 public static final String SNAP_NAME = "snapcraft"; 300 private static final String WEBSWING_ENVVAR = "WEBSWING_VERSION"; 301 public static final String WEBSWING_NAME = "webswing"; 302 303 // 304 // Home dir for ZAP, i.e. where the config file is. Can be set on cmdline, otherwise will be set 305 // to default loc 306 private static String zapHome = null; 307 // Default home dir for 'full' releases - used for copying full conf file when dev/daily release 308 // run for the first time 309 // and also for the JVM options config file 310 private static String zapStd = null; 311 // Install dir for ZAP, but default will be cwd 312 private static String zapInstall = null; 313 314 private static Boolean onKali = null; 315 private static Boolean inContainer = null; 316 private static String containerName; 317 private static Boolean lowMemoryOption = null; 318 319 // ZAP: Added i18n 320 public static I18N messages = null; 321 322 /** 323 * The system's locale (as determined by the JVM at startup, {@code Locale#getDefault()}). 324 * 325 * <p>The locale is kept here because the default locale is later overridden with the user's 326 * chosen locale/language. 327 * 328 * @see Locale#getDefault() 329 */ 330 private static final Locale SYSTEMS_LOCALE = Locale.getDefault(); 331 332 /** The path to bundled (in zap.jar) config.xml file. */ 333 private static final String PATH_BUNDLED_CONFIG_XML = 334 "/org/zaproxy/zap/resources/" + FILE_CONFIG_NAME; 335 336 /** 337 * Name of directory that contains the (source and translated) resource files. 338 * 339 * @see #MESSAGES_PREFIX 340 * @see #VULNERABILITIES_PREFIX 341 */ 342 public static final String LANG_DIR = "lang"; 343 344 /** 345 * Prefix (file name) of vulnerabilities.xml files. 346 * 347 * @see #VULNERABILITIES_EXTENSION 348 * @since 2.4.0 349 */ 350 public static final String VULNERABILITIES_PREFIX = "vulnerabilities"; 351 352 /** 353 * @deprecated (2.4.0) Use {@link #VULNERABILITIES_PREFIX} instead. It will be removed in a 354 * following release. 355 */ 356 @Deprecated public static String VULNS_BASE = VULNERABILITIES_PREFIX; 357 358 /** 359 * Extension (with dot) of vulnerabilities.xml files. 360 * 361 * @see #VULNERABILITIES_PREFIX 362 * @since 2.4.0 363 */ 364 public static final String VULNERABILITIES_EXTENSION = ".xml"; 365 366 /** 367 * Flag that indicates whether or not the "dev mode" is enabled. 368 * 369 * @see #isDevMode() 370 */ 371 private static boolean devMode; 372 373 private static boolean silent; 374 375 // ZAP: Added dirbuster dir 376 public String DIRBUSTER_DIR = "dirbuster"; 377 public String DIRBUSTER_CUSTOM_DIR = DIRBUSTER_DIR; 378 379 public String FUZZER_DIR = "fuzzers"; 380 381 public static String FOLDER_LOCAL_PLUGIN = FOLDER_PLUGIN; 382 383 public static final URL OK_FLAG_IMAGE_URL = 384 Constant.class.getResource("/resource/icon/10/072.png"); // Green 385 public static final URL INFO_FLAG_IMAGE_URL = 386 Constant.class.getResource("/resource/icon/10/073.png"); // Blue 387 public static final URL LOW_FLAG_IMAGE_URL = 388 Constant.class.getResource("/resource/icon/10/074.png"); // Yellow 389 public static final URL MED_FLAG_IMAGE_URL = 390 Constant.class.getResource("/resource/icon/10/076.png"); // Orange 391 public static final URL HIGH_FLAG_IMAGE_URL = 392 Constant.class.getResource("/resource/icon/10/071.png"); // Red 393 public static final URL BLANK_IMAGE_URL = 394 Constant.class.getResource("/resource/icon/10/blank.png"); 395 public static final URL SPIDER_IMAGE_URL = 396 Constant.class.getResource("/resource/icon/10/spider.png"); 397 398 private static Logger LOG = LogManager.getLogger(Constant.class); 399 getEyeCatcher()400 public static String getEyeCatcher() { 401 return staticEyeCatcher; 402 } 403 setEyeCatcher(String eyeCatcher)404 public static void setEyeCatcher(String eyeCatcher) { 405 staticEyeCatcher = eyeCatcher; 406 } 407 Constant()408 public Constant() { 409 this(null); 410 } 411 Constant(ControlOverrides overrides)412 private Constant(ControlOverrides overrides) { 413 initializeFilesAndDirectories(overrides); 414 setAcceleratorKeys(); 415 } 416 getDefaultHomeDirectory(boolean incDevOption)417 public static String getDefaultHomeDirectory(boolean incDevOption) { 418 if (zapStd == null) { 419 zapStd = System.getProperty("user.home"); 420 if (zapStd == null) { 421 zapStd = "."; 422 } 423 424 if (isLinux()) { 425 // Linux: Hidden Zap directory in the user's home directory 426 zapStd += FILE_SEPARATOR + "." + PROGRAM_NAME_SHORT; 427 } else if (isMacOsX()) { 428 // Mac Os X: Support for writing the configuration into the users Library 429 zapStd += 430 FILE_SEPARATOR 431 + "Library" 432 + FILE_SEPARATOR 433 + "Application Support" 434 + FILE_SEPARATOR 435 + PROGRAM_NAME_SHORT; 436 } else { 437 // Windows: Zap directory in the user's home directory 438 zapStd += FILE_SEPARATOR + PROGRAM_NAME; 439 } 440 } 441 442 if (incDevOption) { 443 if (isDevMode() || isDailyBuild()) { 444 // Default to a different home dir to prevent messing up full releases 445 return zapStd + "_D"; 446 } 447 } 448 return zapStd; 449 } 450 copyDefaultConfigs(File f, boolean forceReset)451 public void copyDefaultConfigs(File f, boolean forceReset) 452 throws IOException, ConfigurationException { 453 FileCopier copier = new FileCopier(); 454 File oldf; 455 if (isDevMode() || isDailyBuild()) { 456 // try standard location 457 oldf = new File(getDefaultHomeDirectory(false) + FILE_SEPARATOR + FILE_CONFIG_NAME); 458 } else { 459 // try old location 460 oldf = new File(zapHome + FILE_SEPARATOR + "zap" + FILE_SEPARATOR + FILE_CONFIG_NAME); 461 } 462 463 if (!forceReset 464 && oldf.exists() 465 && Paths.get(zapHome).equals(Paths.get(getDefaultHomeDirectory(true)))) { 466 // Dont copy old configs if forcedReset or they've specified a non std directory 467 LOG.info("Copying defaults from " + oldf.getAbsolutePath() + " to " + FILE_CONFIG); 468 copier.copy(oldf, f); 469 470 if (isDevMode() || isDailyBuild()) { 471 ZapXmlConfiguration newConfig = new ZapXmlConfiguration(f); 472 newConfig.setProperty( 473 OptionsParamCheckForUpdates.DOWNLOAD_DIR, Constant.FOLDER_LOCAL_PLUGIN); 474 newConfig.save(); 475 } 476 } else { 477 LOG.info("Copying default configuration to " + FILE_CONFIG); 478 copyDefaultConfigFile(); 479 } 480 } 481 copyDefaultConfigFile()482 private void copyDefaultConfigFile() throws IOException { 483 Path configFile = Paths.get(FILE_CONFIG); 484 copyFileToHome(configFile, "xml/" + FILE_CONFIG_NAME, PATH_BUNDLED_CONFIG_XML); 485 try { 486 setLatestVersion(new ZapXmlConfiguration(configFile.toFile())); 487 } catch (ConfigurationException e) { 488 throw new IOException("Failed to set the latest version:", e); 489 } 490 } 491 492 /** 493 * Sets the latest version ({@link #VERSION_TAG}) to the given configuration and then saves it. 494 * 495 * @param config the configuration to change 496 * @throws ConfigurationException if an error occurred while saving the configuration. 497 */ setLatestVersion(XMLConfiguration config)498 private static void setLatestVersion(XMLConfiguration config) throws ConfigurationException { 499 config.setProperty(VERSION_ELEMENT, VERSION_TAG); 500 config.save(); 501 } 502 copyFileToHome( Path targetFile, String sourceFilePath, String fallbackResource)503 private static void copyFileToHome( 504 Path targetFile, String sourceFilePath, String fallbackResource) throws IOException { 505 Path defaultConfig = Paths.get(getZapInstall(), sourceFilePath); 506 if (Files.exists(defaultConfig)) { 507 Files.copy(defaultConfig, targetFile, StandardCopyOption.REPLACE_EXISTING); 508 } else { 509 try (InputStream is = Constant.class.getResourceAsStream(fallbackResource)) { 510 if (is == null) { 511 throw new IOException("Bundled resource not found: " + fallbackResource); 512 } 513 Files.copy(is, targetFile, StandardCopyOption.REPLACE_EXISTING); 514 } 515 } 516 } 517 getUrlDefaultConfigFile()518 private static URL getUrlDefaultConfigFile() { 519 Path path = getPathDefaultConfigFile(); 520 if (Files.exists(path)) { 521 try { 522 return path.toUri().toURL(); 523 } catch (MalformedURLException e) { 524 LOG.debug("Failed to convert file system path:", e); 525 } 526 } 527 return Constant.class.getResource(PATH_BUNDLED_CONFIG_XML); 528 } 529 initializeFilesAndDirectories()530 public void initializeFilesAndDirectories() { 531 initializeFilesAndDirectories(null); 532 } 533 initializeFilesAndDirectories(ControlOverrides overrides)534 private void initializeFilesAndDirectories(ControlOverrides overrides) { 535 536 FileCopier copier = new FileCopier(); 537 File f = null; 538 539 // Set up the version from the manifest 540 PROGRAM_VERSION = getVersionFromManifest(); 541 PROGRAM_TITLE = PROGRAM_NAME + " " + PROGRAM_VERSION; 542 543 if (zapHome == null) { 544 zapHome = getDefaultHomeDirectory(true); 545 } 546 547 zapHome = getAbsolutePath(zapHome); 548 f = new File(zapHome); 549 550 FILE_CONFIG = zapHome + FILE_CONFIG; 551 FOLDER_SESSION = zapHome + FOLDER_SESSION; 552 DBNAME_UNTITLED = zapHome + DBNAME_UNTITLED; 553 DIRBUSTER_CUSTOM_DIR = zapHome + DIRBUSTER_DIR; 554 FUZZER_DIR = zapHome + FUZZER_DIR; 555 FOLDER_LOCAL_PLUGIN = zapHome + FOLDER_PLUGIN; 556 557 try { 558 System.setProperty(SYSTEM_PAROS_USER_LOG, zapHome); 559 560 if (!f.isDirectory()) { 561 if (f.exists()) { 562 System.err.println("The home path is not a directory: " + zapHome); 563 System.exit(1); 564 } 565 if (!f.mkdir()) { 566 System.err.println("Unable to create home directory: " + zapHome); 567 System.err.println("Is the path correct and there's write permission?"); 568 System.exit(1); 569 } 570 } else if (!f.canWrite()) { 571 System.err.println("The home path is not writable: " + zapHome); 572 System.exit(1); 573 } else { 574 Path installDir = Paths.get(getZapInstall()).toRealPath(); 575 if (installDir.equals(Paths.get(zapHome).toRealPath())) { 576 System.err.println( 577 "The install dir should not be used as home dir: " + installDir); 578 System.exit(1); 579 } 580 } 581 582 setUpLogging(); 583 584 f = new File(FILE_CONFIG); 585 if (!f.isFile()) { 586 this.copyDefaultConfigs(f, false); 587 } 588 589 f = new File(FOLDER_SESSION); 590 if (!f.isDirectory()) { 591 LOG.info("Creating directory " + FOLDER_SESSION); 592 if (!f.mkdir()) { 593 // ZAP: report failure to create directory 594 System.out.println("Failed to create directory " + f.getAbsolutePath()); 595 } 596 } 597 f = new File(DIRBUSTER_CUSTOM_DIR); 598 if (!f.isDirectory()) { 599 LOG.info("Creating directory " + DIRBUSTER_CUSTOM_DIR); 600 if (!f.mkdir()) { 601 // ZAP: report failure to create directory 602 System.out.println("Failed to create directory " + f.getAbsolutePath()); 603 } 604 } 605 f = new File(FUZZER_DIR); 606 if (!f.isDirectory()) { 607 LOG.info("Creating directory " + FUZZER_DIR); 608 if (!f.mkdir()) { 609 // ZAP: report failure to create directory 610 System.out.println("Failed to create directory " + f.getAbsolutePath()); 611 } 612 } 613 f = new File(FOLDER_LOCAL_PLUGIN); 614 if (!f.isDirectory()) { 615 LOG.info("Creating directory " + FOLDER_LOCAL_PLUGIN); 616 if (!f.mkdir()) { 617 // ZAP: report failure to create directory 618 System.out.println("Failed to create directory " + f.getAbsolutePath()); 619 } 620 } 621 622 } catch (Exception e) { 623 System.err.println("Unable to initialize home directory! " + e.getMessage()); 624 e.printStackTrace(System.err); 625 System.exit(1); 626 } 627 628 // Upgrade actions 629 try { 630 try { 631 632 // ZAP: Changed to use ZapXmlConfiguration, to enforce the same character encoding 633 // when reading/writing configurations. 634 XMLConfiguration config = new ZapXmlConfiguration(FILE_CONFIG); 635 config.setAutoSave(false); 636 637 long ver = config.getLong(VERSION_ELEMENT); 638 639 if (ver == VERSION_TAG) { 640 // Nothing to do 641 } else if (isDevMode() || isDailyBuild()) { 642 // Nothing to do 643 } else { 644 // Backup the old one 645 LOG.info("Backing up config file to " + FILE_CONFIG + ".bak"); 646 f = new File(FILE_CONFIG); 647 try { 648 copier.copy(f, new File(FILE_CONFIG + ".bak")); 649 } catch (IOException e) { 650 String msg = 651 "Failed to backup config file " 652 + FILE_CONFIG 653 + " to " 654 + FILE_CONFIG 655 + ".bak " 656 + e.getMessage(); 657 System.err.println(msg); 658 LOG.error(msg, e); 659 } 660 661 if (ver == V_PAROS_TAG) { 662 upgradeFrom1_1_0(config); 663 upgradeFrom1_2_0(config); 664 } 665 if (ver <= V_1_0_0_TAG) { 666 // Nothing to do 667 } 668 if (ver <= V_1_1_0_TAG) { 669 upgradeFrom1_1_0(config); 670 } 671 if (ver <= V_1_2_0_TAG) { 672 upgradeFrom1_2_0(config); 673 } 674 if (ver <= V_1_2_1_TAG) { 675 // Nothing to do 676 } 677 if (ver <= V_1_3_0_TAG) { 678 // Nothing to do 679 } 680 if (ver <= V_1_3_1_TAG) { 681 // Nothing to do 682 } 683 if (ver <= V_1_4_1_TAG) { 684 upgradeFrom1_4_1(config); 685 } 686 if (ver <= V_2_0_0_TAG) { 687 upgradeFrom2_0_0(config); 688 } 689 if (ver <= V_2_1_0_TAG) { 690 // Nothing to do 691 } 692 if (ver <= V_2_2_0_TAG) { 693 upgradeFrom2_2_0(config); 694 } 695 if (ver <= V_2_2_2_TAG) { 696 upgradeFrom2_2_2(config); 697 } 698 if (ver <= V_2_3_1_TAG) { 699 upgradeFrom2_3_1(config); 700 } 701 if (ver <= V_2_4_3_TAG) { 702 upgradeFrom2_4_3(config); 703 } 704 if (ver <= V_2_5_0_TAG) { 705 upgradeFrom2_5_0(config); 706 } 707 if (ver <= V_2_7_0_TAG) { 708 upgradeFrom2_7_0(config); 709 } 710 if (ver <= V_2_8_0_TAG) { 711 upgradeFrom2_8_0(config); 712 } 713 if (ver <= V_2_9_0_TAG) { 714 upgradeFrom2_9_0(config); 715 } 716 if (ver <= V_2_10_0_TAG) { 717 upgradeFrom2_10_0(config); 718 } 719 720 // Execute always to pick installer choices. 721 updateCfuFromDefaultConfig(config); 722 723 LOG.info("Upgraded from " + ver); 724 725 setLatestVersion(config); 726 } 727 728 } catch (ConfigurationException | ConversionException | NoSuchElementException e) { 729 handleMalformedConfigFile(e); 730 } 731 } catch (Exception e) { 732 System.err.println( 733 "Unable to upgrade config file " + FILE_CONFIG + " " + e.getMessage()); 734 e.printStackTrace(System.err); 735 System.exit(1); 736 } 737 738 // ZAP: Init i18n 739 Locale locale = loadLocale(overrides); 740 741 Locale.setDefault(locale); 742 743 messages = new I18N(locale); 744 } 745 loadLocale(ControlOverrides overrides)746 private Locale loadLocale(ControlOverrides overrides) { 747 try { 748 String lang = null; 749 if (overrides != null) { 750 lang = overrides.getOrderedConfigs().get(OptionsParamView.LOCALE); 751 } 752 if (lang == null || lang.isEmpty()) { 753 XMLConfiguration config = new ZapXmlConfiguration(FILE_CONFIG); 754 config.setAutoSave(false); 755 756 lang = config.getString(OptionsParamView.LOCALE, OptionsParamView.DEFAULT_LOCALE); 757 if (lang.length() == 0) { 758 lang = OptionsParamView.DEFAULT_LOCALE; 759 } 760 } 761 762 String[] langArray = lang.split("_"); 763 return new Locale(langArray[0], langArray[1]); 764 765 } catch (Exception e) { 766 System.out.println("Failed to load locale " + e); 767 } 768 return Locale.ENGLISH; 769 } 770 setUpLogging()771 private void setUpLogging() throws IOException { 772 backupLegacyLog4jConfig(); 773 774 String fileName = "log4j2.properties"; 775 File logFile = new File(zapHome, fileName); 776 if (!logFile.exists()) { 777 String defaultConfig = "/org/zaproxy/zap/resources/log4j2-home.properties"; 778 copyFileToHome(logFile.toPath(), "xml/" + fileName, defaultConfig); 779 } 780 781 Configurator.reconfigure(logFile.toURI()); 782 } 783 backupLegacyLog4jConfig()784 private static void backupLegacyLog4jConfig() { 785 String fileName = "log4j.properties"; 786 Path backupLegacyConfig = Paths.get(zapHome, fileName + ".bak"); 787 if (Files.exists(backupLegacyConfig)) { 788 logAndPrintInfo("Ignoring legacy log4j.properties file, backup already exists."); 789 return; 790 } 791 792 Path legacyConfig = Paths.get(zapHome, fileName); 793 if (Files.exists(legacyConfig)) { 794 logAndPrintInfo("Creating backup of legacy log4j.properties file..."); 795 try { 796 Files.move(legacyConfig, backupLegacyConfig); 797 } catch (IOException e) { 798 logAndPrintError("Failed to backup legacy Log4j configuration file:", e); 799 } 800 } 801 } 802 handleMalformedConfigFile(Exception e)803 private void handleMalformedConfigFile(Exception e) throws IOException { 804 logAndPrintError("Failed to load/upgrade config file:", e); 805 try { 806 Path backupPath = Paths.get(zapHome, "config-" + Math.random() + ".xml.bak"); 807 logAndPrintInfo("Creating back up for user inspection: " + backupPath); 808 Files.copy(Paths.get(FILE_CONFIG), backupPath); 809 logAndPrintInfo("Back up successfully created."); 810 } catch (IOException ioe) { 811 logAndPrintError("Failed to backup file:", ioe); 812 } 813 logAndPrintInfo("Using default config file..."); 814 copyDefaultConfigFile(); 815 } 816 logAndPrintError(String message, Exception e)817 private static void logAndPrintError(String message, Exception e) { 818 LOG.error(message, e); 819 System.err.println(message); 820 e.printStackTrace(); 821 } 822 logAndPrintInfo(String message)823 private static void logAndPrintInfo(String message) { 824 LOG.info(message); 825 System.out.println(message); 826 } 827 copyProperty(XMLConfiguration fromConfig, XMLConfiguration toConfig, String key)828 private void copyProperty(XMLConfiguration fromConfig, XMLConfiguration toConfig, String key) { 829 toConfig.setProperty(key, fromConfig.getProperty(key)); 830 } 831 copyAllProperties( XMLConfiguration fromConfig, XMLConfiguration toConfig, String prefix)832 private void copyAllProperties( 833 XMLConfiguration fromConfig, XMLConfiguration toConfig, String prefix) { 834 Iterator<String> iter = fromConfig.getKeys(prefix); 835 while (iter.hasNext()) { 836 String key = iter.next(); 837 copyProperty(fromConfig, toConfig, key); 838 } 839 } 840 upgradeFrom1_1_0(XMLConfiguration config)841 private void upgradeFrom1_1_0(XMLConfiguration config) throws ConfigurationException { 842 // Upgrade the regexs 843 // ZAP: Changed to use ZapXmlConfiguration, to enforce the same character encoding when 844 // reading/writing configurations. 845 XMLConfiguration newConfig = new ZapXmlConfiguration(getUrlDefaultConfigFile()); 846 newConfig.setAutoSave(false); 847 848 copyAllProperties(newConfig, config, "pscans"); 849 } 850 upgradeFrom1_2_0(XMLConfiguration config)851 private void upgradeFrom1_2_0(XMLConfiguration config) throws ConfigurationException { 852 // Upgrade the regexs 853 // ZAP: Changed to use ZapXmlConfiguration, to enforce the same character encoding when 854 // reading/writing configurations. 855 XMLConfiguration newConfig = new ZapXmlConfiguration(getUrlDefaultConfigFile()); 856 newConfig.setAutoSave(false); 857 858 copyProperty(newConfig, config, "view.brkPanelView"); 859 copyProperty(newConfig, config, "view.showMainToolbar"); 860 } 861 upgradeFrom1_4_1(XMLConfiguration config)862 private void upgradeFrom1_4_1(XMLConfiguration config) { 863 // As the POST_FORM option for the spider has been updated from int to boolean, keep 864 // compatibility for old versions 865 Object postForm = config.getProperty("spider.postform"); 866 if (postForm != null) { 867 boolean enabled = !"0".equals(postForm.toString()); 868 config.setProperty("spider.postform", enabled); 869 config.setProperty("spider.processform", enabled); 870 } 871 872 // Move the old session tokens to the new "httpsessions" hierarchy and 873 // delete the old "session" hierarchy as it's no longer used/needed. 874 String[] tokens = config.getStringArray("session.tokens"); 875 for (int i = 0; i < tokens.length; ++i) { 876 String elementBaseKey = "httpsessions.tokens.token(" + i + ")."; 877 878 config.setProperty(elementBaseKey + "name", tokens[i]); 879 config.setProperty(elementBaseKey + "enabled", Boolean.TRUE); 880 } 881 config.clearTree("session"); 882 883 // Update the anti CSRF tokens elements/hierarchy. 884 tokens = config.getStringArray("anticsrf.tokens"); 885 config.clearTree("anticsrf.tokens"); 886 for (int i = 0; i < tokens.length; ++i) { 887 String elementBaseKey = "anticsrf.tokens.token(" + i + ")."; 888 889 config.setProperty(elementBaseKey + "name", tokens[i]); 890 config.setProperty(elementBaseKey + "enabled", Boolean.TRUE); 891 } 892 893 // Update the invoke applications elements/hierarchy. 894 List<Object[]> oldData = new ArrayList<>(); 895 for (int i = 0; ; i++) { 896 String baseKey = "invoke.A" + i + "."; 897 String host = config.getString(baseKey + "name"); 898 if (host == null || "".equals(host)) { 899 break; 900 } 901 902 Object[] data = new Object[6]; 903 data[0] = host; 904 data[1] = config.getString(baseKey + "directory", ""); 905 data[2] = config.getString(baseKey + "command"); 906 data[3] = config.getString(baseKey + "parameters"); 907 data[4] = config.getBoolean(baseKey + "output", true); 908 data[5] = config.getBoolean(baseKey + "note", false); 909 oldData.add(data); 910 } 911 config.clearTree("invoke.A"); 912 for (int i = 0, size = oldData.size(); i < size; ++i) { 913 String elementBaseKey = "invoke.apps.app(" + i + ")."; 914 Object[] data = oldData.get(i); 915 916 config.setProperty(elementBaseKey + "name", data[0]); 917 config.setProperty(elementBaseKey + "directory", data[1]); 918 config.setProperty(elementBaseKey + "command", data[2]); 919 config.setProperty(elementBaseKey + "parameters", data[3]); 920 config.setProperty(elementBaseKey + "output", data[4]); 921 config.setProperty(elementBaseKey + "note", data[5]); 922 config.setProperty(elementBaseKey + "enabled", Boolean.TRUE); 923 } 924 925 // Update the authentication elements/hierarchy. 926 oldData = new ArrayList<>(); 927 for (int i = 0; ; i++) { 928 String baseKey = "connection.auth.A" + i + "."; 929 String host = config.getString(baseKey + "hostName"); 930 if (host == null || "".equals(host)) { 931 break; 932 } 933 934 Object[] data = new Object[5]; 935 data[0] = host; 936 data[1] = Integer.valueOf(config.getString(baseKey + "port", "80")); 937 data[2] = config.getString(baseKey + "userName"); 938 data[3] = config.getString(baseKey + "password"); 939 data[4] = config.getString(baseKey + "realm"); 940 oldData.add(data); 941 } 942 config.clearTree("connection.auth.A"); 943 for (int i = 0, size = oldData.size(); i < size; ++i) { 944 String elementBaseKey = "connection.auths.auth(" + i + ")."; 945 Object[] data = oldData.get(i); 946 947 config.setProperty(elementBaseKey + "name", "Auth " + i); 948 config.setProperty(elementBaseKey + "hostName", data[0]); 949 config.setProperty(elementBaseKey + "port", data[1]); 950 config.setProperty(elementBaseKey + "userName", data[2]); 951 config.setProperty(elementBaseKey + "password", data[3]); 952 config.setProperty(elementBaseKey + "realm", data[4]); 953 config.setProperty(elementBaseKey + "enabled", Boolean.TRUE); 954 } 955 956 // Update the passive scan elements/hierarchy. 957 String[] names = config.getStringArray("pscans.names"); 958 oldData = new ArrayList<>(); 959 for (String pscanName : names) { 960 String baseKey = "pscans." + pscanName + "."; 961 962 Object[] data = new Object[8]; 963 data[0] = pscanName; 964 data[1] = config.getString(baseKey + "type"); 965 data[2] = config.getString(baseKey + "config"); 966 data[3] = config.getString(baseKey + "reqUrlRegex"); 967 data[4] = config.getString(baseKey + "reqHeadRegex"); 968 data[5] = config.getString(baseKey + "resHeadRegex"); 969 data[6] = config.getString(baseKey + "resBodyRegex"); 970 data[7] = config.getBoolean(baseKey + "enabled"); 971 oldData.add(data); 972 } 973 config.clearTree("pscans.names"); 974 for (String pscanName : names) { 975 config.clearTree("pscans." + pscanName); 976 } 977 for (int i = 0, size = oldData.size(); i < size; ++i) { 978 String elementBaseKey = "pscans.autoTagScanners.scanner(" + i + ")."; 979 Object[] data = oldData.get(i); 980 981 config.setProperty(elementBaseKey + "name", data[0]); 982 config.setProperty(elementBaseKey + "type", data[1]); 983 config.setProperty(elementBaseKey + "config", data[2]); 984 config.setProperty(elementBaseKey + "reqUrlRegex", data[3]); 985 config.setProperty(elementBaseKey + "reqHeadRegex", data[4]); 986 config.setProperty(elementBaseKey + "resHeadRegex", data[5]); 987 config.setProperty(elementBaseKey + "resBodyRegex", data[6]); 988 config.setProperty(elementBaseKey + "enabled", data[7]); 989 } 990 } 991 upgradeFrom2_0_0(XMLConfiguration config)992 private void upgradeFrom2_0_0(XMLConfiguration config) { 993 String forcedBrowseFile = config.getString("bruteforce.defaultFile", ""); 994 if (!"".equals(forcedBrowseFile)) { 995 String absolutePath = ""; 996 // Try the 'local' dir first 997 File f = new File(DIRBUSTER_CUSTOM_DIR + File.separator + forcedBrowseFile); 998 if (!f.exists()) { 999 f = new File(DIRBUSTER_DIR + File.separator + forcedBrowseFile); 1000 } 1001 if (f.exists()) { 1002 absolutePath = f.getAbsolutePath(); 1003 } 1004 config.setProperty("bruteforce.defaultFile", absolutePath); 1005 } 1006 1007 // Remove the manual request editor configurations that were incorrectly created. 1008 config.clearTree("nullrequest"); 1009 config.clearTree("nullresponse"); 1010 } 1011 upgradeFrom2_2_0(XMLConfiguration config)1012 private void upgradeFrom2_2_0(XMLConfiguration config) { 1013 try { 1014 if (config.getInt(OptionsParamCheckForUpdates.CHECK_ON_START, 0) == 0) { 1015 /* 1016 * Check-for-updates on start disabled - force another prompt to ask the user, 1017 * as this option can have been unset incorrectly before. 1018 * And we want to encourage users to use this ;) 1019 */ 1020 config.setProperty(OptionsParamCheckForUpdates.DAY_LAST_CHECKED, ""); 1021 } 1022 } catch (ConversionException e) { 1023 LOG.debug( 1024 "The option " + OptionsParamCheckForUpdates.CHECK_ON_START + " is not an int.", 1025 e); 1026 } 1027 // Clear the block list - addons were incorrectly added to this if an update failed 1028 config.setProperty(AddOnLoader.ADDONS_BLOCK_LIST, ""); 1029 } 1030 upgradeFrom2_2_2(XMLConfiguration config)1031 private void upgradeFrom2_2_2(XMLConfiguration config) { 1032 try { 1033 // Change the type of the option from int to boolean. 1034 int oldValue = config.getInt(OptionsParamCheckForUpdates.CHECK_ON_START, 1); 1035 config.setProperty(OptionsParamCheckForUpdates.CHECK_ON_START, oldValue != 0); 1036 } catch (ConversionException e) { 1037 LOG.debug( 1038 "The option " 1039 + OptionsParamCheckForUpdates.CHECK_ON_START 1040 + " is no longer an int.", 1041 e); 1042 } 1043 } 1044 upgradeFrom2_3_1(XMLConfiguration config)1045 private void upgradeFrom2_3_1(XMLConfiguration config) { 1046 // Remove old authentication options no longer used 1047 config.clearProperty("connection.confirmRemoveAuth"); 1048 config.clearTree("options.auth"); 1049 } 1050 upgradeFrom2_4_3(XMLConfiguration config)1051 private void upgradeFrom2_4_3(XMLConfiguration config) { 1052 List<Object[]> oldData = new ArrayList<>(); 1053 // Convert extensions' options to not use extensions' names as XML element names 1054 for (Iterator<String> it = config.getKeys("ext"); it.hasNext(); ) { 1055 String key = it.next(); 1056 1057 Object[] data = new Object[2]; 1058 data[0] = key.substring(4); 1059 data[1] = config.getBoolean(key); 1060 oldData.add(data); 1061 } 1062 config.clearTree("ext"); 1063 1064 for (int i = 0, size = oldData.size(); i < size; ++i) { 1065 String elementBaseKey = "extensions.extension(" + i + ")."; 1066 Object[] data = oldData.get(i); 1067 1068 config.setProperty(elementBaseKey + "name", data[0]); 1069 config.setProperty(elementBaseKey + "enabled", data[1]); 1070 } 1071 } 1072 upgradeFrom2_5_0(XMLConfiguration config)1073 private static void upgradeFrom2_5_0(XMLConfiguration config) { 1074 String oldConfigKey = "proxy.modifyAcceptEncoding"; 1075 config.setProperty( 1076 "proxy.removeUnsupportedEncodings", config.getBoolean(oldConfigKey, true)); 1077 config.clearProperty(oldConfigKey); 1078 1079 // Convert passive scanners options to new structure 1080 Set<String> classnames = new HashSet<>(); 1081 for (Iterator<String> it = config.getKeys(); it.hasNext(); ) { 1082 String key = it.next(); 1083 if (!key.startsWith("pscans.org")) { 1084 continue; 1085 } 1086 classnames.add(key.substring(7, key.lastIndexOf('.'))); 1087 } 1088 1089 List<Object[]> oldData = new ArrayList<>(); 1090 for (String classname : classnames) { 1091 Object[] data = new Object[3]; 1092 data[0] = classname; 1093 data[1] = config.getBoolean("pscans." + classname + ".enabled", true); 1094 data[2] = config.getString("pscans." + classname + ".level", ""); 1095 oldData.add(data); 1096 } 1097 1098 config.clearTree("pscans.org"); 1099 1100 for (int i = 0, size = oldData.size(); i < size; ++i) { 1101 String elementBaseKey = "pscans.pscanner(" + i + ")."; 1102 Object[] data = oldData.get(i); 1103 1104 config.setProperty(elementBaseKey + "classname", data[0]); 1105 config.setProperty(elementBaseKey + "enabled", data[1]); 1106 config.setProperty(elementBaseKey + "level", data[2]); 1107 } 1108 } 1109 upgradeFrom2_7_0(XMLConfiguration config)1110 private static void upgradeFrom2_7_0(XMLConfiguration config) { 1111 // Remove options from SNI Terminator. 1112 config.clearTree("sniterm"); 1113 1114 String certUseKey = "certificate.use"; 1115 try { 1116 // Change the type of the option from int to boolean. 1117 int oldValue = config.getInt(certUseKey, 0); 1118 config.setProperty(certUseKey, oldValue != 0); 1119 } catch (ConversionException e) { 1120 LOG.debug("The option " + certUseKey + " is no longer an int.", e); 1121 } 1122 } 1123 upgradeFrom2_8_0(XMLConfiguration config)1124 private static void upgradeFrom2_8_0(XMLConfiguration config) { 1125 // Update to a newer default user agent 1126 config.setProperty( 1127 ConnectionParam.DEFAULT_USER_AGENT, ConnectionParam.DEFAULT_DEFAULT_USER_AGENT); 1128 updatePscanTagMailtoPattern(config); 1129 } 1130 upgradeFrom2_9_0(XMLConfiguration config)1131 static void upgradeFrom2_9_0(XMLConfiguration config) { 1132 String oldKeyName = ".markocurrences"; // This is the typo we want to fix 1133 String newKeyName = ".markoccurrences"; 1134 config.getKeys() 1135 .forEachRemaining( 1136 key -> { 1137 if (key.endsWith(oldKeyName)) { 1138 config.setProperty( 1139 key.replace(oldKeyName, newKeyName), 1140 config.getProperty(key)); 1141 config.clearProperty(key); 1142 } 1143 }); 1144 // Update to a newer default user agent 1145 config.setProperty( 1146 ConnectionParam.DEFAULT_USER_AGENT, ConnectionParam.DEFAULT_DEFAULT_USER_AGENT); 1147 // Use new Look and Feel 1148 config.setProperty( 1149 OptionsParamView.LOOK_AND_FEEL, OptionsParamView.DEFAULT_LOOK_AND_FEEL_NAME); 1150 config.setProperty( 1151 OptionsParamView.LOOK_AND_FEEL_CLASS, OptionsParamView.DEFAULT_LOOK_AND_FEEL_CLASS); 1152 } 1153 upgradeFrom2_10_0(XMLConfiguration config)1154 private static void upgradeFrom2_10_0(XMLConfiguration config) { 1155 // Update to a newer default user agent 1156 config.setProperty( 1157 ConnectionParam.DEFAULT_USER_AGENT, ConnectionParam.DEFAULT_DEFAULT_USER_AGENT); 1158 } 1159 updatePscanTagMailtoPattern(XMLConfiguration config)1160 private static void updatePscanTagMailtoPattern(XMLConfiguration config) { 1161 String autoTagScannersKey = "pscans.autoTagScanners.scanner"; 1162 List<HierarchicalConfiguration> tagScanners = config.configurationsAt(autoTagScannersKey); 1163 String badPattern = "<.*href\\s*['\"]?mailto:"; 1164 String goodPattern = "<.*href\\s*=\\s*['\"]?mailto:"; 1165 1166 for (int i = 0, size = tagScanners.size(); i < size; ++i) { 1167 String currentKeyResBodyRegex = autoTagScannersKey + "(" + i + ").resBodyRegex"; 1168 if (config.getProperty(currentKeyResBodyRegex).equals(badPattern)) { 1169 config.setProperty(currentKeyResBodyRegex, goodPattern); 1170 break; 1171 } 1172 } 1173 } 1174 updateCfuFromDefaultConfig(XMLConfiguration config)1175 private static void updateCfuFromDefaultConfig(XMLConfiguration config) { 1176 Path path = getPathDefaultConfigFile(); 1177 if (!Files.exists(path)) { 1178 return; 1179 } 1180 1181 ZapXmlConfiguration defaultConfig; 1182 try { 1183 defaultConfig = new ZapXmlConfiguration(path.toFile()); 1184 } catch (ConfigurationException e) { 1185 logAndPrintError("Failed to read default configuration file " + path, e); 1186 return; 1187 } 1188 1189 copyPropertyIfSet(defaultConfig, config, "start.checkForUpdates"); 1190 copyPropertyIfSet(defaultConfig, config, "start.downloadNewRelease"); 1191 copyPropertyIfSet(defaultConfig, config, "start.checkAddonUpdates"); 1192 copyPropertyIfSet(defaultConfig, config, "start.installAddonUpdates"); 1193 copyPropertyIfSet(defaultConfig, config, "start.installScannerRules"); 1194 copyPropertyIfSet(defaultConfig, config, "start.reportReleaseAddons"); 1195 copyPropertyIfSet(defaultConfig, config, "start.reportBetaAddons"); 1196 copyPropertyIfSet(defaultConfig, config, "start.reportAlphaAddons"); 1197 } 1198 copyPropertyIfSet(XMLConfiguration from, XMLConfiguration to, String key)1199 private static void copyPropertyIfSet(XMLConfiguration from, XMLConfiguration to, String key) { 1200 Object value = from.getProperty(key); 1201 if (value != null) { 1202 to.setProperty(key, value); 1203 } 1204 } 1205 setLocale(String loc)1206 public static void setLocale(String loc) { 1207 String[] langArray = loc.split("_"); 1208 Locale locale = new Locale(langArray[0], langArray[1]); 1209 1210 Locale.setDefault(locale); 1211 if (messages == null) { 1212 messages = new I18N(locale); 1213 } else { 1214 messages.setLocale(locale); 1215 } 1216 } 1217 getLocale()1218 public static Locale getLocale() { 1219 return messages.getLocal(); 1220 } 1221 1222 /** 1223 * Returns the system's {@code Locale} (as determined by the JVM at startup, {@link 1224 * Locale#getDefault()}). Should be used to show locale dependent information in the system's 1225 * locale. 1226 * 1227 * <p><strong>Note:</strong> The default locale is overridden with the ZAP's user defined 1228 * locale/language. 1229 * 1230 * @return the system's {@code Locale} 1231 * @see Locale#setDefault(Locale) 1232 */ getSystemsLocale()1233 public static Locale getSystemsLocale() { 1234 return SYSTEMS_LOCALE; 1235 } 1236 getInstance()1237 public static Constant getInstance() { 1238 if (instance == null) { 1239 // ZAP: Changed to use the method createInstance(). 1240 createInstance(null); 1241 } 1242 return instance; 1243 } 1244 createInstance(ControlOverrides overrides)1245 public static synchronized void createInstance(ControlOverrides overrides) { 1246 if (instance == null) { 1247 instance = new Constant(overrides); 1248 } 1249 } 1250 setAcceleratorKeys()1251 private void setAcceleratorKeys() { 1252 1253 // Undo/Redo 1254 if (Constant.isMacOsX()) { 1255 ACCELERATOR_UNDO = "meta Z"; 1256 ACCELERATOR_REDO = "meta shift Z"; 1257 ACCELERATOR_TRIGGER_KEY = "Meta"; 1258 } else { 1259 ACCELERATOR_UNDO = "control Z"; 1260 ACCELERATOR_REDO = "control Y"; 1261 ACCELERATOR_TRIGGER_KEY = "Control"; 1262 } 1263 } 1264 1265 // Determine Windows Operating System 1266 // ZAP: Changed to final. 1267 private static final Pattern patternWindows = 1268 Pattern.compile("window", Pattern.CASE_INSENSITIVE); 1269 isWindows()1270 public static boolean isWindows() { 1271 String os_name = System.getProperty("os.name"); 1272 1273 Matcher matcher = patternWindows.matcher(os_name); 1274 return matcher.find(); 1275 } 1276 1277 // Determine Linux Operating System 1278 // ZAP: Changed to final. 1279 private static final Pattern patternLinux = Pattern.compile("linux", Pattern.CASE_INSENSITIVE); 1280 isLinux()1281 public static boolean isLinux() { 1282 String os_name = System.getProperty("os.name"); 1283 Matcher matcher = patternLinux.matcher(os_name); 1284 return matcher.find(); 1285 } 1286 1287 // Determine Windows Operating System 1288 // ZAP: Changed to final. 1289 private static final Pattern patternMacOsX = Pattern.compile("mac", Pattern.CASE_INSENSITIVE); 1290 isMacOsX()1291 public static boolean isMacOsX() { 1292 String os_name = System.getProperty("os.name"); 1293 Matcher matcher = patternMacOsX.matcher(os_name); 1294 return matcher.find(); 1295 } 1296 setZapHome(String dir)1297 public static void setZapHome(String dir) { 1298 zapHome = getAbsolutePath(dir); 1299 } 1300 1301 /** 1302 * Returns the absolute path for the given {@code directory}. 1303 * 1304 * <p>The path is terminated with a separator. 1305 * 1306 * @param directory the directory whose path will be made absolute 1307 * @return the absolute path for the given {@code directory}, terminated with a separator 1308 * @since 2.4.0 1309 */ getAbsolutePath(String directory)1310 private static String getAbsolutePath(String directory) { 1311 String realPath = Paths.get(directory).toAbsolutePath().toString(); 1312 String separator = FileSystems.getDefault().getSeparator(); 1313 if (!realPath.endsWith(separator)) { 1314 realPath += separator; 1315 } 1316 return realPath; 1317 } 1318 getZapHome()1319 public static String getZapHome() { 1320 return zapHome; 1321 } 1322 1323 /** 1324 * Returns the path to default configuration file, located in installation directory. 1325 * 1326 * <p><strong>Note:</strong> The default configuration file is not guaranteed to exist, for 1327 * example, when running just with ZAP JAR. 1328 * 1329 * @return the {@code Path} to default configuration file. 1330 * @since 2.4.2 1331 */ getPathDefaultConfigFile()1332 public static Path getPathDefaultConfigFile() { 1333 return Paths.get(getZapInstall(), "xml", FILE_CONFIG_NAME); 1334 } 1335 getContextsDir()1336 public static File getContextsDir() { 1337 return getFromHomeDir(USER_CONTEXTS_DIR); 1338 } 1339 getPoliciesDir()1340 public static File getPoliciesDir() { 1341 return getFromHomeDir(USER_POLICIES_DIR); 1342 } 1343 getFromHomeDir(String subDir)1344 private static File getFromHomeDir(String subDir) { 1345 Path path = Paths.get(Constant.getZapHome(), subDir); 1346 1347 try { 1348 if (Files.notExists(path)) { 1349 Files.createDirectory(path); 1350 } 1351 } catch (IOException e) { 1352 } 1353 1354 if (Files.isDirectory(path) && Files.isWritable(path)) { 1355 return path.toFile(); 1356 } 1357 return Model.getSingleton().getOptionsParam().getUserDirectory(); 1358 } 1359 setZapInstall(String dir)1360 public static void setZapInstall(String dir) { 1361 zapInstall = getAbsolutePath(dir); 1362 } 1363 getZapInstall()1364 public static String getZapInstall() { 1365 if (zapInstall == null) { 1366 String path = "."; 1367 Path localDir = Paths.get(path); 1368 if (!Files.isDirectory(localDir.resolve("db")) 1369 || !Files.isDirectory(localDir.resolve("lang"))) { 1370 try { 1371 Path sourceLocation = 1372 Paths.get( 1373 ZAP.class 1374 .getProtectionDomain() 1375 .getCodeSource() 1376 .getLocation() 1377 .toURI()); 1378 if (!Files.isDirectory(sourceLocation)) { 1379 sourceLocation = sourceLocation.getParent(); 1380 } 1381 path = sourceLocation.toString(); 1382 } catch (URISyntaxException e) { 1383 System.err.println( 1384 "Failed to determine the ZAP installation dir: " + e.getMessage()); 1385 path = localDir.toAbsolutePath().toString(); 1386 } 1387 // Loggers wont have been set up yet 1388 System.out.println("Defaulting ZAP install dir to " + path); 1389 } 1390 1391 zapInstall = getAbsolutePath(path); 1392 } 1393 return zapInstall; 1394 } 1395 getManifest()1396 private static Manifest getManifest() { 1397 String className = Constant.class.getSimpleName() + ".class"; 1398 String classPath = Constant.class.getResource(className).toString(); 1399 if (!classPath.startsWith("jar")) { 1400 // Class not from JAR 1401 return null; 1402 } 1403 String manifestPath = 1404 classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; 1405 try { 1406 return new Manifest(new URL(manifestPath).openStream()); 1407 } catch (Exception e) { 1408 // Ignore 1409 return null; 1410 } 1411 } 1412 getVersionFromManifest()1413 private static String getVersionFromManifest() { 1414 Manifest manifest = getManifest(); 1415 if (manifest != null) { 1416 Attributes attr = manifest.getMainAttributes(); 1417 return attr.getValue("Implementation-Version"); 1418 } else { 1419 return DEV_VERSION; 1420 } 1421 } 1422 getReleaseCreateDate()1423 public static Date getReleaseCreateDate() { 1424 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 1425 Manifest manifest = getManifest(); 1426 if (manifest != null) { 1427 Attributes attr = manifest.getMainAttributes(); 1428 try { 1429 return sdf.parse(attr.getValue("Create-Date")); 1430 } catch (ParseException e) { 1431 // Ignore - treat as undated 1432 } 1433 } 1434 return null; 1435 } 1436 getInstallDate()1437 public static Date getInstallDate() { 1438 String className = Constant.class.getSimpleName() + ".class"; 1439 String classPath = Constant.class.getResource(className).toString(); 1440 if (!classPath.startsWith("jar:file:")) { 1441 // Class not from JAR 1442 return null; 1443 } 1444 classPath = classPath.substring(9); 1445 int ind = classPath.indexOf("!"); 1446 if (ind > 0) { 1447 classPath = classPath.substring(0, ind); 1448 } 1449 File f = new File(classPath); 1450 if (f.exists()) { 1451 // Choose the parent directories date, in case the creation date was maintained 1452 return new Date(f.getParentFile().lastModified()); 1453 } 1454 return null; 1455 } 1456 1457 /** 1458 * Tells whether or not the "dev mode" should be enabled. 1459 * 1460 * <p>Should be used to enable development related utilities/functionalities. 1461 * 1462 * @return {@code true} if the "dev mode" should be enabled, {@code false} otherwise. 1463 * @since 2.8.0 1464 */ isDevMode()1465 public static boolean isDevMode() { 1466 return devMode || isDevBuild(); 1467 } 1468 1469 /** 1470 * Sets whether or not the "dev mode" should be enabled. 1471 * 1472 * <p><strong>Note:</strong> This method should be called only by bootstrap classes. 1473 * 1474 * @param devMode {@code true} if the "dev mode" should be enabled, {@code false} otherwise. 1475 */ setDevMode(boolean devMode)1476 public static void setDevMode(boolean devMode) { 1477 Constant.devMode = devMode; 1478 } 1479 1480 /** 1481 * Sets whether or not ZAP should be 'silent' i.e. not make any unsolicited requests. 1482 * 1483 * <p><strong>Note:</strong> This method should be called only by bootstrap classes. 1484 * 1485 * @param silent {@code true} if ZAP should be silent, {@code false} otherwise. 1486 * @since 2.8.0 1487 */ setSilent(boolean silent)1488 public static void setSilent(boolean silent) { 1489 Constant.silent = silent; 1490 } 1491 1492 /** 1493 * Tells whether or not ZAP is running from a dev build. 1494 * 1495 * @return {@code true} if it's a dev build, {@code false} otherwise. 1496 * @see #isDevMode() 1497 */ isDevBuild()1498 public static boolean isDevBuild() { 1499 return isDevBuild(PROGRAM_VERSION); 1500 } 1501 isDevBuild(String version)1502 public static boolean isDevBuild(String version) { 1503 // Dev releases with be called "Dev Build" date stamped builds will be of the format 1504 // D-{yyyy}-{mm}-{dd} 1505 return DEV_VERSION.equals(version); 1506 } 1507 isDailyBuild(String version)1508 public static boolean isDailyBuild(String version) { 1509 // Date stamped builds will be of the format D-{yyyy}-{mm}-{dd} 1510 return version.startsWith("D-"); 1511 } 1512 isDailyBuild()1513 public static boolean isDailyBuild() { 1514 return isDailyBuild(PROGRAM_VERSION); 1515 } 1516 1517 /** 1518 * If true then ZAP should not make any unsolicited requests, e.g. check-for-updates 1519 * 1520 * @return true if ZAP should not make any unsolicited requests, e.g. check-for-updates 1521 * @since 2.8.0 1522 */ isSilent()1523 public static boolean isSilent() { 1524 return silent; 1525 } 1526 setLowMemoryOption(boolean lowMem)1527 public static void setLowMemoryOption(boolean lowMem) { 1528 if (lowMemoryOption != null) { 1529 throw new InvalidParameterException( 1530 "Low memory option already set to " + lowMemoryOption); 1531 } 1532 lowMemoryOption = lowMem; 1533 } 1534 isLowMemoryOptionSet()1535 public static boolean isLowMemoryOptionSet() { 1536 return lowMemoryOption != null && lowMemoryOption; 1537 } 1538 1539 /** 1540 * Tells whether or not ZAP is running in Kali (and it's not a daily build). 1541 * 1542 * @return {@code true} if running in Kali (and it's not daily build), {@code false} otherwise 1543 */ isKali()1544 public static boolean isKali() { 1545 if (onKali == null) { 1546 onKali = Boolean.FALSE; 1547 File osReleaseFile = new File("/etc/os-release"); 1548 if (isLinux() && !isDailyBuild() && osReleaseFile.exists()) { 1549 // Ignore the fact we're on Kali if this is a daily build - they will only have been 1550 // installed manually 1551 try (InputStream in = Files.newInputStream(osReleaseFile.toPath())) { 1552 Properties osProps = new Properties(); 1553 osProps.load(in); 1554 String osLikeValue = osProps.getProperty("ID"); 1555 if (osLikeValue != null) { 1556 String[] oSLikes = osLikeValue.split(" "); 1557 for (String osLike : oSLikes) { 1558 if (osLike.equalsIgnoreCase("kali")) { 1559 onKali = Boolean.TRUE; 1560 break; 1561 } 1562 } 1563 } 1564 } catch (Exception e) { 1565 // Ignore 1566 } 1567 } 1568 } 1569 return onKali; 1570 } 1571 1572 /** 1573 * Returns true if ZAP is running in a container like Docker or Flatpak 1574 * 1575 * @see #getContainerName 1576 * @since 2.11.0 1577 */ isInContainer()1578 public static boolean isInContainer() { 1579 if (inContainer == null) { 1580 // This is created by the Docker files from 2.11 1581 File containerFile = new File(ZAP_CONTAINER_FILE); 1582 File flatpakFile = new File(FLATPAK_FILE); 1583 File snapFile = new File(SNAP_FILE); 1584 if (isLinux() && containerFile.exists()) { 1585 inContainer = true; 1586 boolean inWebSwing = System.getenv(WEBSWING_ENVVAR) != null; 1587 try { 1588 containerName = 1589 new String( 1590 Files.readAllBytes(containerFile.toPath()), 1591 StandardCharsets.UTF_8) 1592 .trim(); 1593 if (inWebSwing) { 1594 // Append the webswing name so we don't loose the docker image name 1595 containerName += "." + WEBSWING_NAME; 1596 } 1597 } catch (IOException e) { 1598 // Ignore 1599 } 1600 } else if (flatpakFile.exists()) { 1601 inContainer = true; 1602 containerName = FLATPAK_NAME; 1603 } else if (snapFile.exists()) { 1604 inContainer = true; 1605 containerName = SNAP_NAME; 1606 } else { 1607 inContainer = false; 1608 } 1609 } 1610 return inContainer; 1611 } 1612 1613 /** 1614 * Returns the name of the container ZAP is running in (if any) e.g. zap2docker-stable, flatpak 1615 * or null if not running in a recognised container 1616 * 1617 * @since 2.11.0 1618 */ getContainerName()1619 public static String getContainerName() { 1620 isInContainer(); 1621 return containerName; 1622 } 1623 } 1624