package org.zaproxy.zap.extension.httpsessions; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.sf.json.JSONObject; import org.apache.commons.httpclient.Cookie; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.zaproxy.zap.extension.api.ApiAction; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiImplementor; import org.zaproxy.zap.extension.api.ApiResponse; import org.zaproxy.zap.extension.api.ApiResponseElement; import org.zaproxy.zap.extension.api.ApiResponseList; import org.zaproxy.zap.extension.api.ApiResponseSet; import org.zaproxy.zap.extension.api.ApiView; import org.zaproxy.zap.utils.ApiUtils; import org.zaproxy.zap.utils.Pair; import org.zaproxy.zap.utils.XMLStringUtil; /** * The Class HttpSessionsAPI. */ public class HttpSessionsAPI extends ApiImplementor { /** The Constant log. */ private static final Logger log = Logger.getLogger(HttpSessionsAPI.class); /** The Constant PREFIX defining the name/prefix of the api. */ private static final String PREFIX = "httpSessions"; /** The action of creating a new empty session for a site and turns it active. */ private static final String ACTION_CREATE_EMPTY_SESSION = "createEmptySession"; /** The action of deleting an existing session. */ private static final String ACTION_REMOVE_SESSION = "removeSession"; /** The action of setting a new active session for a site. */ private static final String ACTION_SET_ACTIVE_SESSION = "setActiveSession"; /** The action of adding a new session token for a site. */ private static final String ACTION_ADD_SESSION_TOKEN = "addSessionToken"; /** The action of removing session token for a site. */ private static final String ACTION_REMOVE_SESSION_TOKEN = "removeSessionToken"; /** The action of unsetting a session as active for a site. */ private static final String ACTION_UNSET_ACTIVE_SESSION = "unsetActiveSession"; /** The action of setting the value for a session token for a particular session. */ private static final String ACTION_SET_SESSION_TOKEN = "setSessionTokenValue"; /** The action of renaming a session. */ private static final String ACTION_RENAME_SESSION = "renameSession"; /** The mandatory parameter required for identifying a site to which an action refers. */ private static final String ACTION_PARAM_SITE = "site"; /** The mandatory parameter required for identifying a session to which an action refers. */ private static final String ACTION_PARAM_SESSION = "session"; /** The mandatory parameter required for identifying a session for renaming. */ private static final String ACTION_PARAM_SESSION_OLD_NAME = "oldSessionName"; /** The mandatory parameter required for renaming a session. */ private static final String ACTION_PARAM_SESSION_NEW_NAME = "newSessionName"; /** The mandatory parameter required for identifying a session token to which an action refers. */ private static final String ACTION_PARAM_TOKEN_NAME = "sessionToken"; /** The mandatory parameter required for setting the value of a session token. */ private static final String ACTION_PARAM_TOKEN_VALUE = "tokenValue"; /** The view which lists all of the sites with session tokens. */ private static final String VIEW_SITES = "sites"; /** The view which describes the current existing sessions for a site. */ private static final String VIEW_SESSIONS = "sessions"; /** The view which describes the current active session for a site. */ private static final String VIEW_ACTIVE_SESSION = "activeSession"; /** The view which describes which are the session tokens for a particular site. */ private static final String VIEW_SESSION_TOKENS = "sessionTokens"; /** The mandatory parameter required for viewing data regarding a particular site. */ private static final String VIEW_PARAM_SITE = "site"; /** The mandatory parameter required for viewing data regarding a particular session. */ private static final String VIEW_PARAM_SESSION = "session"; /** The extension. */ private ExtensionHttpSessions extension; /** * Instantiates a new http sessions api implementor. * * @param extension the extension */ public HttpSessionsAPI(ExtensionHttpSessions extension) { super(); this.extension = extension; // Register the actions this.addApiAction(new ApiAction(ACTION_CREATE_EMPTY_SESSION, new String[] { ACTION_PARAM_SITE }, new String[] { ACTION_PARAM_SESSION })); this.addApiAction(new ApiAction(ACTION_REMOVE_SESSION, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_SESSION })); this.addApiAction(new ApiAction(ACTION_SET_ACTIVE_SESSION, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_SESSION })); this.addApiAction(new ApiAction(ACTION_UNSET_ACTIVE_SESSION, new String[] { ACTION_PARAM_SITE })); this.addApiAction(new ApiAction(ACTION_ADD_SESSION_TOKEN, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_TOKEN_NAME })); this.addApiAction(new ApiAction(ACTION_REMOVE_SESSION_TOKEN, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_TOKEN_NAME })); this.addApiAction(new ApiAction(ACTION_SET_SESSION_TOKEN, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_SESSION, ACTION_PARAM_TOKEN_NAME, ACTION_PARAM_TOKEN_VALUE })); this.addApiAction(new ApiAction(ACTION_RENAME_SESSION, new String[] { ACTION_PARAM_SITE, ACTION_PARAM_SESSION_OLD_NAME, ACTION_PARAM_SESSION_NEW_NAME })); // Register the views this.addApiView(new ApiView(VIEW_SITES)); this.addApiView(new ApiView(VIEW_SESSIONS, new String[] { VIEW_PARAM_SITE }, new String[] { VIEW_PARAM_SESSION })); this.addApiView(new ApiView(VIEW_ACTIVE_SESSION, new String[] { VIEW_PARAM_SITE })); this.addApiView(new ApiView(VIEW_SESSION_TOKENS, new String[] { VIEW_PARAM_SITE })); } @Override public String getPrefix() { return PREFIX; } @Override public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException { if (log.isDebugEnabled()) { log.debug("Request for handleApiAction: " + name + " (params: " + params.toString() + ")"); } HttpSessionsSite site; switch (name) { case ACTION_CREATE_EMPTY_SESSION: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), true); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } final String sessionName = getParam(params, ACTION_PARAM_SESSION, ""); if ("".equals(sessionName)) { site.createEmptySession(); } else { site.createEmptySession(sessionName); } return ApiResponseElement.OK; case ACTION_REMOVE_SESSION: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } HttpSession sessionRS = site.getHttpSession(params.getString(ACTION_PARAM_SESSION)); if (sessionRS == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SESSION); } site.removeHttpSession(sessionRS); return ApiResponseElement.OK; case ACTION_SET_ACTIVE_SESSION: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } String sname = params.getString(ACTION_PARAM_SESSION); for (HttpSession session : site.getHttpSessions()) { if (session.getName().equals(sname)) { site.setActiveSession(session); return ApiResponseElement.OK; } } // At this point, the given name does not match any session name throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SESSION); case ACTION_UNSET_ACTIVE_SESSION: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } site.unsetActiveSession(); return ApiResponseElement.OK; case ACTION_ADD_SESSION_TOKEN: extension.addHttpSessionToken(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), params.getString(ACTION_PARAM_TOKEN_NAME)); return ApiResponseElement.OK; case ACTION_REMOVE_SESSION_TOKEN: extension.removeHttpSessionToken(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), params.getString(ACTION_PARAM_TOKEN_NAME)); return ApiResponseElement.OK; case ACTION_SET_SESSION_TOKEN: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } HttpSession sessionSST = site.getHttpSession(params.getString(ACTION_PARAM_SESSION)); if (sessionSST == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SESSION); } extension.addHttpSessionToken(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), params.getString(ACTION_PARAM_TOKEN_NAME)); sessionSST.setTokenValue(params.getString(ACTION_PARAM_TOKEN_NAME), new Cookie(null /* domain */, params.getString(ACTION_PARAM_TOKEN_NAME), params.getString(ACTION_PARAM_TOKEN_VALUE))); return ApiResponseElement.OK; case ACTION_RENAME_SESSION: site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } if (!site.renameHttpSession(params.getString(ACTION_PARAM_SESSION_OLD_NAME), params.getString(ACTION_PARAM_SESSION_NEW_NAME))) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, Constant.messages.getString("httpsessions.api.error.rename")); } return ApiResponseElement.OK; default: throw new ApiException(ApiException.Type.BAD_ACTION); } } @Override public ApiResponse handleApiView(String name, JSONObject params) throws ApiException { if (log.isDebugEnabled()) { log.debug("Request for handleApiView: " + name + " (params: " + params.toString() + ")"); } HttpSessionsSite site; switch (name) { case VIEW_SITES: // Get all sites with sessions ApiResponseList responseSites = new ApiResponseList(name); for (String s : extension.getSites()) { responseSites.addItem(new ApiResponseElement("site", s)); } return responseSites; case VIEW_SESSIONS: // Get existing sessions site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } ApiResponseList response = new ApiResponseList(name); String vsName = getParam(params, VIEW_PARAM_SESSION, ""); // If a session name was not provided if (vsName == null || vsName.isEmpty()) { Set<HttpSession> sessions = site.getHttpSessions(); if (log.isDebugEnabled()) { log.debug("API View for sessions for " + ApiUtils.getAuthority(params.getString(VIEW_PARAM_SITE)) + ": " + site); } // Build the response for (HttpSession session : sessions) { // Dont include 'null' sessions if (session.getTokenValuesUnmodifiableMap().size() > 0) { response.addItem(createSessionResponse(session)); } } } // If a session name was provided else { HttpSession session = site.getHttpSession(vsName); if (session != null) { response.addItem(createSessionResponse(session)); } } return response; case VIEW_ACTIVE_SESSION: // Get existing sessions site = extension.getHttpSessionsSite(ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)), false); if (site == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } if (log.isDebugEnabled()) { log.debug("API View for active session for " + ApiUtils.getAuthority(params.getString(VIEW_PARAM_SITE)) + ": " + site); } if (site.getActiveSession() != null) { return new ApiResponseElement("active_session", site.getActiveSession().getName()); } else { return new ApiResponseElement("active_session", ""); } case VIEW_SESSION_TOKENS: final String siteName = ApiUtils.getAuthority(params.getString(ACTION_PARAM_SITE)); // Check if the site exists if (extension.getHttpSessionsSite(siteName, false) == null) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, ACTION_PARAM_SITE); } // Get session tokens HttpSessionTokensSet sessionTokens = extension.getHttpSessionTokensSet(siteName); ApiResponseList responseST = new ApiResponseList("session_tokens"); if (sessionTokens != null) { Set<String> tokens = sessionTokens.getTokensSet(); // Build response list if (tokens != null) { for (String token : tokens) { responseST.addItem(new ApiResponseElement("token", token)); } } } return responseST; default: throw new ApiException(ApiException.Type.BAD_VIEW); } } private ApiResponseList createSessionResponse(HttpSession session) { ApiResponseList sessionResult = new ApiResponseList("session"); sessionResult.addItem(new ApiResponseElement("name", session.getName())); sessionResult.addItem(new TokenValuesResponseSet(session.getTokenValuesUnmodifiableMap())); sessionResult.addItem(new ApiResponseElement("messages_matched", Integer.toString(session.getMessagesMatched()))); return sessionResult; } private static class TokenValuesResponseSet extends ApiResponseSet<Cookie> { private final List<List<Pair<String, String>>> xmlTokenElements; public TokenValuesResponseSet(Map<String, Cookie> values) { super("tokens", values); this.xmlTokenElements = convertTokenValues(values); } @Override public void toXML(Document doc, Element parent) { parent.setAttribute("type", "set"); for (List<Pair<String, String>> tokenElements : xmlTokenElements) { Element tokenSet = doc.createElement("token"); tokenSet.setAttribute("type", "set"); for (Pair<String, String> element : tokenElements) { Element el = doc.createElement(element.first); el.appendChild(doc.createTextNode(XMLStringUtil.escapeControlChrs(element.second))); tokenSet.appendChild(el); } parent.appendChild(tokenSet); } } private static List<List<Pair<String, String>>> convertTokenValues(Map<String, Cookie> values) { List<List<Pair<String, String>>> tokens = new ArrayList<>(); for (Entry<String, Cookie> token : values.entrySet()) { Cookie cookie = token.getValue(); List<Pair<String, String>> fields = new ArrayList<>(); fields.add(new Pair<>("name", token.getKey())); fields.add(new Pair<>("value", cookie.getValue())); fields.add(new Pair<>("domain", cookie.getDomain())); fields.add(new Pair<>("path", cookie.getPath())); fields.add(new Pair<>("secure", Boolean.toString(cookie.getSecure()))); tokens.add(fields); } return tokens; } } }