/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2010 psiinon@gmail.com
* Copyright 2014 Jay Ball - Aspect Security
*
* 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.
*/
package org.zaproxy.zap.extension.globalexcludeurl;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.configuration.ConversionException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.log4j.Logger;
import org.parosproxy.paros.common.AbstractParam;
import org.parosproxy.paros.model.Model;
import org.zaproxy.zap.extension.api.ZapApiIgnore;
public class GlobalExcludeURLParam extends AbstractParam {
private static final Logger logger = Logger.getLogger(GlobalExcludeURLParam.class);
private static final String GLOBAL_EXCLUDE_URL_BASE_KEY = "globalexcludeurl";
private static final String ALL_TOKENS_KEY = GLOBAL_EXCLUDE_URL_BASE_KEY + ".url_list.url";
private static final String TOKEN_REGEX_KEY = "regex";
private static final String TOKEN_DESCRIPTION_KEY = "description";
private static final String TOKEN_ENABLED_KEY = "enabled";
private static final String CONFIRM_REMOVE_TOKEN_KEY = GLOBAL_EXCLUDE_URL_BASE_KEY + ".confirmRemoveToken";
private static ArrayList<GlobalExcludeURLParamToken> defaultList = new ArrayList<GlobalExcludeURLParamToken>();
/** Fills in the list of default regexs to ignore. In a future version, this could be read from a
* system-wide default HierarchicalConfiguration xml config file
* instead or even a HierarchicalConfiguration string directly embedded in this file. */
private void setDefaultList() {
// Remember, these are regexs, so escape properly \\ vs \
// Also, http://regexpal.com/ for quick testing.
// The file formats are common types, not inclusive of all types. Remember, more == slower;
// complex == slower. Don't overload with every obscure image/audio/video format in existence.
/* At some point in the future, this could be read from some a config file and
* parsed. Thus, we just make it as arrays of strings and assume there
* is some level of parsing at some point. Since it is rarely accessed (like
* once per boot, it need not be optimized). */
final String defaultListArray[][] = {
{
"^.*\\.(gif|jpe?g|png|ico|icns|bmp)$",
"Extension - Image (ends with .extension)",
"false"
}, {
"^.*\\.(mp[34]|mpe?g|m4[ap]|aac|avi|mov|wmv|og[gav])$",
"Extension - Audio/Video (ends with .extension)",
"false"
}, {
"^.*\\.(pdf|docx?|xlsx?|pptx?)$",
"Extension - PDF & Office (ends with .extension)",
"false"
}, {
"^.*\\.(css|js)$",
"Extension - Stylesheet, JavaScript (ends with .extension)",
"false"
}, {
"^.*\\.(sw[fa]|flv)$",
"Extension - Flash & related (ends with .extension)",
"false"
}, {
"^[^\\?]*\\.(gif|jpe?g|png|ico|icns|bmp)\\?.*$",
"ExtParam - Image (extension plus ?params=values)",
"false"
}, {
"^[^\\?]*\\.(mp[34]|mpe?g|m4[ap]|aac|avi|mov|wmv|og[gav])\\?.*$",
"ExtParam - Audio/Video (extension plus ?params=values)",
"false"
}, {
"^[^\\?]*\\.(pdf|docx?|xlsx?|pptx?)\\?.*$",
"ExtParam - PDF & Office (extension plus ?params=values)",
"false"
}, {
"^[^\\?]*\\.(css|js)\\?.*$",
"ExtParam - Stylesheet, JavaScript (extension plus ?params=values)",
"false"
}, {
"^[^\\?]*\\.(sw[fa]|flv)\\?.*$",
"ExtParam - Flash & related (extension plus ?params=values)",
"false"
}, {
"^[^\\?]*/(WebResource|ScriptResource)\\.axd\\?d=.*$",
"ExtParam - .NET adx resources (SR/WR.adx?d=)",
"false"
}, {
"^https?://api\\.bing\\.com/qsml\\.aspx?query=.*$",
"Site - Bing API queries",
"false"
}, {
"^https?://(safebrowsing-cache|sb-ssl|sb|safebrowsing\\.clients)\\.google\\.com/.*$",
"Site - Google malware detector updates",
"false"
}, {
"^https?://([^/])*\\.?lastpass\\.com",
"Site - Lastpass manager",
"false"
}, {
"^https?://(.*addons|aus[0-9])\\.mozilla\\.(org|net|com)/.*$",
"Site - Mozilla Firefox browser updates",
"false"
}, {
"^https?://([^/])*\\.?(getfoxyproxy\\.org|getfirebug\\.com|noscript\\.net)",
"Site - Mozilla Firefox extensions phoning home",
"false"
}, {
// some of this from http://serverfault.com/questions/332003/what-urls-must-be-in-ies-trusted-sites-list-to-allow-windows-update
"^https?://(.*update\\.microsoft|.*\\.windowsupdate)\\.com/.*$",
"Site - Microsoft Windows updates",
"false"
}, {
"^https?://clients2\\.google\\.com/service/update2/crx.*$",
"Site - Google Chrome extension updates",
"false"
}
};
for (String row[] : defaultListArray) {
boolean b = row[2].equalsIgnoreCase("true") ? true : false;
defaultList.add( new GlobalExcludeURLParamToken( row[0], row[1], b));
}
}
private List<GlobalExcludeURLParamToken> tokens = null;
private List<String> enabledTokensNames = null;
private boolean confirmRemoveToken = true;
public GlobalExcludeURLParam() {
super();
setDefaultList();
}
@Override
protected void parse() {
try {
List<HierarchicalConfiguration> fields = ((HierarchicalConfiguration) getConfig()).configurationsAt(ALL_TOKENS_KEY);
this.tokens = new ArrayList<>(fields.size());
enabledTokensNames = new ArrayList<>(fields.size());
List<String> tempTokensNames = new ArrayList<>(fields.size());
for (HierarchicalConfiguration sub : fields) {
String regex = sub.getString(TOKEN_REGEX_KEY, "");
if (!"".equals(regex) && !tempTokensNames.contains(regex)) {
boolean enabled = sub.getBoolean(TOKEN_ENABLED_KEY, true);
String desc = sub.getString(TOKEN_DESCRIPTION_KEY, "");
this.tokens.add(new GlobalExcludeURLParamToken(regex, desc, enabled));
tempTokensNames.add(regex);
if (enabled) {
enabledTokensNames.add(regex);
}
}
}
} catch (ConversionException e) {
logger.error("Error while loading Global Exclude URL tokens: " + e.getMessage(), e);
this.tokens = new ArrayList<>(defaultList.size());
this.enabledTokensNames = new ArrayList<>(defaultList.size());
}
if (this.tokens.size() == 0) {
for (GlobalExcludeURLParamToken geu : defaultList) {
this.tokens.add(new GlobalExcludeURLParamToken(geu));
}
}
try {
this.confirmRemoveToken = getConfig().getBoolean(CONFIRM_REMOVE_TOKEN_KEY, true);
} catch (ConversionException e) {
logger.error("Error while loading the confirm remove token option: " + e.getMessage(), e);
}
}
public List<GlobalExcludeURLParamToken> getTokens() {
return tokens;
}
public void setTokens(List<GlobalExcludeURLParamToken> tokens) {
this.tokens = new ArrayList<>(tokens);
((HierarchicalConfiguration) getConfig()).clearTree(ALL_TOKENS_KEY);
ArrayList<String> enabledTokens = new ArrayList<>(tokens.size());
for (int i = 0, size = tokens.size(); i < size; ++i) {
String elementBaseKey = ALL_TOKENS_KEY + "(" + i + ").";
GlobalExcludeURLParamToken token = tokens.get(i);
getConfig().setProperty(elementBaseKey + TOKEN_REGEX_KEY, token.getRegex());
getConfig().setProperty(elementBaseKey + TOKEN_DESCRIPTION_KEY, token.getDescription());
getConfig().setProperty(elementBaseKey + TOKEN_ENABLED_KEY, Boolean.valueOf(token.isEnabled()));
if (token.isEnabled()) {
enabledTokens.add(token.getRegex());
}
}
enabledTokens.trimToSize();
this.enabledTokensNames = enabledTokens;
// after saving, force the proxy to refresh the URL lists.
Model.getSingleton().getSession().forceGlobalExcludeURLRefresh();
}
public void addToken(String regex) {
this.tokens.add(new GlobalExcludeURLParamToken(regex));
this.enabledTokensNames.add(regex);
}
public void removeToken(String regex) {
this.tokens.remove(new GlobalExcludeURLParamToken(regex));
this.enabledTokensNames.remove(regex);
}
public List<String> getTokensNames() {
return enabledTokensNames;
}
@ZapApiIgnore
public boolean isConfirmRemoveToken() {
return this.confirmRemoveToken;
}
@ZapApiIgnore
public void setConfirmRemoveToken(boolean confirmRemove) {
this.confirmRemoveToken = confirmRemove;
getConfig().setProperty(CONFIRM_REMOVE_TOKEN_KEY, Boolean.valueOf(confirmRemoveToken));
}
}